一、前言
面向切面编程 AOP 是一种常见的编程思想,是面向对象编程OOP的一种补充,AOP 框架通过修改源代码,将处理逻辑编织到指定的业务模块中。
常见的处理比如:在方法执行前进行校验,在方法执行后进行日志的记录,事务管理,消息通知,业务监控等。
本篇主要介绍 Aspectj 通过注解配置,切点表达式的书写!!
二、动态代理技术说明
- JDK动态代理:JDK原生的实现方式,需要被代理的目标类必须实现接口!它会根据目标类的接口动态生成一个代理对象!代理对象和目标对象有相同的接口!(拜把子)
- CGLIB:通过继承被代理的目标类实现代理,所以不需要目标类实现接口!(认干爹)
<!-- 开启aspect注解的支持,proxy-target-class表示选择动态代理的方式,true表示使用CGLIB代理,false表示使用JDK动态代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
三、AOP 术语说明
横切关注点:从每个方法中抽取出来的同一类非核心业务。
前置通知、
通知 (Advice): 每一个横切关注点上要做的事情都需要写一个方法来实现。通知的类型有:前置通知、返回通知、异常通知、后置通知、环绕通知。
**连接点 (Joint Point)**:指那些被拦截到的点。在Spring中,可以被动态代理拦截目标类的方法。
**切入点 (Pointcut)**:定位连接点的方式,是一个表达式。符合条件的每一个方法都是一个具体的连接点。
**切面 (Aspect)**:切入点和通知的结合,是一个类。
**目标(target)**:被代理的目标对象。
**代理 (proxy)**:向目标对象应用通知之后创建的代理对象。
织入 (weaving): 把通知应用到目标上,生成代理对象的过程。可以在编译期织入,也可以在运行期织入,Spring采用后者。
四、通知类型介绍
Spring AOP 中现有方法进行增强处理有 5 种通知类型,分别是:
注解描述说明@Before在被代理的目标方法前执行不能阻止业务模块的执行,除非通知方法抛出异常@AfterReturning在被代理的目标方法成功结束后执行-@AfterThrowing在被代理的目标方法异常结束后执行-@After在被代理的目标方法最终结束后执行无论业务模块是否抛出异常,都会执行@Around使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置封装后由通知方法调用暴露的proceed()方法,去执行目标方法
以上五种通知类型可以对现有方法进行增强处理。
如果需要对现有类增加新的方法,可以通过 @DeclareParents 注解可以实现,DeclareParents 是一种 引入 (Introduction ) 类型的模型,在属性声明上使用,主要用于为指定的业务模块添加新的接口和相应的实现。
五、切点表达式简介
切点指示器描述arg()限制连接点匹配参数为指定类型的执行方法@args()限制连接点匹配参数由指定注解标注的执行方法execution()用于匹配是连接点的执行方法this()限制连接点匹配AOP代理的Bean应用为指定类型的类target()限制连接点匹配目标对象为指定类型的类@target()限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解within()限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型的注解@within()限制连接点匹配指定注解所标注的类型(当使用SpringAOP时,方法定义在由指定的注解所标注的类型)@annotation限制匹配带有指定注解连接点
六、通配符合与逻辑运算符
@AspectJ 支持三种通配符:
符号描述匹配任意字符,只匹配一个元素…匹配任意字符,可以匹配多个元素,在表示类时,必须和联合使用+表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.test.User+,表示继承该类的所有子类包括本身
逻辑运算符:
切点表达式由切点函数组成,切点函数之间还可以进行逻辑运算,组成复合切点。
符号描述XML中表示&&与操作符。相当于切点的交集运算。xml配置文件中使用切点表达式,&是特殊字符,所以需要用and来表示//或操作符。相当于切点的并集运算。or!非操作符,相当于切点的反集运算。not
七、切点表达式
(1)arg() 限制连接点匹配参数为指定类型的执行方法
语法:
args(param-pattern)
- param-pattern:参数类型,全路径的
示例:
@Pointcut("args(java.lang.String)")
public void stringParams() {}
(2)@args() 限制连接点匹配参数由指定注解标注的执行方法
这个指示符是用来匹配连接点的参数的,@args 指出连接点在运行时传过来的 参数的类必须要有指定的注解
语法:
@args(annotation-type)
annotation-type:注解类型,全路径的
示例:
// 参数 @Entity 注解的 bean 对象的方法
@Pointcut("@args(javax.persistence.Entity)")
public void methodsEntityParams() {}
(3)execution() 用于匹配是连接点的执行方法
Spring 切面粒度最小是达到方法级别,而 execution 表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的配置,所以是使用最广泛的。
语法:
// 问号 ? 表示该项可以没有
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern:方法的可见性修饰符,如 public,protected,private;
- ret-type-pattern:方法的返回值类型,如 int,void 等;
- declaring-type-pattern:方法所在类的全路径名,如 com.spring.Aspect;
- name-pattern:方法名,如 getOrderDetail();
- param-pattern:方法的参数类型,如 java.lang.String;
- throws-pattern:方法抛出的异常类型,如 java.lang.Exception;
示例:
// 匹配目标类的所有 public 方法,第一个 * 代表返回类型,第二个 * 代表方法名,..代表方法的参数
execution(public * *(..))
// 匹配目标类所有以 User 为后缀的方法。第一个 * 代表返回类型,*User 代表以 User 为后缀的方法
execution(* *User(..))
// 匹配 User 类里的所有方法
execution(* com.test.demo.User.*(..))
// 匹配 User 类及其子类的所有方法
execution(* com.test.demo.User+.*(..)) :
// 匹配 com.test 包下的所有类的所有方法
execution(* com.test.*.*(..))
// 匹配 com.test 包下及其子孙包下所有类的所有方法
execution(* com.test..*.*(..)) :
// 匹配 getOrderDetail 方法,且第一个参数类型是 Long,第二个参数类型是 String
execution(* getOrderDetail(Long, String))
(4)this() 和 target()
this() 限制连接点匹配 AOP 代理的 Bean 应用为指定类型的类
target() 限制连接点匹配目标对象为指定类型的类
this 用来匹配的连接点所属的对象引用是某个特定类型的实例,target 用来匹配的连接点所属目标对象必须是指定类型的实例;那么这两个有什么区别呢?原来AspectJ在实现代理时有两种方式:
- 如果当前对象引用的类型没有实现自接口时,spring aop使用生成一个基于CGLIB的代理类实现切面编程
- 如果当前对象引用实现了某个接口时,Spring aop使用JDK的动态代理机制来实现切面编程
this指示符就是用来匹配基于CGLIB的代理类,通俗的来讲就是,如果当前要代理的类对象没有实现某个接口的话,则使用this;target指示符用于基于JDK动态代理的代理类,通俗的来讲就是如果当前要代理的目标对象有实现了某个接口的话,则使用target.:
public class FooDao implements BarDao {
...
}
比如在上面这段代码示例中,spring aop将使用jdk的动态代理来实现切面编程,在编写匹配这类型的目标对象的连接点表达式时要使用target指示符, 如下所示:
@Pointcut("target(org.test.dao.BarDao)")
如果 FooDao 类没有实现任何接口,或者在spring aop配置属性:proxyTargetClass设为true时,Spring Aop会使用基于CGLIB的动态字节码技为目标对象生成一个子类将为代理类,这时应该使用this指示器:
@Pointcut("this(org.test.dao.FooDao)")
(6)@target() 限制连接点匹配特点的执行对象,这些对象对应的类要具备指定类型的注解
这个指示器匹配指定连接点,这个连接点所属的目标对象的类有一个指定的注解:
语法:
@target(annotation-type)
- annotation-type:注解类型,全路径的
示例:
@Pointcut("@target(org.springframework.stereotype.Repository)")
(7)within() 限制连接点匹配特点的执行对象,这些对象对应的类要具备指定类型的注解
within 表达式的粒度为类,其参数为全路径的类名(可使用通配符),表示匹配当前表达式的所有类都将被当前方法环绕。如下是 within 表达式的语法:
语法:
within(declaring-type-pattern)
示例:
within 表达式只能指定到类级别,如下示例表示匹配 com.spring.service.BusinessObject 中的所有方法:
within(com.spring.service.BusinessObject)
within 表达式路径和类名都可以使用通配符进行匹配,比如如下表达式将匹配 com.spring.service 包下的所有类,不包括子包中的类:
within(com.spring.service.*)
如下表达式表示匹配 com.spring.service 包及子包下的所有类:
within(com.spring.service..*)
(8)@within()限制连接点匹配指定注解所标注的类型(当使用 Spring AOP 时,方法定义在由指定的注解所标注的类里)
@within表示匹配带有指定注解的类
语法:
@within(annotation-type)
- annotation-type:注解类型,全路径的
示例:
如下所示示例表示匹配使用 org.springframework.stereotype.Repository 注解标注的类:
@Pointcut("@within(org.springframework.stereotype.Repository)")
(9)@annotation() 限制匹配带有指定注解连接点
指定注解标注的方法
语法:
@annotation(annotation-type)
- annotation-type:注解类型,全路径的
示例:
@Pointcut("@annotation(com.test.annotations.LogAuto)")
版权归原作者 Sebastian Wang 所有, 如有侵权,请联系我们删除。