Spring整合MyBatis
一、后处理器
概述
Spring的AOP应用中,会产生处理后处理器的代理对象,我们可以通过debug调试得到该Aspectware..对象。
该对象可以在产生bean完成之前做一些再加工处理的操作。
自定义后处理器
Spring的AOP中会产生动态代理对象去调用后处理器方法;我们也可以自定义一个后处理器的类;同样可以完成AOP的触发过程。
生命周期:后处理器的触发执行,在init方法的前后
构造方法->SET->后处理器前->init->后处理器后->bean完成->销毁
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器 在init之前执行~~~"+bean.getClass());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器 在init之后执行~~~"+bean.getClass());
return bean;// 此处的返回是 getBean() 最终的返回值
}
}
注意:需要将后处理器的bean放到spring容器
动态代理源码(了解)
在Spring的AOP过程中,搜索AbstractAutoProxyCreator类,里面提供了产生动态代理对象的后处理器的执行过程,判断缓存里面有没有代理的bean,如果没有,则创建动态代理的bean。
二、Spring整合MyBatis
导包
导入依赖包,及引入完整的容器配置,需要将tx事务相关的文件引入
<!-- Spring常用依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- aop包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- spring的包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- Druid连接池包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- spring+mybatis集成依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
DAO测试
先从DAO层测试开始,将mybatis相关bean交给spring容器完成。
创建DAO接口与Mapper文件。
<!-- 数据库配置的引入 -->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 类似mybatis配置文件中的数据源的引入 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!--基本配置-->
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<!-- 注意:不要直接使用${username}和${password}因为会读到系统的账户 -->
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 产生SQLSessionFactory的bean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 将上面的数据源引入到SqlSessionFactoryBean的工厂中 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 注册Mapper文件 -->
<property name="mapperLocations">
<list>
<value>classpath:mapper/*.xml</value>
</list>
</property>
<!-- 取别名 -->
<property name="typeAliasesPackage" value="com.qf.b_project.entity"></property>
</bean>
<!-- 产生UserDao的bean -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--产生DAO接口下的实现类的对象(bean方式) id为接口首字母小写
<bean id="userDao" class="com.qf.dao.Userdao实现类" >
-->
<property name="basePackage" value="com.qf.b_project.dao"></property>
</bean>
<bean id="userService" class="com.qf.b_project.service.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
Druid监控中心(了解)
只要配置web,并加入映射路径,即可访问到该监控中心
<!--web.xml-->
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
访问路径:http://localhost:8080//druid/
在监控平台可观察数据库访问的变化。
三、事务
事务配置
在数据库操作中引入事务,提升安全性;成功了提交,失败了,回滚
事务中引入的数据源和SqlSessionFactory中引入的数据源是同一个,否则事务是无效的。
<!-- 引入事务管理器,里面的数据源和SqlSessionFactory的是同一个 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务中的增强:自身id,绑定事务管理器的bean -->
<tx:advice id="tx" transaction-manager="txManager">
<!-- 事务规则的配置 -->
<tx:attributes>
<!-- name对应业务层的方法名 select开头,一般是查询-->
<tx:method name="select*"/>
<!-- 增删改增加事务的方法 add*代表业务层方法名add开头的 -->
<tx:method name="add*" />
<tx:method name="delete*" />
<tx:method name="update*" />
</tx:attributes>
</tx:advice>
<!-- 切点与增强的绑定,形成切面 UserServiceImpl类下的方法都需要加增强-->
<aop:config>
<aop:pointcut id="po" expression="execution(* com.qf.b_project.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="tx" pointcut-ref="po"></aop:advisor>
</aop:config>
事务的属性
隔离级别:
默认为repeatable-read级别,隔离级别从低到高,分别是:
read-uncommited < read-commited < repeatable-read < serialized-read
级别越高安全性越高,最高的级别完全不会出现安全隐患。
级别越高性能越低,一般建议用中间的默认级别:repeatable-read。
read-uncommited:可能出现脏读,不可重复读,虚读
read-commited:避免了出现脏读;可能出现不可重复读,虚读
repeatable-read:避免了出现脏读,不可重复读;可能出现虚读
serialized-read:完全避免了所有安全隐患
脏读:处在事务当中,查看到了另一个线程中未提交的update数据
不可重复读:处在事务当中,查看到了另一个线程中已提交的update数据
虚读:处在事务当中,查看到了另一个线程中已提交的insert数据
传播行为:在事务的嵌套中能够使用,查询可以设置为:SUPPORTS;表示不存在外部事务,则不开启新事务。默认REQUIRED
读写性:readyonly,默认为false,表示可读可写;往往查询可以改为true,只读即可。
事务超时:timeout,默认为-1,根据数据库设置超时时间,一般CRUD都可不用设置超时
事务回滚:运行时异常会自动回滚,编译时异常则不会回滚,除非将编译时异常转运行时异常;要么加上属性 : rollback-for=“Exception”,往往该属性用在增删改中。
测试事务回滚
测试事务是否有效,可以通过事务的回滚实现来做实验
业务层:
@Override
public int updateUser(User user) throws Exception {
//开启事务
int res = userDao.updateUser(user);
System.out.println("修改:"+res);
//int i=1/0; //运行时异常自动回滚
if(true) { //编译时异常
//try {
throw new Exception("编译时异常");
//} catch (Exception e) { //转运行时异常
// throw new RuntimeException("运行时异常");
//}
}
//提交或回滚事务
return res;
}
容器中的配置:
<!-- 事务中的增强:自身id,绑定事务管理器的bean -->
<tx:advice id="tx" transaction-manager="txManager">
<!-- 事务规则的配置 -->
<tx:attributes>
<!-- name对应业务层的方法名 select开头,一般是查询
propagation="SUPPORTS":不存在外部事务,则不用开启新事务
-->
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<!-- 增删改增加事务的方法 add*代表业务层方法名add开头的 -->
<tx:method name="add*" rollback-for="Exception" />
<tx:method name="delete*" rollback-for="Exception" />
<tx:method name="update*" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
四、注解开发
注解类
@Service 业务类专用 生成
@Repository dao实现类专用 生成
@Component 通用 其他类的bean产生
@Controller web层专用 控制层的bean参数
@Scope 用户控制bean的创建模式
DI注解
- @Autowired 基于类型自动注入 (
) - @Resource 基于名称自动注入 (
) - @Qualifier(“userDAO”) 限定要自动注入的bean的id,一般和@Autowired联用
- @Value DI注入 注入属性值
注解所需的spring配置:
<!-- 在com.qf包下面都可以进行注解扫描 -->
<context:component-scan base-package="com.qf"></context:component-scan>
<!-- 使用事务的注解方式 不常用 -->
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
注解应用:
@Service //产生<bean id="userServiceImpl" class... />
@Scope("singleton") //单例设置
public class UserServiceImpl implements UserService {
//@Autowired
//@Qualifier("userDao") //@Autowired+@Qualifier=先按类型匹配,有冲突再按ID匹配
@Resource //先按名字匹配,如果名字匹配不了则按类型
private UserDao userDao; //IOC注入
@Value("zsf") //<properties name="name" value="zsf"/>
private String name;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public User selectById(Integer id) {
System.out.println(name);
return userDao.selectById(id);
}
}
AOP注解
AOP的注解方式应用,完成的目标:给核心业务加入增强
AOP注解启动:
<!-- 添加如下配置,启用aop注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
切面类的定义:
@Aspect //将当前类MyAspect当成切面类和增强类 里面可以产生切点和增强
@Component //MyAspect类产生bean对象
public class MyAspect {
// 定义切入点
@Pointcut("execution(* com.qf.b_project.service.UserServiceImpl.*(..))")
public void pc(){}
@Before("pc()") // 前置增强
public void mybefore(JoinPoint a) {
System.out.println("target:"+a.getTarget());
System.out.println("args:"+a.getArgs());
System.out.println("method's name:"+a.getSignature().getName());
System.out.println("before~~~~");
}
}
五、Spring单元测试
导入依赖包,然后进行单元测试
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class) //由SpringJUnit4ClassRunner启动测试
@ContextConfiguration("classpath:bean2.xml") //spring的配置文件位置
public class SpringJunit {//当前测试类也会被纳入工厂中,产生bean
/* @Resource
private UserDao userDao; //IOC的注入
@Test
public void daoTest(){
User user = userDao.selectById(1);
System.out.println(user);
}*/
@Resource
private UserService userService;
@Test
public void serviceTest(){
User user = userService.selectById(1);
System.out.println(user);
}
}
六、总结与作业
总结
1.后处理器
给bean创建过程中增加再加工过程;通过动态代理完成
自定义后处理器--实现BeanPostProcessor接口
2.Spring整合MyBatis(重点)
将之前MyBatis用Spring容器来配置
整合数据源-SqlSessionFactory-MapperScannerConfigurer产生DAO-进行DAO测试
监控中心(了解)
3.事务(重点)
事务配置、属性说明、测试事务回滚
4.注解开发(重点)
注解类(重点)-事务注解测试、DI注解(重点)、AOP注解
5.Spring单元测试
进行dao,service的单元测试
作业
1.使用注解方式完成StudentService的查询
2.使用注解的事务,完成StudentService的修改
注意:使用spring的测试
晨考
1. @Service 和@Service("userService")
2. @Service和@Component的区别
3. 事务中增删改加入rollback-for="Exception"属性的作用
4. 事务隔离级别中,脏读,不可重复读,虚读的区别