Spring AOP
1.SpringAOP介绍
1.1 概念
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。——摘自百度百科
理解:SpringAOP就是一种可以使代码复现,减少代码量,提高工作效率,当需求发生变化的时候,可以更好的更改代码的技术。
1.2 使用场景
我们之前在学习一些代码的时候,一些重复需要做的事情,代码都是几乎相同的这种工作,比如在IO操作的时候,就可以将这些代码抽取出来变成工具类·,就不用每次创建一个新的IO流的时候就要把所有的代码都写一遍,这种思想在jdbc连接的时候也充分体现了,将jdbc的五个步骤都封装到了工具类中。而面向切面编程就是找到共同的切点,将代码放到这个地方。
1.3 AOP中一些术语
连接点(Joinpoint):程序执行到某一个特定的位置,方法的运行前后,Spring只支持方法的连接点
切点(Pointcut):切点可以定位到对应的连接点,一个切点可以定位多个连接点,因为可以在多个地方使用这部分的代码
增强(Advice):又称为通知,这个包括一些前置、后置、异常等通知。
目标对象(Target):增强织入的目标类
引介(Introduction):特殊的增强为类添加一些属性和方法
织入(weaving):Spring采用动态代理织入的方式,将通知添加到目标具体连接点上的过程
代理(Proxy):一个类被织入了增强后,就产生了一个结果类,这就称为代理类
切面(Aspect):由切点和增强组成
2.SpringAOP入门案例
Spring完成了大部分困难的地方,在配置SpringAOP的动态代理的时候,需要注意配置的三个要素:何时,何地,做何事
何时:执行方法之前、之后、发生异常、等时候
何地:在包中那些方法需要这个配置
做何事:在这个地方提前执行了什么代码,或者运行了之后做了什么事
2.1 XML实现SpringAOP
下面是配置文件,由于没有使用Spring的一些注解,因此,这里采用bean配置文件的方式把类交给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: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
">
<bean id="userServiceImpl" class="cn.itsource._01xml_aop.service.impl.UserServiceImpl"></bean>
<bean id="txManager" class="cn.itsource._01xml_aop.util.TxManager"></bean>
<!--何时何地,做何事-->
<aop:config>
<!--其实配的就是方法-->
<!--切点 *表示返回值任意 .. 表示任意参数,可有可无-->
<aop:pointcut id="poinCut" expression="execution(* cn.itsource._01xml_aop.service.impl.UserServiceImpl.delete(..))"></aop:pointcut>
<!--切面 ref引入一个外部bean-->
<!-- 这里面的方法对应的是事务的几个方法名 -->
<aop:aspect ref="txManager">
<!--<!–前置通知:开始的时候,执行方法begin在配置切点的地方–>-->
<!--<aop:before method="begin" pointcut-ref="poinCut"></aop:before>-->
<!--<!–后置通知 –>-->
<!--<aop:after method="commit" pointcut-ref="poinCut"></aop:after>-->
<!--<!–异常通知–>-->
<!--<aop:after-throwing method="rollback" pointcut-ref="poinCut"></aop:after-throwing>-->
<!--<!–最终通知–>-->
<!--<aop:after-returning method="close" pointcut-ref="poinCut"></aop:after-returning>-->
<!--环绕通知,使用这一个就等于完成了上面定义的四个功能-->
<aop:around method="around" pointcut-ref="poinCut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
2.2 注解的形式实现SpringAOP
<?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
">
<!-- 普通注解 扫描包路径 -->
<context:component-scan base-package="cn.itsource._02anno_aop"></context:component-scan>
<!-- 切面注解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
开启了对注解的支持后就可以使用注解来实现事务
package cn.itsource._02anno_aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect // 配置切面
public class TxManager {
// 自定义一个方法,方法名
@Pointcut("execution(* cn.itsource._02anno_aop.service.*.*.*(..))")
public void txPointCut() {
}
// 前置通知
//@Before("txPointCut()")
public void begin() {
System.out.println("开启事务");
}
// 后置通知
//@AfterReturning("txPointCut()")
public void close() {
System.out.println("提交事务");
}
// 异常通知
//@AfterThrowing("txPointCut()")
public void rollback() {
System.out.println("回滚事务");
}
// 最终通知
//@After("txPointCut()")
public void commit() {
System.out.println("关闭事务");
}
// 环绕通知
// 使用注解的环绕通知有问题,一般都使用其他几个注解共同使用
@Around("txPointCut()")
public void around(ProceedingJoinPoint joinPoint) {
try {
//开启事务
begin();
joinPoint.proceed();
//提交事务
commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
rollback();
} finally {
// 关闭事务
close();
}
}
}
注意:环绕通知和普通通知只能使用一种,这里发现在使用环绕通知的时候,事务的提交关闭顺序颠倒了,因此不推荐使用这种方式去使用通知,推荐使用四个通知配合使用。