文章目录
- 前言
- 一、AOP
- 1.1问题的提出
- 1.2AOP 概述
- 1.3AOP基本概念
- 二、springAOP实现
- 2.1基于aspectj 的 xml配置方式实现
- 2.1.1下载 AOP 相关 jar
- 2.1.2在xml文件中配置
- 2.2基于注解方式的实现
- 2.2.1启动 AspectJ 支持
- 2.2.2定义通知
- 2.2.3 测试
- 三、事物管理
- 3.1 事务的相关概念
- 3.2 Spring中的事务管理
- 3.2.1编程式事务
- 3.2.1声明式事务
- 四、事务传播行为
- 4.1什么叫事务传播行为?
- 4.2事务传播行为类型
- 4.3测试事务传播行为
- 五、Spring 集成 Mybatis
- 5.1导入mybatis jar 包
- 5.2配置 sqlSessionFactory
- 5.3指定生成接口代理
- 总结
前言
Spring是分层的JavaSE/EE full-stack 轻量级开源框架,以IOC(Inverse of Control 控制反转)和AOP(Aspect Oriented Programming 面向切面编程)为内核,使用基本的JavaBean来实现程序功能。
一、AOP
1.1问题的提出
在实际开发中,可能会同时需要编写业务核心代码、事务处理代码和日志记录代码,通常的做法如下
public class Myservice {
public void logic(){
doLog();
//处理业务核心代码
doTransaction();
}
public void doLog(){
//日志代码
}
public void doTransaction(){
//事务代码
}
}
这种将核心业务模块与其他非核心的代码交织在一起,不利于代码的维护,影响了代码的模块独立性能。实际上,事务处理和日志记录可以独立在一个模块里,可给所有的服务公用,因此,面向切面的编程技术AOP便应运而生。
1.2AOP 概述
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP和OOP有什么区别?
OOP:面向对象编程:针对业务处理过程的实体及其属性和行为进行抽象封装,以获 得更加清晰高效的逻辑单元划分
AOP:AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果
例:
对于学生这样一个业务实体进行封装,自然是OOP的任务,我们可以建立一个Student类,并将学生相关的属性和行为封装其中。而用AOP 设计思想对学生进行封装则无从谈起。
同样,对于保存日志、权限检查、事务处理这些动作片段进行划分,是AOP的目标领域。
总结:OOP面向名次领域,AOP面向动词领域。
面向切面编程的好处就是: 减少重复,专注业务
核心原理: 使用动态代理的方式在执行方法前后或者出现异常的时候做加入相关的逻辑
1.3AOP基本概念
1.连接点(Joinpoint):类中可以被增强的方法,这个方法就被称为连接点
2.切入点(pointcut):类中有很多方法可以被增强,但实际中只有 add 和 update被增了,那么 add 和 update 方法就被称为切入点(实际实现的连接点)
3.通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通 知分为方法执行前通知,方法执行后通知,环绕通知等.
4.切面(Aspect):把通知添加到切入点的过程叫切面.
5.目标(Target): 代理的目标对象(要增强的类)
6.代理(Proxy): 向目标对象应用通知之后创建的代理对象
二、springAOP实现
在Spring中使用AOP开发时,一般使用 AspectJ的实现方式。 AspectJ是一个优秀面向切面的框架,它扩展了Java语言,提供了强大的切面实现。
AspectJ 中常用的通知有五种类型:
1.前置通知:方法执行之前
2.后置通知:在方法执行之后执行
3.环绕通知:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。
4.异常通知:在方法抛出异常之后
5.最终通知:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容。
2.1基于aspectj 的 xml配置方式实现
2.1.1下载 AOP 相关 jar
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
2.1.2在xml文件中配置
<bean id="aopDemo" class="com.ffyc.spring.aop.AopDemo"></bean>
<aop:config>
<!--配置切入点-->
<!--execution(* com.ffyc.spring.dao.*.*(..)) 该表达式的意思是匹配com.ffyc.spring.dao中任意类的任意方法的执行-->
<!-- 返回类型 包名 任意参数-->
<aop:pointcut expression="execution(* com.ffyc.spring.dao.UserDao.save2(..))" id="save2"></aop:pointcut>
<!--编织:配置通知和切入点,把两者连接起来-->
<aop:aspect ref="aopDemo">
<!-- <aop:before method="saveLog" pointcut-ref="save2"/>-->
<!--配置环绕通知,可以用环绕通知一次性完成前置后置和异常通知-->
<aop:around method="saveLogRound" pointcut-ref="save2"/>
</aop:aspect>
</aop:config>
2.2基于注解方式的实现
2.2.1启动 AspectJ 支持
<!--启动 AspectJ 支持-->
<aop:aspectj-autoproxy />
2.2.2定义通知
@Component
@Aspect
public class AopDemo {
public void saveLog(){
System.out.println("保存日志");
}
/*环绕通知:当调用配置了环绕通知的方法时,先会进通知的方法,执行前置通知,当调用ProceedingJoinPoint类的processd()方法会进入到
实际被增强的方法体中,如果出现异常,则执行异常通知,最后执行后置通知*/
@Around("execution(* com.ffyc.spring.dao.UserDao.save2(..))")
public void saveLogRound(ProceedingJoinPoint pj) throws Throwable {
System.out.println("前置通知");
pj.proceed();
System.out.println("后置通知");
}
}
2.2.3 测试
@Repository
public class UserDao {
public void save2(){
System.out.println("被增强的方法体执行了");
}
}
@Service()
public class UserService {
@Autowired
@Qualifier("userDao" )
private UserDao userDao;
public UserDao getUserDao() {
return userDao;
}
}
public class Test1 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService=applicationContext.getBean("userService",UserService.class);
userService.getUserDao().save2();
}
}
三、事物管理
3.1 事务的相关概念
事物可以看做是由对数据库若干操作组成的一个单元。
理解事务之前,先举一个日常生活中最常见的事:转账。
比如你要给你好朋友转账500元,大体有两个步骤:首先你的银行卡扣掉500元钱;然后你好朋友的银行卡多500元钱。这两个步骤必须是要么都执行要么都不执行。不管哪一个步骤失败了以后,整个转账过程都能回滚。
事务就是用来解决类似问题的。事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,从而保证数据满足一致性的要求。
3.2 Spring中的事务管理
3.2.1编程式事务
在项目中很少使 用 , 这种方式需要注入一个事务管理对象 TransactionTemplate ,然后在我们代码中需要提交事务或回滚事务时自己写代码实现。
3.2.1声明式事务
建立在AOP基础上,本质是对方法前后进行拦截,所以声明式事务是方法级别的。
Spring针对不同的dao框架,提供了不同的实现类,Jdbc,mybatis事物管理实现类是 DataSourceTransactionManager.
1.配置事物管理器
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClass}"></property>
<property name="url" value="${jdbcUrl}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${pwd}"></property>
<property name="initialSize" value="10"></property>
<property name="maxActive" value="20"></property>
</bean>
<!-- 配置 spring 事务管理类, 并注入数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2.Xml配置方式
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置切入点-->
<aop:pointcut expression="execution(* com.ffyc.spring.dao.UserDao.save2(..))" id="allmethod"/>
<!--把切入点和配置传播行为编织起来-->
<aop:advisor advice-ref="txadvice" pointcut-ref="allmethod"/>
</aop:config>
测试
public class Test1 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService=applicationContext.getBean("userService",UserService.class);
userService.getUserDao().save2();
}
}
UserDao中的save2方法
public void save2(){
jdbcTemplate.update("INSERT INTO admin(NAME,sex,phone)VALUES(?,?,?)", "李军", "男", "15757900521");
int x=10/0;
jdbcTemplate.update("INSERT INTO admin(NAME,sex,phone)VALUES(?,?,?)", "王刚", "男", "15257909876");
}
save2()方法中出现了除0异常,由于我们为save2()方法加了事务管理,所以数据库中应该没有插入李军和王刚的信息
当我们把异常取消,重新测试一下代码,看看数据能不能正常插入
public void save2(){
jdbcTemplate.update("INSERT INTO admin(NAME,sex,phone)VALUES(?,?,?)", "李军", "男", "13057900521");
// int x=10/0;
jdbcTemplate.update("INSERT INTO admin(NAME,sex,phone)VALUES(?,?,?)", "王刚", "男", "13057900521");
}
从数据库中可以看出两条数据插入到了数据库中
3.注解方式
xml的配置相对来说比较繁琐,不方便使用,spring实现事务管理还有一种注解方式
开启注解事务管理
<tx:annotation-driven transaction-manager="transactionManager"/>
在需要事务管理的方法或者类(把注解加到类上面,该类中的所有方法都添加事务管理)上面添加 @Transactional注解即可
@Transactional(propagation= Propagation.REQUIRED)
public void save2(){
jdbcTemplate.update("INSERT INTO admin(NAME,sex,phone)VALUES(?,?,?)", "李军", "男", "13057900521");
// int x=10/0;
jdbcTemplate.update("INSERT INTO admin(NAME,sex,phone)VALUES(?,?,?)", "王刚", "男", "13057900521");
}
四、事务传播行为
4.1什么叫事务传播行为?
即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。
事务传播行为当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
注意:事务传播行为是 Spring 框架独有的事务增强特性,他不属于的事务实际提供方数据库行为
例如:A事务方法调用B事务方法时,B是在A事务方法中运行还是由自己开启一个方法运行,这就是由事务传播行为决定
4.2事务传播行为类型
4.2.1 PROPAGATION.REQUIRED
4.2.2 PROPAGATION.SUPPORTS
4.2.3 PROPAGATION.REQUIRES_NEW
4.3测试事务传播行为
1.PROPAGATION.REQUIRED
public class Test1 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
DeptService deptService=applicationContext.getBean("DeptService",DeptService.class);
deptService.saveDept();
}
}
@Service(value = "DeptService")
public class DeptService {
@Autowired
DeptDao deptDao;
@Autowired
CommonService commonService;
@Transactional(propagation= Propagation.REQUIRED)
public void saveDept(){
deptDao.saveDept();
commonService.saveLog();
}
}
@Service
public class CommonService {
@Autowired
CommonDao commonDao;
@Transactional(propagation=Propagation.REQUIRED)
public void saveLog(){
commonDao.saveLog();
int a=10/0;
}
}
因为CommonService的事务传播行为为Propagation.REQUIRES_NEW,所以CommonService加入到了DeptService事务中,当CommonService中的saveLog()方法出现异常时,两个方法都不会提交数据到数据库中
2.PROPAGATION.REQUIRES_NEW
这次测试我们把异常加到调用者上面
@Transactional(propagation= Propagation.REQUIRED)
public void saveDept(){
deptDao.saveDept();
commonService.saveLog();
int a=10/0;
}
然后修改被调用者的事务管理为PROPAGATION.REQUIRES_NEW
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void saveLog(){
commonDao.saveLog();
}
因为CommonService的事务传播行为为Propagation.REQUIRES_NEW,所以CommonService把DeptService事务挂起,自己新建了一个事务,由于该事务中没有异常,所以 commonDao.saveLog()会提交到数据库中,但DeptService中的saveDept()方法出现了异常,所以deptDao.saveDept()不会提交到数据库中
注意:
事务不生效的场景:
1.异常被 catch 捕获导致@Transactional 失效
2.@Transactional 应用在非 public 修饰的方法上
本文主要介绍了三种事务传播类型,关于其他的事务传播类型,读者可以自行查阅相关资料了解
五、Spring 集成 Mybatis
其核心是将 SqlSessionFactory 交由 Spring 管理,并由 Spring管理对dao接口的代理实现。
5.1导入mybatis jar 包
<!--Spring 结合 mybatis 插件包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
5.2配置 sqlSessionFactory
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml"></property>
</bean>
5.3指定生成接口代理
<!-- 生成接口代理对象-->
<bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ffyc.ssm.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
在 service 中注入Dao代理接口,此接口由spring代理实现
总结
本章主要介绍了spring的AOP的实现,事务管理以及几种事务传播行为。AOP是spring的内核,读者不仅要掌握AOP的实现,更应该在深入学习AOP的原理,扩展自己的思维。在以后的学习中,我们将利用SpringBoot整合spring、mybatis以及springMVC,进一步提高我们的开发效率。