SSM框架
mybatis
快速入门:
- 导入坐标
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency>
建立映射文件的位置:创建位置:必须和持久层接口在相同的包中。名称:必须以持久层接口名称命名文件名,扩展名是.xml
映射文件的内容,指明了映射哪个实体类,下面有个例子
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 配置查询所有操作 -->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>
主配置文件SQlMapConfig.xml
目的是连接数据库,并且写出映射文件的位置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 配置 mybatis 的环境 -->
<environments default="mysql">
<!-- 配置 mysql 的环境 -->
<environment id="mysql">
<!-- 配置事务的类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置连接数据库的信息:用的是数据源(连接池) -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ee50"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</dataSource>
</environment>
</environments>
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>
</configuration>
- 下面示范一个使用mybatis的步骤
/**
*
* <p>Title: MybatisTest</p>
* <p>Description: 测试 mybatis 的环境</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class MybatisTest {
public static void main(String[] args)throws Exception {
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建 SqlSessionFactory 的构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象 SqlSessionFactory,通过读取的配置文件的内容
SqlSessionFactory factory = builder.build(in);
//4.使用 SqlSessionFactory 生产 SqlSession 对象,SqlSession真正与数据库打交道
SqlSession session = factory.openSession();
//5.使用 SqlSession 创建 dao 接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//6.使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
//7.释放资源
session.close();
in.close();
}
}
- 介绍基于注解的mybatis(这个时候就要把映射文件给删除),在sqlmapconfig文件中就要告诉使用注解的类是哪一个
<!-- 告知 mybatis 映射配置的位置 -->
<mappers>
<mapper class="com.itheima.dao.IUserDao"/>
</mappers>
注意:在使用映射文件的时候是resource,现在用的是class
自定义mybatis查询的流程分析(使用代理类对象)
一、首先理一下思路:
(1)最终我们执行的就是findAll()方法,在这里 方法中我们分为几个步骤。
1、根据sqlMapConifg配置文件创建connection对象。
2、获取预处理对象,然后根据sql语句执行查询。(这里的sql就是需要配置文件得来的)。
3、我们遍历结果集,把查到的数据封装到实体类对象中,这个对象又存在泛型数组中,通过反射创建一个实体类对象(创建的前提条件就是有这个实体类的全限定类名)
小结:这就是为什么我们需要两个配置文件,(1)sqlmapconfig.xml主要是提供数据库连接的信息,同时提供映射配置文件的信息。(2)mapper.xml提供了sql语句获取预处理对象,resultType属性告诉了封装是要封装到哪个实体类当中,用于反射实例化一个对象,这个时候就可以知道泛型数组具体是哪种类型。
在我们开发的时候绝对不止一个映射文件,一个方法,这个时候我们就可以用一个map集合,里面装一些mapper对象,这些对象有两个属性,第一个是sql语句,第二个是全限定类名。并且map的key是接口的全限定类名+方法名。
(2)第一步我们知道了执行findAll()方法的前提条件,但是findAll()必须要有一个调用他的类,现在就讨论在哪里调用的findAll()
使用了动态代理,然后增强方法中调用了这个方法, 有一个getMapper的泛型方法,他的参数是一个接口的字节码,在这个方法里 使用实现接口的动态代理的方法
proxy.newProxyInstance(类加载器,代理对象要实现的接口字节数组,增强方法)。
接下来就是一层一层的往上套。这里我们根据那几个步骤来写思路
首先介绍一下用到的类:
Resources 用于加载配置文件的类
SqlSessionFactoryBuilder 构建者的类,里面的builde方法就是根据从配置文件得到的内容来构建
SqlSessionFactory工厂的,然后在这个方法里调用了一个工具类的方法
(不用深究,主要是加载配置文件内容的),这个方法的目的就是把获取了内容,
把driver,url,username,password封装到一个Configuration类中。
Configuration类,自定义的mybatis的配置类,里面的属性有:
driver,url,username,password,mappers(map集合,就是用于存储上面说的sql语句和全限定类名)。
SqlSessionFactory 是一个接口,里面有一个opensession方法,用于打开新的ssqlsession对象
DefaultSqlSessionFactory 是SqlSessionFactory的实现类,里面有configuration对象,意思就是这个工厂有了数据库的一套消息和要操作的实体类,里面实现的opensession()方法直接new一个真正操作数据库的DefaultSqlSession(对象,这里一定不要忘了把那个配置类传进去,因为他有整套信息。
SqlSession 是一个接口,他是整个mybatis和数据库交互的核心类,它里面主要负责根据dao接口的字节码创建一个代理对象,和释放资源。
DefaultSqlSession 是 SqlSession接口的实现类,是一个操作数据库的对象,有两个属性,Configuration,Connection。创建DefaultSqlSessionFactory的时候,把Configuration传进来了,然后根据Configuration,创建链接。对于创建代理对象的方法,直接返回一个具有增强方法的代理类对象(其中增强方法的类是MapperProxy,实现了InvocationHandle的invoke方法)。对于释放资源的方法,把链接关闭就可以了。
MapperProxy实现了InvocationHandle,两个属性:mappers,conn。调用这个构造器的时候就要把所说的那个集合给传进来,用于增强方法的时候组合key,然后通过这个key找到要封装的mapper,这个mapper就有sql语句,和实体类的全限定类名。
这个时候就可以调用工具类的selectList()方法了,把查到的mapper,conn传进去就ok了。
//1.读取配置文件,这里使用类加载器读取主配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建 SqlSessionFactory 的构建者对象,这个构建者主要是用于创建SqlSessionFactory工厂的
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.使用构建者创建工厂对象 SqlSessionFactory,其实我们返回一个DefaultSqlSessionFactory,
并且他还有数据库和sql语句,全限定类名一套信息。
SqlSessionFactory factory = builder.build(in);
//4.使用 SqlSessionFactory 生产 SqlSession 对象,这里其实已经是DefaultSqlSessionFactory了,
然后他有了一套信息,返回一个操作一个数据库的对象,这个可操作数据库的对象应该得到刚才的
配置信息同时创建一个链接。
SqlSession session = factory.openSession();
//5.使用 SqlSession 创建 dao 接口的代理对象,这里我们就是用了动态代理,
就有了一个代理对象,getMapper根据传进来的字节码然后创建代理对象,
同时这个代理对象有了一个增强方法。
IUserDao userDao = session.getMapper(IUserDao.class);
//6.使用代理对象执行查询所有方法
List<User> users = userDao.findAll();
for(User user : users) {
System.out.println(user);
}
//7.释放资源
session.close();
in.close();
二、涉及的设计模式。
构建者模式:暂时不是很理解什么是分离‘复杂对象的构建算法’和‘部件及组装方式’,在自定义的框架里,他的用法就是为了创建一个工厂SqlSessionFactory。
工厂模式,主要是用于实例化对象,用工厂方法代替new操作,降低了程序的耦合,增加了程序的可扩展性和以后更少的修改量。这里SqlSessionFactory 生产 SqlSession 对象就是。
代理模式,动态代理,减少了代码的冗余,我们返回一个代理对象,它里面含有具体的增强方法,例如上面的session.getMapper(IUserDao.class);就是创建了一个iuserdao的代理对象,他有了增强的方法,可以具体操作某个方法。
mybatis的一些配置(记录一下每个配置,方便以后忘了可以查)
SqlMapConfig.xml主配置文件- 首先写一下配置的内容
-properties(属性)
--property
-settings(全局配置参数)
--setting
-typeAliases(类型别名)
--typeAliase
--package
-typeHandlers(类型处理器)
-objectFactory(对象工厂)
-plugins(插件)
-environments(环境集合属性对象)
--environment(环境子属性对象)
---transactionManager(事务管理)
---dataSource(数据源)
-mappers(映射器)
--mapper
--package
下面是一个示例,方便看
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置properties
可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息
resource属性: 常用的
用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
resource="jdbc.properties", 建议使用这个
url属性:
是要求按照Url的写法来写地址
URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
它的写法:
http://localhost:8080/mybatisserver/demo1Servlet
协议 主机 端口 URI
URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。
-->
<properties url="file:///D:/IdeaProjects/day02_eesy_01mybatisCRUD/src/main/resources/jdbcConfig.properties">
<!-- <property name="driver" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property> -->
</properties>
<!-- 使用typeAliases配置别名,它只能配置domain中类的别名 -->
<typeAliases>
<!-- typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就再区分大小写
<typeAlias type="com.itheima.domain.User" alias="user"></typeAlias> -->
<!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写 -->
<package name="com.itheima.domain"/>
</typeAliases>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境 -->
<environment id="mysql">
<!-- 配置事务 -->
<transactionManager type="JDBC"/>
<!-- 配置连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件的位置 -->
<mappers>
<!-- <mapper resource="com/itheima/dao/IUserDao.xml"></mapper> -->
<!-- package标签是用于指定dao接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了,resource用于指定映射文件,class指定接口 -->
<package name="com.itheima.dao"/>
</mappers>
</configuration>
- sqlmapconfig.xml配置数据源,深入数据源
<!-- 配置数据源(连接池)信息 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
MyBatis 在初始化时,根据<dataSource>的 type 属性来创建相应类型的的数据源 DataSource,即:
type=”POOLED”:MyBatis 会创建 PooledDataSource 实例,连接池实例
type=”UNPOOLED” : MyBatis 会创建 UnpooledDataSource 实例,不用连接池
type=”JNDI”:MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用
分析POOLED:它使用了数据库连接池,当我们真正创建了sqlsession对象并且执行sql语句的时候才会去调用datasource对象去创建链接,当我们用完了就归还给连接池,是一个懒加载。
(Mapper.xml)映射文件的配置
resultType:把结果封装成什么类型(如果字段名和属性名一样用这个)
resultMap:把结果封装成什么类型(字段名和属性名不一样)- 具体的用法是建立表和类的对应关系
<!-- 建立 User 实体和数据库表的对应关系
type 属性:指定实体类的全限定类名
id 属性:给定一个唯一标识,是给查询 select 标签引用用的。
-->
<resultMap type="com.itheima.domain.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
id 标签:用于指定主键字段
result 标签:用于指定非主键字段
column 属性:用于指定数据库列名
property 属性:用于指定实体类属性名称
映射配置
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">//这里resultMap就指向了上面定义的
select * from user
</select>
parameterType:传入参数的类型
#{} sql语句使用这个字符,它相当于jdbc的占位符,具体的数据由里面的内容决定。- {}内的写法主要是用该ognl表达式 格式:对象.对象 例如#{user.username} 他就是去user对象中找getUsername(),如果我们在parameterType声明了这个user,就可以省略user.直接写username就可以了。
keyColumn:列名,keyProperty:属性名。
配置动态sql语句 ```xml if标签 我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
- <br />几个扩展的问题:
- 当我们插入一个数据的时候,我们需要返回他自动增长的id的时候我们要将自动增长的值返回就可以了,具体实现如下xml
- 模糊查询的两个方法xml
我们在上面将原来的#{}占位符,改成了${value}。注意如果用模糊查询的这种写法,那么${value}的写 法就是固定的,不能写成其它名字。
#{}表示一个占位符号
通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其它名称。
${}表示拼接 sql 串
通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换, ${}可以接收简 单类型值或 pojo 属性值,${}括号中只能是 value。
4. <br />
<a name="654ea41e"></a>
### mybatis的一对一,一对多以及事务控制
- <br />
- <br />事务控制:本身也是用JDBC的setAutoCommit()来设者事务提交方式的,我们观察上面这个图发现我们每次cud操作的时候都必须手动进行事物的提交,原因是setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。那我们在创建session对象的时候,设为true就可以了。`**_factory.openSession(true);_**`
- <br />一对一的查询
- 方式一:(推荐)用户的个实体类作为账户的子类,这样返回的时候就有了账户的那几个属性
```java
/**
*
* <p>Title: Account</p>
* <p>Description: 账户的实体类</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
}
}
/**
*
* <p>Title: AccountUser</p>
* <p>Description: 它是 account 的子类,包括了父类的属性</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class AccountUser extends Account implements Serializable {
private String username;
private String address;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return super.toString() + "
AccountUser [username=" + username + ",
address=" + address + "]";
}
}
/**
*
* <p>Title: IAccountDao</p>
* <p>Description: 账户的持久层接口</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface IAccountDao {
/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List<AccountUser> findAll();
}
映射文件的信息
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IAccountDao">
<!-- 配置查询所有操作-->
<select id="findAll" resultType="accountuser">
select a.*,u.username,u.address from account a,user u where a.uid =u.id;
</select>
</mapper>
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。
- 方式二:resultMap专门定义一对一查询,思路是把User作为Account的一个属性
/**
*
* <p>Title: Account</p>
* <p>Description: 账户的实体类</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
}
}
/**
*
* <p>Title: IAccountDao</p>
* <p>Description: 账户的持久层接口</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public interface IAccountDao {
/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List<Account> findAll();
}
注意:第二种方式,将返回值改 为了 Account 类型。
因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息。
修改映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IAccountDao">
<!-- 建立对应关系 -->
<resultMap type="account" id="accountMap">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用于指定从表方的引用实体属性的 -->
<association property="user" javaType="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u where a.uid =u.id;
</select>
</mapper>
- 一对多查询(这个时候就只能用刚才的映射关系,mybatis已经做好了和集合的关系)
/**
*
* <p>Title: User</p>
* <p>Description: 用户的实体类</p>
* <p>Company: http://www.itheima.com/ </p>
*/
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", birthday=" + birthday
+ ", sex=" + sex + ", address="
+ address + "]";
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
<resultMap type="user" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection 是用于建立一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型-->
<collection property="accounts" ofType="account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>
<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account
a on u.id =a.uid
</select>
</mapper>
collection部分定义了用户关联的账户信息。表示关联查询结果集
property="accList" :关联查询的结果集存储在 User 对象的上哪个属性。
ofType="account" :指定关联查询的结果集中的对象类型即 List 中的对象类型。此处可以使用别名,也可以使用全限定名。
一级缓存和二级缓存
一级缓存
一级查询缓存是针对与SqlSession类的实例对象中,当第一次查询某个数据的时候,SqlSession类的实例对象会将该数据存入一级缓存区域,在没有收到改变该数据的请求(执行了commit操作,即增 删 改)之前,用户再次查询该数据都是从缓存中获取数据,而不是再次连接池数据库进行查询。
二级缓存
一级缓存是针对SqlSession对象的、二级缓存是针对于Mapper实例的,当多个SqlSession类的实例对象加载的是同一个Mapper文件配置的IuserDao.class(就是sqlsession.getMapper(相同的接口.class))的时候,那么他们就共享一个Mapper缓存。
Spring
<?xml version=”1.0” encoding=”UTF-8”?>
- <br />3、主类测试有没有成功java
/
模拟一个表现层
@author 黑马程序员
@Company http://www.ithiema.com
@Version 1.0
*/
public class Client {
/
使用 main 方法获取容器测试执行
/
public static void main(String[] args) {
//1.使用 ApplicationContext 接口,就是在获取 spring 容器
ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);
//2.根据 bean 的 id 获取对象
IAccountService aService = (IAccountService) ac.getBean(“accountService”);
System.out.println(aService);
IAccountDao aDao = (IAccountDao) ac.getBean(“accountDao”);
System.out.println(aDao);
}
}
运行结果如下:
<a name="T727W"></a>
### Spring基于XML的IOC的细节(重点)
<a name="V9qQ7"></a>
#### 工厂类的结构图
- <br />
<a name="qAfta"></a>
##### BeanFactory 和 ApplicationContext 的区别
BeanFactory是顶层接口,ApplicationContext是字节口,BeanFactory是懒加载,什么时候使用什么时候创建ioc容器,ApplicationContext是立即创建ioc容器。<br />ApplicationContext接口的实现类:<br /> ClassPathXmlApplicationContext:<br /> 它是从类的根路径下加载配置文件 推荐使用这种<br /> FileSystemXmlApplicationContext:<br /> 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。<br /> AnnotationConfigApplicationContext:<br /> 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
<a name="ogxjq"></a>
#### IOC中bean标签的细节
- <br />作用:就是创建对象(默认情况是调用类中的无参构造器,如果没有就不成功)
- <br />**属性**:
- id:给对象在容器提供一个唯一标识,用于获取对象。
- class:指定类的全限定类名,拿来反射创建对象,默认情况调用无参构造器。
- scope:指定对象的作用范围
- singleton:单例,默认值
- prototype:多例
- request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
- session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
- global session<br />:WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
- init-method:指定类中的初始化方法名称。
- destroy-method:指定类中销毁方法名称。
- <br />**bean的作用范围和生命周期**
- scope="singleton"
- 单例的,作用范围是整个应用。
- 生命周期:(就是和容器的状态是一样的)
- 对象出生:当应用加载,读取文件创建容器的时候对象就被创建了
- 对象活着:只要容器在,对象就一直活着
- 对象死亡:销毁容器时,对象就被销毁了
- scope="prototype"
- 每次访问对象时,就会重新创建(不归容器管),就当时有点用
- 生命周期:
- 对象出生:当使用对象时,创建新的对象实例。
- 对象活着:只要对象在使用中,就一直活着。
- 对象死亡:当对象长时间不用时,由垃圾回收器回收。
- <br />**实例化Bean的三种方式**
- <br />第一种:就是调用默认无参构造器
- <br />xml
- <br />第二种:静态工厂,然后bean标签里这个工厂的静态方法
- <br />xml
第二种方式:spring 管理静态工厂-使用静态工厂的方法创建对象
/
模拟一个静态工厂,创建业务层实现类
/
public class StaticFactory {
public static IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
- <br />第三种:实例化工厂,然后用实例化工厂的方法创建对象
- <br />xml
/
模拟一个实例工厂,创建业务层实现类
此工厂创建对象,必须现有工厂实例对象,再调用方法
/
public class InstanceFactory {
public IAccountService createAccountService(){
return new AccountServiceImpl();
}
}
<a name="666480c3"></a>
### **Spring的依赖注入(IOC)**
- 概念:其实就是我们之前已经把对象交给spring处理了,我们就可以用spring操作这个对象,给这个对象注入我们想给它的值。
<a name="kuhOS"></a>
#### XML配置IOC
- 构造函数注入:xml
- set方法注入xml
顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:
/** /
public class AccountServiceImpl implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public void saveAccount() {
System.out.println(name+”,”+age+”,”+birthday);
}
}
- 使用p名称空间注入数据(本质还是调用set方法,所以要提供set方法)
- 而且还多一个p约束,就在第二行xml
- <br />注入集合的数据(不像上面就是基本类型或者某个实体类)
- <br />xml
<a name="MSsZA"></a>
#### 基于注解的IOC配置(当我们使用注解注入的时候,set方法可以不用写,一般用这个)
- <br />快速环境搭建:
- 1、多了一个jar包:spring-aop-5.0.2.RELEAS.jar
- 2、把需要放入到容器中的类加上Component(当不属于三层的时候可以用这个)
- 3、创建bean.xml文件,基于注解的时候我们要多导约束,下面是注解的xml的一个例子xml
<?xml version=”1.0” encoding=”UTF-8”?>
- <br />几个Spring常用的注解介绍:
- <br />用于创建对象的:@Component,@Controller,@Service,@Repository,用于把注解的这个类放到容器中。
- <br />用于注入数据的:
- @Autowired:动按照类型注入。当使用注解注入属性时,set 方法可以省 略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象 变量名 称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。下面描述一下这个注解的匹配过程。

- <br />@Qualifier:
作用:在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和[@Autowire ](/Autowire ) 一起使用;但是给方法参数注入时,可以独立使用。属性:
value:指定 bean 的 id。
- <br />@Resource:作用:
直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。属性:name:指定 bean 的 id。
- <br />@Value作用:注入基本数据类型和 String 类型数据的属性:value:用于指定值
- <br />用于改变作用范围的:
- @Scope作用:指定 bean 的作用范围。属性:value:指定范围的值。取值:singleton prototype request session globalsession
- <br />和生命周期相关的:(了解)
- [@PostConstruct ](/PostConstruct ) 作用:用于指定初始化方法。
- @PreDestroy作用:用于指定销毁方法。
- <br />**XML和注解的选择问题:**
- 注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML 的优势:
修改时,不用改源码。不涉及重新编译和部署。
- 管理bean方式的比较:

- <br />
<a name="e64710cd"></a>
### **Spring整合Junit(掌握)**
- <br />为什么要整合Junit?因为我们想在测试类中,直接注入某些对象,方便测试,而不是每个测试方法都包含以下两行代码:
针对这个问题,我们如果能自动 创建spring容器就可以解决了,但是junit他不知道有没有spring框架跟别说创建容器了,不过他有一个注解,这个注解可以让我们替换掉他的运行器,刚好spring提供了一个运行器,可以读取配置文件来创建容器,我们只要告诉这个运行器配置文件在哪里就可以了。java
ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);
IAccountService as = ac.getBean(“accountService”,IAccountService.class);
获取了容器之后,再创建相应的对象。
- <br />配置步骤:java
1、导入一个spring中aop的jar包。
2、使用@RunWith注解替换原有的运行器
/
测试类
@author 黑马程序员
@Company http://www.ithiema.com
@Version 1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {
}
3、使用@ContextConfiguration指定Spring配置文件的位置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {“classpath:bean.xml”})
@ContextConfiguration 注解:
locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明
classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。
public class AccountServiceTest {
}
- <br />
<a name="Dkk8X"></a>
### Spring中的aop
<a name="IWU9V"></a>
#### 动态代理
<a name="ByhoB"></a>
##### jdk动态代理java
package com.itheima.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/
模拟一个消费者
/
public class Client {
public static void main(String[] args) {
Producer producer = new Producer();
/
动态代理:
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理
基于子类的动态代理
基于接口的动态代理:
涉及的类:Proxy
提供者:JDK官方
如何创建代理对象:
使用Proxy类中的newProxyInstance方法
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用
newProxyInstance方法的参数:
ClassLoader:类加载器
它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]:字节码数组
它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
此接口的实现类都是谁用谁写。
/
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.判断当前方法是不是销售
if(“saleProduct”.equals(method.getName())) {
System.out.println(args[0]);
//2.获取方法执行的参数
Float money = (Float) args[0];
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f);
}
}
package com.itheima.proxy;
/
对生产厂家要求的接口
/
public interface IProducer {
/
销售
@param money
*/
public void saleProduct(float money);
/
售后
@param money
/
public void afterService(float money);
}
package com.itheima.proxy;
/**
一个生产者
/
public class Producer implements IProducer{
@Override
public void saleProduct(float money) {
System.out.println(“拿到钱”+money);
}
@Override
public void afterService(float money) {
System.out.println(“—-afterService”);
}
}
<a name="USeZp"></a>
##### cglib动态代理java
package com.itheima.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
模拟一个消费者
/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
/**
动态代理:
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理
基于子类的动态代理
基于子类的动态代理:
涉及的类:Enhancer
提供者:第三方cglib库
如何创建代理对象:
使用Enhancer类中的create方法
创建代理对象的要求:
被代理类不能是最终类
create方法的参数:
Class:字节码
它是用于指定被代理对象的字节码。
Callback:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
此接口的实现类都是谁用谁写。
我们一般写的都是该接口的子接口实现类:MethodInterceptor
/
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
执行被代理对象的任何方法都会经过该方法
@param proxy
@param method
@param args
以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
@param methodProxy :当前执行方法的代理对象
@return returnValue 是方法的返回值
@throws Throwable
/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if(“saleProduct”.equals(method.getName())) {
returnValue = method.invoke(producer, money0.8f);
}
System.out.println(“———->”+returnValue);
return returnValue;
}
});
cglibProducer.saleProduct(12000f);
}
}
package com.itheima.cglib;
/**
一个生产者
/
public class Producer {
/**
销售
@param money
/
public Integer saleProduct(float money){
System.out.println(“销售产品,并拿到钱:”+money);
Integer integer = new Integer(1);
return integer;
}
/*
售后
@param money
/
public void afterService(float money){
System.out.println(“提供售后服务,并拿到钱:”+money);
}
}
- <br />
<a name="hRW5Z"></a>
#### AOP相关术语:
- Joinpoint(链接点):通俗点就是方法
- PointCut(切入点):就是说明我们要对那些方法进行拦截
- Advice(增强/通知):就是对拦截的方法进行一系列操作,就叫通知。分为:前置、后置、异常、最终、环绕、异常。
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
- Target(目标对象):代理的目标对象。
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
- Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
- Aspect(切面):是切入点和通知(引介)的结合。
- <br />
<a name="AcZAM"></a>
#### 基于XML的AOP配置
- 首先导入相应的jar包

- 导入aop的的约束xml
此处要导入 aop 的约束
<?xml version=”1.0” encoding=”UTF-8”?>
- <br />配置步骤:通过实例来更清楚的描述
单独说一下环绕通知xml
这里把切入点表达式好好说明一下 属性: id:表达式的唯一标识 expression:表达式,就是拦截那些方法。 表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数)) 修饰符可以省略,返回值可以用 ,表示可以返回任意值,包名,方法名也可以这样用,不过有几级包就有几个 ,参数列表中使用 .. 表示参数可以是任意数据类型,有参数可以使任意类型,如果使用 * 标识参数可以是任何类型,但是必须有参数。
所以一般来说我们都是对业务层的恶方法增强,所以切入点表达式都是切到业务层实现类的哪个包 execution( com.itheima.service.impl..*(..))
```java
/**
* 环绕通知
* @param pjp
* spring 框架为我们提供了一个接口:ProceedingJoinPoint,它可以作为环绕通知的方法参数。
* 在环绕通知执行时,spring 框架会为我们提供该接口的实现类对象,我们直接使用就行。
* @return
*/
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
基于注解的aop配置
- 1、还是导入上面的jar包,在配置文件中导入context的名称空间,开启spring对注解的支持,和配置要扫描的包
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.itheima"/>
<!-- 配置spring开启注解AOP的支持 -->
<aop:aspectj-autoproxy/>
</beans>
- 2、在通知类上使用@Aspect注解他是一个切面类,然后在增强的方法上使用注解配置他是什么通知,这里用示例描述
/**
* 事务控制类
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Component("txManager")
@Aspect//表明当前类是一个切面类
public class TransactionManager {
//定义一个 DBAssit
@Autowired
private DBAssit dbAssit ;
}
@Before
作用:
把当前方法看成是前置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
//开启事务
@Before("execution(* com.itheima.service.impl.*.*(..))")
public void beginTransaction() {
try {
dbAssit.getCurrentConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
@AfterReturning
作用:
把当前方法看成是后置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用
//提交事务
@AfterReturning("execution(* com.itheima.service.impl.*.*(..))")
public void commit() {
try {
dbAssit.getCurrentConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
@AfterThrowing
作用:
把当前方法看成是异常通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用
//回滚事务
@AfterThrowing("execution(* com.itheima.service.impl.*.*(..))")
public void rollback() {
try {
dbAssit.getCurrentConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
@After
作用:
把当前方法看成是最终通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用
//释放资源
@After("execution(* com.itheima.service.impl.*.*(..))")
public void release() {
try {
dbAssit.releaseConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
@Around
作用:
把当前方法看成是环绕通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
/**
* 环绕通知
* @param pjp
* @return
*/
@Around("execution(* com.itheima.service.impl.*.*(..))")
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
针对于环绕通知,这个时候也可以像上面一样直接在后面写切入点表达式,但是我这里介绍另一种方式,就是在一个方法上面使用@Pointcut注解写表达式,然后@Around在引用这个方法
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt1() {}
引用方式:
/**
* 环绕通知
* @param pjp
* @return
*/
@Around("pt1()")//注意:千万别忘了写括号
public Object transactionAround(ProceedingJoinPoint pjp) {
//定义返回值
Object rtValue = null;
try {
//获取方法执行所需的参数
Object[] args = pjp.getArgs();
//前置通知:开启事务
beginTransaction();
//执行方法
rtValue = pjp.proceed(args);
//后置通知:提交事务
commit();
}catch(Throwable e) {
//异常通知:回滚事务
rollback();
e.printStackTrace();
}finally {
//最终通知:释放资源
release();
}
return rtValue;
}
Spring中的声明式事务控制
1、拷贝必要的jar包

2、创建Spring的配置文件并导入约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
(xml配置方式)重点
配置步骤:
(1)配置事务管理器
<!-- 配置业务层 -->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 配置账户的持久层 -->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
<!-- spring中基于XML的声明式事务控制配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性
是在事务的通知tx:advice标签的内部
-->
<!-- 第一步: 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 第二部:配置事务的通知 引用事务管理器内的方法来管理事
务,该通知可以对特定的方法进行特定的事务控制 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"> //这里引用一个事务管理器引用
<!-- 第五步:配置事务的属性
isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
-->
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>//增删改操作
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>//查询操作,优先检索这个
</tx:attributes>
</tx:advice>
<!-- 第三步:配置aop -->
<aop:config>
<!-- 配置切入点表达式 -->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<!--第四步: 建立切入点表达式和事务通知的对应关系 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
</beans>
基于注解的配置方式
1、导入坐标
2、配置扫描的包,开启spring对注解的支持
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.itheima"/>
<!-- 前提:配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
<!-- spring中基于注解 的声明式事务控制配置步骤
1、配置事务管理器
2、开启spring对注解事务的支持
3、在需要事务支持的地方使用@Transactional注解,相当于已经告诉了aop中要拦截的方法,和事务建立起关系
-->
<!-- 第一步: 配置事务管理器 ,并注入数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 第二步:开启spring对注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
@Service("accountService")
//第三步:事务支持的地方使用注解
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS) //只读 事务传播行为
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
@Override
public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
}
@Override
@Transactional(readOnly=false,propagation=Propagation.REQUIRED)
public void transfer(String sourceName, String targeName, Float money) {
//1.根据名称查询两个账户
Account source = accountDao.findAccountByName(sourceName);
Account target = accountDao.findAccountByName(targeName);
//2.修改两个账户的金额
source.setMoney(source.getMoney()-money);//转出账户减钱
target.setMoney(target.getMoney()+money);//转入账户加钱
//3.更新两个账户
accountDao.updateAccount(source);
//int i=1/0;
accountDao.updateAccount(target);
/*该注解的属性和 xml 中的属性含义一致。该注解可以出现在接口上,类上和方法上。
出现接口上,表示该接口的所有实现类都有事务支持。
出现在类上,表示类中所有方法有事务支持
出现在方法上,表示方法有事务支持。
以上三个位置的优先级:方法>类>接口*/
SpringMVC
1、快速入门
**1.1、导入坐标,引入开发jar包,具体坐标**
<!-- 版本锁定 -->
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
**1.2 配置核心的控制器(DispatcherServlet)**
1.2.1在web.xml中配置文件中核心控制器DispatcherServlet
<!-- SpringMVC的核心控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置Servlet的初始化参数,读取springmvc的配置文件,创建spring容器 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置servlet启动时加载对象,然后上面配置了初始化参数就可以读取配置文件 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
**1.3 编写springmvc.xml的配置文件**(这里面就可以创建容器,配置视图解析器,开启注解)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring创建容器时要扫描的包 -->
<context:component-scan base-package="com.itheima"></context:component-scan>
<!-- 配置视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property> //前缀
<property name="suffix" value=".jsp"></property> //后缀
</bean>
<!-- 配置spring开启注解mvc的支持
<mvc:annotation-driven></mvc:annotation-driven>-->
</beans>
**1.4 编写index.jsp和HelloController控制器类**
index.jsp
<body>
<h3>入门案例</h3>
<a href="${ pageContext.request.contextPath }/hello">入门案例</a>
</body>
HelloController
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 控制器
* @author rt
*/
@Controller
public class HelloController {
/**
* 接收请求
* @return
*/
@RequestMapping(path="/hello")
public String sayHello() {
System.out.println("Hello SpringMVC!!");
return "success";
}
}
**1.5 因为在视图解析器配置的前缀是web-inf/pages 后缀是.jsp,所以我们在web-inf下面创建pages文件夹然后编写jsp页面**
<body>
<h3>入门成功!!</h3>
</body>
**1.6小结,分析执行过程**
(1)启动Tomcat服务器的时候,因为配置了load-on-startup标签,可以加载对象,DispatchaerServlet对象便是这个servlet加载时的对象,同时给它配置了初始化参数,这个参数就是classpath:springmvc.xml,就可以加载这个配置文件了。
(2)springmvc.xml中开启了spring对注解的支持,同时扫描了包,HelloController对象就会被加到容器中。
(3)index.jsp发送请求的时候,请求先到DIsPatcherServlet核心控制器,然后再根据@RequestMapping注解找到具体执行的方法。
(4)根据返回值,再根据配置的视图解析器,然后去就去指定目录下查找指定的jsp文件。
(5)TomCat服务器渲染页面做出响应

2、请求参数的绑定
1.**绑定说明:**
1.1 表单提交的数据都是k=v格式的
1.2 springmvc的参数绑定过程是把表单提交的请求参数,作为控制器种方法的参数进行绑定的
1.3 要求: 提交的表单的name要和参数的名称相同(区分大小写)
2、支持的数据类型
2.1 基本数据类型和字符串类型
2.2 实体类型(JavaBean)
2.2.1 提交表单的name和JavaBean的属性名要一致
2.2.2 如果一个javabean类中包含其他引用类型,表单的name属性就要写成:对象.属性
例如:address.name
2.3 集合数据类型(List、map集合等)
2.3.1 jsp页面的编写方式 List[0].属性
**3、请求参数中文乱码的解决**
在web.xml中配置spring提供的过滤器类
<!-- 配置过滤器,解决中文乱码的问题 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-
class>
<!-- 指定字符集 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
**4、自定义类型转换器(表单提交的数据都是字符串类型,spring内部会默认进行数据类型转换)**
1、如果我们想把字符串转换成日期就可以自定义一个类型转换器
package cn.itcast.utils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
* 把字符串转换成日期的转换器,实现接口Converter
* @author rt
*/
public class StringToDateConverter implements Converter<String, Date>{
/**
* 进行类型转换的方法
*/
public Date convert(String source) {
// 判断
if(source == null) {
throw new RuntimeException("参数不能为空");
}try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// 解析字符串
Date date = df.parse(source);
return date;
} catch (Exception e) {
throw new RuntimeException("类型转换错误");
}
}
}
2、注册自定义类型转换器,在springmvc.xml配置文件中编写配置
<!-- 注册自定义类型转换器 -->
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="cn.itcast.utils.StringToDateConverter"/>
</set>
</property>
</bean>
<!-- 开启Spring对MVC注解的支持 -->
<mvc:annotation-driven conversion-service="conversionService"/>
3、常用的注解
1. RequestParam注解
1.1 作用:把请求中的指定名称的参数传递给控制器中的形参赋值
1.2 属性
1.2.1 value:请求参数中的名称
1.2.2 required:请求参数中是否必须提供此参数,默认值是true,必须提供
1.3 代码
/**
* 接收请求
* @return
*/
@RequestMapping(path="/hello")
public String sayHello(@RequestParam(value="username",required=false)String name) {
System.out.println("aaaa");
System.out.println(name);
return "success";
}
RequestBody注解- 作用:用于获取请求体的内容(注意:get方法不可以)
- 属性
- required:是否必须有请求体,默认值是true
- 代码如下
/**
* 接收请求
* @return
*/
@RequestMapping(path="/hello")
public String sayHello(@RequestBody String body) {
System.out.println("aaaa");
System.out.println(body);
return "success";
}
PathVariable注解
作用:拥有绑定url中的占位符的。例如:url中有/delete/{id},{id}就是占位符
属性- value:指定url中的占位符名称
代码如下
<a href="user/hello/1">入门案例</a>
/**
* 接收请求
* @return
*/
@RequestMapping(path="/hello/{id}")
public String sayHello(@PathVariable(value="id") String id) {
System.out.println(id);
return "success";
}
RequestHeader注解- 作用:获取指定请求头的值
- 属性
- value:请求头的名称
- 代码如下
@RequestMapping(path="/hello")
public String sayHello(@RequestHeader(value="Accept") String header) {
System.out.println(header);
return "success";
}
CookieValue注解- 作用:用于获取指定cookie的名称的值
- 属性
- value:cookie的名称
- 代码
@RequestMapping(path="/hello")
public String sayHello(@CookieValue(value="JSESSIONID") String cookieValue) {
System.out.println(cookieValue);
return "success";
}
ModelAttribute注解
作用- 出现在方法上:表示当前方法会在控制器方法执行前线执行。
- 出现在参数上:获取指定的数据给参数赋值。
应用场景
当提交表单数据不是完整的实体数据时,保证没有提交的字段使用数据库原来的数据。
具体的代码
修饰的方法有返回值 ```java /**
- 作用在方法,先执行
- @param name
- @return / @ModelAttribute public User showUser(String name) { System.out.println(“showUser执行了…”); // 模拟从数据库中查询对象 User user = new User(); user.setName(“哈哈”); user.setPassword(“123”); user.setMoney(100d); return user; } /*
- 修改用户的方法
- @param cookieValue
@return */ @RequestMapping(path=”/updateUser”) public String updateUser(User user) { System.out.println(user); return “success”; } ```
修饰的方法没有返回值 ```java /**
- 作用在方法,先执行
- @param name
- @return
/
@ModelAttribute
public void showUser(String name,Map
map) { System.out.println(“showUser执行了…”); // 模拟从数据库中查询对象 User user = new User(); user.setName(“哈哈”); user.setPassword(“123”); user.setMoney(100d); map.put(“abc”, user); } /* - 修改用户的方法
- @param cookieValue
@return */@RequestMapping(path=”/updateUser”) public String updateUser(@ModelAttribute(value=”abc”) User user) { System.out.println(user); return “success”; } ```
SessionAttributes注解- 作用:用于多次执行控制器方法间的参数共享
- 属性
- value:指定存入属性的名称
代码如下@Controller @RequestMapping(path="/user") @SessionAttributes(value= {"username","password","age"},types= {String.class,Integer.class}) // 把数据存入到session域对象中 public class HelloController { /** * 向session中存入值 * @return */ @RequestMapping(path="/save") public String save(Model model) { System.out.println("向session域中保存数据"); model.addAttribute("username", "root"); model.addAttribute("password", "123"); model.addAttribute("age", 20); return "success"; } /** * 从session中获取值 * @return */ @RequestMapping(path="/find") public String find(ModelMap modelMap) { String username = (String) modelMap.get("username"); String password = (String) modelMap.get("password"); Integer age = (Integer) modelMap.get("age"); System.out.println(username + " : "+password +" : "+age); return "success"; } /** * 清除值 * @return */ @RequestMapping(path="/delete") public String delete(SessionStatus status) {status.setComplete(); return "success"; } }
2、Sprinmvc的响应数据和结果视图
**2.1 返回值分类**
2.1.1 字符串:controller 方法返回字符串是指定了逻辑视图名,通过视图解析器解析为物理视图地址
2.1.2 void :这个时候可以在这个方法上面加上request和response就像原生web一样,重定向或者转发
2.1.3 ModelAndView: 是SpringMvc为我们提供的一个对象,该对象可以作为控制器方法的返回值,然后他就会存在request中,setViewName,就是返回的那个页面,同时他是以map的形式存在,下面有示例代码。
@RequestMapping("/testReturnModelAndView")
public ModelAndView testReturnModelAndView() {
ModelAndView mv = new ModelAndView();
mv.addObject("username", "张三");
mv.setViewName("success");
return mv;
}
jsp使用el表达式获取
${requestScope.username}
**2.2 转发和重定向**
2.2.1: forward转发,就是springmvc提供的默认值,使用的格式
return "forward:/WEB-INF/pages/success.jsp";
//注意,这里就要像原生web一样了写真正的url
2.2.2: Redirect重定向,使用格式
return "redirect: request.getContextPath()+"/index.jsp";
//和原生重定向一样不能访问web-inf下的目录
**2.3 ResponseBody响应json数据**
作用: 该注解用于将 Controller 的方法返回的对象,通过 HttpMessageConverter 接口转换为指定格式的
数据如:json,xml 等,通过 Response 响应给客户端。
在使用之前要导入三个jar包

下面通过代码更好的展示
jsp 中的代码:
<script
type="text/javascript"
src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
$("#testJson").click(function(){
$.ajax({
type:"post",
url:"${pageContext.request.contextPath}/testResponseJson",
contentType:"application/json;charset=utf-8",
data:'{"id":1,"name":"test","money":999.0}',
dataType:"json",
success:function(data){
alert(data);
}
});
});
})
</script>
<!-- 测试异步请求 -->
<input type="button" value=" 测试 ajax 请求 json 和响应 json" id="testJson"/>
控制器中的代码:
/**
* 响应 json 数据的控制器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
@Controller("jsonController")
public class JsonController {
/**
* 测试响应 json 数据
*/
@RequestMapping("/testResponseJson")
public @ResponseBody Account testResponseJson(@RequestBody Account account) {
System.out.println("异步请求:"+account);
return account;
}
}
运行结果如图:

3、SpringMvc中的异常处理
1、处理的思路:dao、service、controller出现异常都往上跑,最后由springmvc前端控制器交给异常处理器进行异常处理。
2、这里实现一个异常处理
2.1 编写一个异常类和错误页面
异常类
/**
* 自定义异常
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class CustomException extends Exception {
private String message;
public CustomException(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
jsp页面
jsp 页面:
<%@
page
language="java"
contentType="text/html;
charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE
html
PUBLIC
"-//W3C//DTD
HTML
4.01
Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>执行失败</title>
</head>
<body>
执行失败!
${message }
</body>
</html>
2.2 自定义异常处理器
/**
* 自定义异常处理器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
ex.printStackTrace();
CustomException customException = null;
//如果抛出的是系统自定义异常则直接转换
if(ex instanceof CustomException){
customException = (CustomException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
customException = new CustomException("系统错误,请与系统管理 员联系!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error");
return modelAndView;//返回error这个页面
}
}
2.3 配置异常处理器
<!-- 配置自定义异常处理器 -->
<bean id="handlerExceptionResolver"
class="com.itheima.exception.CustomExceptionResolver"/>
运行结果:

4、SpringMvc中的拦截器
**1、作用:**
他是springmvc独有的,只有使用了Springmvc才能使用。它只会拦截访问的控制器的方法,如果访问的jsp、html、css、js这些静态资源的时候他不会拦截。(和过滤器很像)
**2、自定义拦截器**(这里的每个方法具体说明了就是再说拦截器链的放行的顺序)
2.1 编写一个普通类实现HandlerInterceptor接口
/**
* 自定义拦截器
* @author 黑马程序员
* @Company http://www.ithiema.com
* @Version 1.0
*/
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
/*
按拦截器定义的顺序调用
只要配置了都会调用
决定是否调用其他拦截器或者直接去控制器层法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponseresponse, Object handler) throws Exception {
System.out.println("preHandle 拦截器拦截了");
return true;
}
/*
按拦截器定义逆序调用
在拦截器链内所有拦截器成功后调用
在控制层处理完请求后,这里主要是对request请求进行处理,在DispatcherServlet向客户端返回响应钱被调用
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
System.out.println("postHandle 方法执行了");
}
/*
按拦截器定义逆序调用
只有当前的这个了preHandle返回true才会调用
在DispatcherServlet完全处理完请求后背盗用,一般处理资源清理的操作
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion 方法执行了");
}
}
多个拦截器的执行顺序:

2.2 配置拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/> <!-- 表示对所有的访问都拦截-->
<bean id="handlerInterceptorDemo1"
class="com.itheima.web.interceptor.HandlerInterceptorDemo1"></bean>
</mvc:interceptor>
</mvc:interceptors>
运行结果:

4、SSM整合框架 (xml+注解)
**1、整合的思路:**
(1)先搭建整合的环境(就是导入坐标,需要的jar包)
(2)先把Spring的配置搭建完成
(3)Spring整合SpringMvc
(4)Spring整合Mybatis
**2、创建Maven的工程,并导入相关的依赖**
(1)创建ssm_parent父工程(打包方式选择pom,必须的)
(2)创建ssm_web子模块(打包方式是war包
(3)创建ssm_service子模块(打包方式是jar包
(4)创建ssm_dao子模块(打包方式是jar包
(5)创建ssm_domain子模块(打包方式是jar包
(6)web依赖于service,service依赖于dao,dao依赖于domain
(7)在ssm_parent的pom.xml文件中引入坐标依赖
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<mysql.version>5.1.6</mysql.version>
<mybatis.version>3.4.5</mybatis.version>
</properties>
<dependencies><!-- spring -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version><scope>compile</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency><dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>ssm</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
**3、搭建Spring框架**
(1)在ssm_web项目中创建applicationContext.xml文件,配置具体信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解扫描,要扫描的是service和dao层的注解,要忽略web层注解,因为web层让SpringMVC框架
去管理 -->
<context:component-scan base-package="cn.itcast">
<!-- 配置要忽略的注解 -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
4、搭建SpringMvc框架
(1)在web.xml中配置Dispatcher前端控制器
<!-- 配置前端控制器:服务器启动必须加载,需要加载springmvc.xml配置文件 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
<init-param><param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置解决中文乱码的过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2)创建SpringMvc.xml并进行相关配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描controller的注解,别的不扫描 -->
<context:component-scan base-package="cn.itcast">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置视图解析器 -->
<bean id="viewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- JSP文件所在的目录 -->
<property name="prefix" value="/WEB-INF/pages/" />
<!-- 文件的后缀名 -->
<property name="suffix" value=".jsp" />
</bean>
<!-- 设置静态资源不过滤 -->
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/images/" mapping="/images/**" />
<mvc:resources location="/js/" mapping="/js/**" />
<!-- 开启对SpringMVC注解的支持 -->
<mvc:annotation-driven />
</beans>
5、Spring整合SpringMvc框架
(1)怎么知道我们整合成功了,只要我们controller成功调用service对象的方法就可以了(说明注入成功了)
(2)当我们项目启动的时候,web.xml已经会去访问springmvc.xml了,这个时候我们我们其实只差ApplicationContext.xml了,我们想办法加载他就可以了。
在web.xml中配置ContextLoaderListener监听器(该监听器只能加载WEB-INF目录下的applicationContext.xml的配置文
件)。
<!-- 配置Spring的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
<!-- 配置加载类路径的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
6、Spring整合MyBatis框架(在Application.xml就可以了)
(1)我们其实只需要把SqlMapConfig.xml的内容配置到Application.xml就可以了
<!-- 配置C3P0的连接池对象 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///ssm" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- 配置SqlSession的工厂,可以创建一个真正的sqlsession ,然后sqlsession就可以创建代理对象存到ioc容器中-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置扫描dao的包 接口 将Mapper接口生成代理注入到Spring-->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.dao"/>
</bean>
7、最后的applicationContext,xml的内容
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解的扫描,希望处理service和dao,controller不需要Spring框架去处理 -->
<context:component-scan base-package="cn.itcast">
<!-- 配置哪些注解不扫描 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- Spring整合MyBatis框架 -->
<!-- 配置连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///ssm"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 配置SqlSessionFactory工厂 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置AccountDao接口所在包 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.itcast.dao"/>
</bean>
</beans>
springmvc.xml的内容
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解扫描,只扫描Controller注解 -->
<context:component-scan base-package="cn.itcast">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置的视图解析器对象 -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 过滤静态资源 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
<!-- 开启SpringMVC注解的支持 -->
<mvc:annotation-driven/>
</beans>
