Spring AOP
AOP 术语
在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、日志代码、事务控制代码、性能监控代码。
名词 | 解释 |
---|---|
Joinpoint(连接点) | 它指的是那些可以用于把增强代码加入到业务主线中的点。在方法执行的前后通过动态代理技术加入增强的代码。在 Spring 框架 AOP 思想的技术实现中,也只支持方法类型的连接点。 |
Pointcut(切入点) | 它指的是那些已经把增强代码加入到业务主线进来之后的连接点。由上图中,我们看出表现层 register 方法就只是连接点,因为判断访问权限的功能并没有对其增强。 |
Advice(通知/增强) | 它指的是切面类中用于提供增强功能的方法。并且不同的方法增强的时机是不一样的。比如,开启事务肯定要在业务方法执行之前执行;提交事务要在业务方法正常执行之后执行,而回滚事务要在业务方法执行产生异常之后执行等等。这些就是通知的类型。其分类有:前置通知 后置通知 异常通知 最终通知 环绕通知。 |
Target(目标对象) | 它指的是代理的目标对象。即被代理对象。 |
Proxy(代理) | 它指的是一个类被 AOP 织入增强后,产生的代理类。即代理对象。 |
Weaving(织 入) | 它指的是把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。 |
Aspect(切面) | 它指定是增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类。例如,事务切面,它里面定义的方法就是和事务相关的,像开启事务,提交事务,回滚事务等等,不会定义其他与事务无关的方法。我们前面的案例中 TrasnactionManager 就是一个切面。 |
Aspect 切面 = 切入点 + 通知
Aspect 切面 = 切入点(锁定方法)+ 方位点(锁定方法中的特殊时机)+ 横切逻辑
众多的概念,目的就是为了锁定要在哪个地方插入什么横切逻辑代码。
AspectJ 切入点表达式
- 关键字:execution(表达式)
- 表达式:
- 访问修饰符返回值包名.包名…类名.方法名(参数列表)
- 标准写法示例:
- public void li.service.impl.AccountServiceImpl.saveAccount()
- 访问修饰符可以省略
- void li.service.impl.AccountServiceImpl.saveAccount()
- 返回值可以使用通配符,表示任意返回值
- * li.service.impl.AccountServiceImpl.saveAccount()
- 包名可以使用通配符,表示任意包,但是有几级包就需要写几个*.
- * *.*.*.AccountServiceImpl.saveAccount()
- 包名可以使用..表示当前包及其子包
- * *..AccountServiceImpl.saveAccount()
- 类名和方法名都可以使用 * 实现通配
- * *..*.*()
- 参数列表:
- 可以直接写数据类型:
- 基本类型直接写名称 int
- 引用类型写包名.类名 java.lang.String
- 可以使用通配符,表示任意类型,但是必须有参数
- 可以使用..有无参数均可,有参数可以是任意类型
- 可以直接写数据类型:
- 全通配写法:
- * *..*.*(..)
- 实际开发中切入点表达式的通常写法:
- 切到业务层实现类下的所有方法 li.service.impl.*.*(..)
五种通知类型
前置通知
配置方式:aop:before 标签
1 | <!-- |
1 | public void beforeMethod(JoinPoint joinPoint) { |
执行时机:在切入点方法(业务核心方法)执行之前执行。
说明:前置通知可以获取切入点方法的参数,并对其进行增强。
后置通知
配置方式:aop:after-returning 标签
1 | <!-- |
1 | public void afterMethod(JoinPoint joinPoint, Object result) { |
执行时机:在切入点方法(业务核心方法)正常执行之后执行,出现异常后后置正常执行通知不再执行了,而是执行异常通知。
说明:后置通知即可以获取切入点方法的参数,也可以获取切入点方法的返回值。
异常通知
配置方式:aop:after-throwing 标签
1 | <!-- |
执行时机:在切入点方法(业务核心方法)执行产生异常之后执行,如果没有出现异常则不会执行。
说明:异常通知即可以获取切入点方法的参数,也可以获取切入点方法执行产生的异常信息。
最终通知
配置方式:aop:after-throwing 标签
1 | <!-- |
执行时机:最终通知的执行时机是在切入点方法(业务核心方法)执行完成之后,切入点方法返回之前执行。 换句话说,无论切入点方法执行是否产生异常,它都会在返回之前执行。
说明:最终通知执行时,可以获取到通知方法的参数。同时它可以做一些清理操作。
环绕通知
配置方式:aop:after-throwing 标签
1 | <!-- |
1 | public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint) { |
执行时机:Spring 框架为我们提供了一个接口 ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
说明:可以控制原有逻辑是否执行。
使用 Spring AOP 的方式
XML 方式
xml 文件头
1 |
|
配置横切逻辑 bean
1 | <bean id="logUtils" class="com.lagou.edu.utils.LogUtils"/> |
使用 aop:config 标签开始 aop 的配置
1 | <!--配置 aop 的过程其实就是把 aop 相关概念落地--> |
改变代理方式的配置
Spring 在选择创建代理对象时,会根据被代理对象的实际情况来选择。被代理对象实现了接口,则采用基于接口的动态代理。当被代理对象没有实现任何接口的时候,Spring会自动切换到基于子类的动态代理方式。
无论被代理对象是否实现接口,只要不是 final 修饰的类都可以采用 cglib 提供的方式创建代理对象。所以 Spring 也考虑到了这个情况,提供了配置的方式实现强制使用基于子类的动态代理(即cglib的方式),配置的方式有两种:
- 使用 aop:config 标签配置
1
<aop:config proxy-target-class="true">
- 使用 aop:aspectj-autoproxy 标签配置
1
2<!--此标签是基于 XML 和注解组合配置 AOP 时的必备标签,表示 Spring 开启注解配置 AOP 的支持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
XML + 注解方式
XML 中开启 Spring 对注解 AOP 的支持
1 | <aop:aspectj-autoproxy/> |
Java 代码示例
1 |
|
注解方式
在使用注解驱动开发 aop 时,是用注解替换掉配置文件中的下面这行配置
1 | <aop:aspectj-autoproxy/> |
在配置类中使用如下注解替换上述配置
1 | //开启spring对注解AOP的支持 |