AOP编程是编程中很重要的一部分,在开发中的地位和使用非常的重要和频繁,在各个技术的源码中也是频频出现,因此很有必要对此知识多加学习
AOP:面向切面编程
AOP是通过预编译方式或运行期动态代理实现程序功能的统一维护的一种技术,利用AOP可以对业务逻辑进行横向抽取,从而使得业务逻辑各部分之间的耦合度降低并提高代码的可重用性,提高开发效率。
Aspectj
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,并且它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。
相关jar包下载地址:aspectj | aspectjweaver
楼主上传的资源地址:优快云
案例地址:Spring-AOP
AspectJ的五种通知方式
在org.aspectj.lang.annotation
包下
注解 | 名称 | 作用域 |
---|---|---|
@Before | 前置通知 | 在方法执行之前执行 |
@After | 后置通知 | 在方法执行之后执行 |
@AfterReturning | 返回通知 | 在方法返回结果之后返回,可以访问方法的返回值,进行操作,例如写日志 |
@AfterThrowing | 异常通知 | 关联的方法若抛出异常,可以访问该异常,并进行操作 |
@Around | 环绕通知 | 围绕着方法进行执行,上述四种的功能都能实现,其必须有返回值 |
@Before属性
- String value();
- String argNames() default “”;
@After属性
- String value();
- String argNames() default “”;
@AfterReturning属性
- String value() default “”;
- String pointcut() default “”;
- String returning() default “”;
- String argNames() default “”;
@AfterThrowing属性
- String value() default “”;
- String pointcut() default “”;
- String throwing() default “”;
- String argNames() default “”;
@Around属性
- String value();
- String argNames() default “”;
上面的五种通知一共有四个类型的参数,就楼主的使用情况来看【在Spring框架中使用】argNames
起来比较麻烦,不灵活,很少会使用到它,如有获取代理对象方法入参的需求,也可以通过JoinPoint
或者Proceedingjoinpoint
对象直接来获取入参列表
Spring对AOP的支持
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:
- 默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。
- 当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB。
Proceedingjoinpoint对象
Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法,暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关)
Proceedingjoinpoint 可以修改被代理的方法的参数和最终的返回值,JoinPoint 则没有这么强大。
AOP编程步骤
AOP编程只需要程序员参与三个部分:
- 定义普通业务组件
- 定义切入点,一个切入点可能横切多个业务组件
- 定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作
综上所述进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理。即:代理对象的方法=增强处理+被代理对象的方法。
Spring AOP 实战
导入依赖:
<!-- 必然需要的是spring的aop jar包(导入spring-context的时候会自动加入此包,因此也可以不加入) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- aspectJ的核心jar包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
配置切面类
切面类开发完毕后在类名上加上@Aspect
注解,表示该类是切面类。
配置切入点
1.自定义注解作为切入点
//@annotation(自定义注解的全限定类名)
//例如:
value="@annotation(com.romantic.aop.MyAop1Annotation)"
2.使用切入点表达式匹配
@pointcut("execution(* com.romantic.*.*(..))")
private void pt() {}
在spring的配置文件内开启AOP的操作
<aop:aspectj-autoproxy proxy-target-class =“true”>
- proxy-target-class
该值默认为false,表示使用jdk动态代理织入增强。当配为true时,表示使用CGLIB动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,Spring也将自动使用CGLIB动态代理。
AOP的执行顺序
以下结果均经本人验证,仅供参考
一个AOP的执行顺序
aroundAdvice...start
beforeAdvice...
被增强方法...
aroundAdvice...end
afterAdvice...
afterReturnAdvice...
有异常的执行顺序
aroundAdvice...start
beforeAdvice...
被增强的方法[异常产生之前]...
afterAdvice...
afterthrowAdvice...
多个AOP的执行顺序配置有三种方式
- 切面类实现
org.springframework.core.Ordered
接口 - 使用注解
@Order(number)
- 使用xml配置文件配置,配置如下
<aop:config expose-proxy="true">
<aop:aspect ref="aopBean" order="0">
.......
</aop:aspect>
</aop:config>
多个AOP的执行顺序:
整体上看Order值小的AOP先执行,但是,如果只看后置(after类型)增强或者afterReturn类型的,就是Order值大的AOP的先执行,这点容易绕晕,画个图来说明一下(以同心圆为例)
以下输出结果均经本人验证,仅供参考
aroundAdvice...start
beforeAdvice...
aroundAdvice222...start
beforeAdvice222...
被增强方法...
aroundAdvice222...end
afterAdvice222...
afterReturnAdvice222...
aroundAdvice...end
afterAdvice...
afterReturnAdvice...