AOP目的:
面向切面编程(aspect-oriented programming,AOP)主要实现的目的是针对业务处理过程中的切面进行提取,诸如日志、事务管理和安全这样的系统服务,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring AOP术语:
- 连接点(Joinpoint)
程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入通知。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。连接点是在应用执行过程中能够插入切面的一个点。
- 切点(Pointcut)
AOP通过“切点”定位特定的连接点。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
- 通知(Advice)
切面的工作被称为通知。是织入到目标类连接点上的一段程序代码。
Spring切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方 法的输出是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调 用之前和调用之后执行自定义的行为。
-
引介(Introduction)
引入允许我们向现有的类添加新方法或属性,是一种特殊的通知。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。 -
切面(Aspect)
切面由切点和通知(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义。
- 织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。
AOP有三种织入的方式:
- 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
-
类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增
强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。 - 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring
AOP就是以这种方式织入切面的。
package com.wanyu.configuration;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AdviceTest {
@Pointcut("execution(public * com.wanyu.fams.controller.*.*(..))")
public void pointcut() {
// 仅用于定义 Pointcut
}
/**
* 可以在执行方法之前和之后改变参数和返回值
* @param joinPoint用于获取目标方法相关信息的参数
* @return 最终的返回值
* @throws Throwable
*/
@Around("pointcut()")
public Object processTx(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around增强:执行方法之前,模拟开始事物");
Object[] args = joinPoint.getArgs();
if(args != null && args.length > 0 && args[0].getClass() == String.class) {
args[0] = "增加的前缀" + args[0];
}
Object rvt = joinPoint.proceed();
System.out.println("Around增强:执行方法之后,模拟结束事物");
if(rvt != null && rvt instanceof Integer) {
rvt = (Integer)rvt * (Integer)rvt;
}
return rvt;
}
/**
* 可以在执行方法之前对目标方法的参数进行判断
* 通过抛出一个异常来阻断目标方法的访问
* @param joinPoint
*/
@Before("pointcut()")
public void authority(JoinPoint joinPoint) {
System.out.println("Before增强:模拟权限检查");
System.out.println("Before增强:被织入增强处理的目标目标方法为:" + joinPoint.getSignature().getName());
System.out.println("Before增强:目标方法的参数为:" + Arrays.toString(joinPoint.getArgs()));
joinPoint.getArgs()[0] = "除了Around其他的都是是不可以修改目标方法的参数的";
System.out.println("joinPoint.getArgs()[0]:"+joinPoint.getArgs()[0]);
System.out.println("Before增强:目标方法的参数为:" + Arrays.toString(joinPoint.getArgs()));
System.out.println("Before增强:被织入增强处理的目标对象为:" + joinPoint.getTarget());
}
/**
* 可以在执行方法之后对目标方法的参数进行判断
* @param joinPoint
*/
@After("pointcut()")
public void release(JoinPoint joinPoint) {
System.out.println("After增强:模拟方法结束后的释放资源");
System.out.println("After增强:被织入增强处理的目标方法为:" + joinPoint.getSignature().getName());
System.out.println("After增强:目标方法的参数为:" + Arrays.toString(joinPoint.getArgs()));
System.out.println("After增强:被织入增强处理的目标对象为" + joinPoint.getTarget());
}
/**
* 与After的区别在于AfterReturning只有在方法执行成功的之后才会被织入,如果After和
* AfterReturning同时存在于一个文件中,谁写在前面谁先运行
* @param joinPoint
* @param rvt方法的返回值
*/
@AfterReturning(pointcut="execution(public * com.wanyu.fams..*.*(..))", returning="rvt")
public void log(JoinPoint joinPoint, Object rvt) {
System.out.println("AfterReturning增强:获取目标方法的返回值:" + rvt);
System.out.println("AfterReturning增强:模拟日志功能");
System.out.println("AfterReturning增强:获织入增强的目标方法为:" + joinPoint.getSignature().getName());
System.out.println("AfterReturning增强:目标方法的参数为:" + Arrays.toString(joinPoint.getArgs()));
System.out.println("AfterReturning增强:被织入增强处理的目标对象为:" + joinPoint.getTarget());
}
}
springAOP切入点表达式怎么排除某些方法
@Pointcut("!execution(* aa.bb..*.set*(..)) and execution(* aa.bb..*.*(..)) ")
如果是这种形式的法执行了很多其他方法 比如init
可以 把 and 换成 && 就可以解决了。
@Pointcut("execution(* aa.bb..*.*(..)) and !execution(* aa.bb..*.set*(..)) ")
这种形式还是都执行了
网友评论