SpringAOP
创建增强
- Spring使用增强类定义横切逻辑,同时由于Spring只支持方法连接点,增强还包括了在方法的哪一点加入横切代码的方位信息,所以增强既包含横切逻辑,还包含部分连接点的信息。
增强类型 - AOP联盟为增强定义了org.aopalliance.aop.Advice接口,Spring支持5种类型的增强**(通知)**
前置增强: org.springframework.aop.BeforeAdvice代表前置增强,因为Spring只支持方法级的增强,所以MethodBeforeAdvice是目前可用的前置增强,表示在目标方法执行前实施增强,而BeforeAdvice是为了将来版本扩展需要而定;
后置增强: org.springframework.aop.AfterReturningAdvice代表后增强,表示在目标方法执行后实施增强;
环绕增强:org.aopalliance.intercept.MethodInterceptor代表环绕增强,表示在目标方法执行前后实施增强;
异常抛出增强: org.springframework.aop.ThrowsAdvice代表抛出异常增强,表示在目标方法抛出异常后实施增强;
public void afterThrowing(Method method, Object[] args, Object target, Exception e) throws Throwable {
System.out.println("method=" + method.getName());
System.out.println("抛出异常:" + e.getMessage());
System.out.println("成功回滚事务");
}
案例:
创建切面
-
我们可能注意到一个问题:增强被织入到目标类的所有方法中,假设我们希望有选择地织入到目标类某些特定的方法中,就需要使用切点进行目标连接点的定位了。描述连接点是进行AOP编程最主要的工作
-
增强提供了连接点方位信息:如织入到方法前面、后面等,而切点进一步描述织入到哪些类的哪些方法上。
-
Spring通过org.springframework.aop.Pointcut接口描述切点,Pointcut由ClassFilter和MethodMatcher构成,它通过ClassFilter定位到某些特定类上,通过MethodMatcher定位到某些特定方法上,这样Pointcut就拥有了描述某些类的某些特定方法的能力。
-
Spring使用org.springframework.aop.Advisor接口表示切面的概念,一个切面同时包含横切代码和连接点信息
-
从广义上说,增强其实就是一种最简单的切面,它既包括横切代码也包括切点信息,只不过它的切点只是简单的方法相对位置的信息。所以增强一般需要和切点联合才可以表示一个更具实用性的切面。
什么是切面???
包含两个关键信息(切点+增强)
接下来说说 AspectJ
- Spring AOP,它包括基于XML配置的AOP和基于@AspcetJ注解的AOP,这两种方法虽然在配置切面时的表现方式不同,但底层都是采用动态代理技术(JDK代理或CGLib代理)。Spring可以集成AspectJ,但AspectJ本身并不属于Spring AOP的范畴。
- Spring在处理@Aspect注解表达式时,需要将Spring的asm模块添加到类路径中。asm是轻量级的字节码处理框架,因为Java的反射机制无法获取入参名,Spring就利用asm处理@AspectJ中所描述的方法入参名。
- 也可以通过配置的方式来生成代理对象
工作原理: - 首先,在配置文件中引入aop命名空间,然后通过aop命名空间的aop:aspectj-autoproxy/自动为Spring容器中那些匹配@AspectJ切面的Bean创建代理,完成切面织入。
AspectJ-基础语法
@Before
前置增强,相当于BeforeAdvice的功能
@AfterReturning
后置增强,相当于AfterReturningAdvice
@Around
环绕增强,相当于MethodInterceptor
@AfterThrowing
抛出增强,相当于ThrowsAdvice
@After
Final增强,不管是抛出异常或者是正常退出,该增强都会得到执行,该增强没有对应的增强接口,可以把它看成ThrowsAdvice和AfterReturningAdvice的混合物,一般用于释放资源,相当于try{}finally{}
环绕增强
ProceedingJoinPoint
proceed
常见案例:
execution(public * *(..))
匹配所有目标类的public方法,第一个*代表返回类型;第二个*代表方法名;而..代表任意入参的方法;
execution(* *To(..))
匹配目标类所有以To为后缀的方法。第一个*代表返回类型;而*To代表任意以To为后缀的方法。
execution(* com.dream.IUserDao.*(..))
匹配IUserDao接口的所有方法,第一个*代表返回任意类型;com.dream.IUserDao.*代表IUserDao接口中的所有方法
execution(*com.dream.*(..))
匹配com. dream包下所有类的所有方法;
最后说一下基于Schema配置切面:
简单来说,就是换一种方式来实现上述的效果:我们来配置一个基于Schema的切面,它使用了aop的命名空间