1. AOP概念
1.1 JoinPoint连接点:程序执行中的特定点,如方法执行,调用构造函数或字段复制等,面向切面编程,JoinPoint就是要被切入的对象。
1.2 Advice通知:在一个连接点中,切面采取的行动,针对切入点,要做的事情。
1.3 Pointcut切点:一个匹配连接点的正则表达式。每个任何连接点匹配一个切入点时,就执行与该切入点相关联的指定通知。关注哪些被切入点可以切入。
1.4 Aspect切面(Advisor):一个分布在应用程序中多个位置的标准代码/功能,通常与实际的业务逻辑(例事务管理)不同。每个切面都侧重于一个特定的横切功能。
1.5 Weaving织入:链接切面和目标对象来创建一个通知对象的过程。
1.6 Advice通知 + Pointcut切点形成了切面Aspect/Advisor
1.7 JoinPoint与Pointcut区别:在Spring AOP中,所有方法执行都是JoinPoint。Pointcut是描述信息,修饰的是JoinPoint,通过Pointcut就可以确定哪些JoinPoint可以织入Advice,JointPoint与Pointcut本质上两个不同的纬度。Advice是在JointPoint执行,Pointcut确定了哪些JoinPoint执行Advice。
2. AOP实现
2.1 织入节点
编译期间:切面在目标类编译时被织入,需要引入独立的编译器。
编译后:增强已经编译出来的类,如我们要增强依赖的 jar 包中的某个类的某个方法。
类加载期:在 JVM 进行类加载的时候进行织入。
运行期:切面在应用运行的某个时期被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象。
2.2 实现框架
AspectJ:AspectJ 是一个采用Java 实现的AOP框架,它能够对代码进行编译(一般在编译期进行),让代码具有AspectJ 的 AOP 功能,AspectJ 是目前实现 AOP 框架中最成熟,功能最丰富的语言。ApectJ 主要采用的是编译期静态织入的方式。
AspectWerkz:基于Java的简单、动态、轻量级、强大的AOP框架。可以在运行时或编译时轻松的改造任何(旧)应用程序或除了rt.jar以外的外部类库。
JBoss AOP:JBoss 4.0带了一个AOP框架。这个框架和JBoss应用服务器紧密地结合,但是也能够在应用中单独运行它。
Spring AOP:Spring AOP 是通过动态代理技术实现的,而动态代理是基于反射设计的。Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理
2.3 AspectJ 与 Spring AOP比较
3. AspectJ实现
AspectJ是提供了完整的AOP方案,采用静态织入,不依赖Spring AOP。
3.1 定义切面
针对test()方法进行拦截,方法执行前打印"before interceptor"
@Aspect
public class AspectjInterceptor {
@Pointcut("execution( * com.hobbit.aspectj.Hello.test())")
public void execute(){
}
@Before("execute()")
public void beforeLog(){
System.out.println("before interceptor");
}
}
3.2 主任务
@Component
public class Hello {
public void test(){
System.out.println("hello aspectj");
}
}
3.3 设置编译器
Java Compiler -> User compiler 选择Ajc,同时设置aspectjtools.jar,见下图
3.4 运行结果
3.5 反编译文件
4. Spring AOP创建代理
spring aop既可以使用AspectJ注解实现拦截,也可以不依赖AspectJ,使用Spring advice + pointcut,实现拦截注入。
tip: spring aop 使用AspectJ注解时,拦截实现方式是?
4.1 使用AspectJ注解
4.1.1 定义元注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
Stringvalue();
}
4.1.2 定义切面
@Aspect
@Component
public class LogInterceptor {
@Pointcut("@annotation(com.hobbit.interceptor.LogAnnotation)")
public void annotationPointCut(){
}
@Before("annotationPointCut()")
public void beforeLog(JoinPoint joinPoint){
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
LogAnnotation logAnnotation = methodSignature.getMethod().getAnnotation(LogAnnotation.class);
if(Objects.nonNull(logAnnotation)){
System.out.println("beforeLog : " + logAnnotation.value());
}
}
}
4.1.3 织入
@Service
public class UserService {
@Autowired
private UserMapperuserMapper;
@LogAnnotation("queryUser")
public UserEntityqueryUser(int id){
return userMapper.getById(id);
}
}
tip:使用Aspect切面时,底层使用AspectJ实现的静态织入,还是有Spring AOP实现的动态织入?
4.2 Spring AOP实现
Spring AOP实现了完整的方案,通过定义advice + pointcut 实现对象动态织入。
4.2.1 定义Advice
public class LogBeforeAdviceimplements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)throws Throwable{
System.out.println("before advice for class:" + target.getClass() +" and method:" + method.getName());
}
}
4.2.2 配置PointCut及切面
4.2.3 通过ProxyFactoryBean创建代理
4.3 创建代理时序图
Spring的代理对象通过Bean的后置处理器,在createBean对象时封装的。创建的代理的时序图如下
5. Spring AOP生效
5.1 Spring 创建代理的节点
Spring 通过org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization实现AspectJ注解代理对象的创建,具体在org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization实现代理对象的创建
代理对象创建前
创建代理对象
5.2 Spring AOP拦截
Spring AOP通过把Advice转化成MethodIntercetor,通过递归调用的方式实现了Advice的织入,完成advice的调用后,调用目标方法完成切入。
目标对象:ReflectiveMethodInvocation,在JdkDynamicAopProxy invoke方法中创建,是JoinPiont的一个实现。
拦截对象:MethodInterceptor.invoke(MethodInvocation invocation)包括了要拦截的对象。实现不同形式的拦截
事务拦截TransactionInterceptor
tip:先调用拦截链,如何时间后置拦截?Interceptor异常是否会导致事务切面回滚?
5.3 总结
Spring的AOP实现并不依赖于AspectJ任何类,它自己实现了一套AOP的。比如它Spring自己提供的BeforeAdvice和AfterAdvice都是对AOP联盟规范的标准实现。以及Spring自己抽象出来的对Advice的包装:org.springframework.aop.Advisor贯穿Spring AOP的始终。