例子 1 在使用 New 的情况下实现 AOP
public class TraceTest {
public static void main(String args[]) {
TraceTest test = new TraceTest();
test.rpcCall();
}
// 虽然 intellij 没有给出提示,但是这个 Trace 还是成功的
@Trace
public void rpcCall() {
System.out.println("call rpc");
}
}
@Aspect
public class TraceAspect {
@Pointcut("@annotation(Trace)")
public void tracePointcutDefinition() {}
@Around("@annotation(Trace) && execution(* *(..))")
public Object aroundTrace(ProceedingJoinPoint pjp) throws Throwable {
Object returnObj = null;
try {
System.out.println("before traced method started...");
returnObj = pjp.proceed();
} catch (Throwable throwable) {
throw throwable;
} finally {
System.out.println("after traced method executed...");
}
return returnObj;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Trace {
}
//before traced method started...
//call rpc
//after traced method executed...
按说使用 Spring AOP 是需要容器来注入的,因为容器会自动的把需要注入的代码放到生成的代理类中,但是从上面的例子我们直接 New 了一个实例,但是这个 new 出来的实例就是有被织入的代码,那么它是如何实现的呢?
AOP 有编译时和运行时注入,上面这个例子在编译时已经注入完毕了,生成的 .class 文件已经是修改以后的了,所以不需要再使用容器,来看一下 pom 配置
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
public void rpcCall();
Code:
0: getstatic #46 // Field ajc$tjp_0:Lorg/aspectj/lang/JoinPoint$StaticPart;
3: aload_0
4: aload_0
5: invokestatic #52 // Method org/aspectj/runtime/reflect/Factory.makeJP:(Lorg/aspectj/lang/JoinPoint$StaticPart;Ljava/lang/Object;Ljava/lang/Object;)Lorg/aspectj/lang/JoinPoint;
8: astore_1
9: aload_0
10: aload_1
11: invokestatic #71 // Method aspectjaop/annotation/TraceAspect.aspectOf:()Laspectjaop/annotation/TraceAspect;
14: aload_1
15: checkcast #59 // class org/aspectj/lang/ProceedingJoinPoint
18: invokestatic #75 // Method rpcCall_aroundBody1$advice:(Laspectjaop/annotation/TraceTest;Lorg/aspectj/lang/JoinPoint;Laspectjaop/annotation/TraceAspect;Lorg/aspectj/lang/ProceedingJoinPoint;)Ljava/lang/Object;
21: pop
22: return
所以,maven aspectj 插件在编译的时候会把代码织入到生成的 .class 文件中,这是 AOP 的一个办法
例子2 Spring AOP
我们要研究的是 Spring AOP
public class Logging {
public void beforeAdvice() {
System.out.println("Going to setup student profile.");
}
public void afterAdvice() {
System.out.println("Student profile has been setup.");
}
public void afterReturningAdvice(Object retVal) {
System.out.println("Returning returning advice ***** :");
}
public void AfterThrowingAdvice(IllegalArgumentException ex) {
System.out.println("There has been an exception: " + ex.toString());
}
}
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void ExceptionRaised() {
System.out.println("this is exception message ");
}
}
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.ExceptionRaised();
}
}
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
第一个例子不需要 aspectjweaver 但是第二个例子却少不了。第一个例子需要 aspectj-maven-plugin 但是第二个例子是不需要的,因为 spring context 会托管注入这一流程,后续的 Spring 章节就是用来分析 Spring AOP 的例子是如何实现的,帮助研究的代码就是例2
下面来分析 AOP 的应用流程。因为例子中被织入代码的类型不是接口,所以动态代理的实现应该是 CGLIB 而不是 JdkDynamicProxy
分析的切入点是函数 getProxy,知道了是 cglib 的 getProxy 函数,就把断点设在这个函数体内,根据函数的返回,能够知道整个调用栈,也可以根据 intellij idea 的 thread stack 直接找到调用栈。
AbstractAutowireCapableBeanFactory.doCreateBean()
AbstractAutowireCapableBeanFactory.initializeBean()
AbstractAutowireCapableBeanFactory.postProcessBeforeInitialization()
AbstractAutowireCapableBeanFactory.postProcessAfterInitialization()
AbstractAdvisorAutoProxyCreator.createProxy
DefaultAopProxyFactory.createAopProxy // 这里选择 JdkDynamicProxy 或者 CGLibProxy
createProxy// 根据具体选取的 AOP proxy 构造代理实例
从上面的代理里,可以明确下面几点问题:
- 对于每一个要生成的 Bean, 都必须在 PostProcessor 时检查它是否被注入了代码。检查的位置在 AbstractAdvisorAutoProxyCreator.findElibleAdvisor对于 Bean 返回 List advisor, 很多 advisor 的实现是 PostProcessor 的子类
- 定义的 Aspect 将被转换成 AspectJPointcutAdvisor,Advisor 分为两部分,第一部分是 Advice 第二部分是 Pointcut,注意 Advice 部分,注意,在 Advisor 接口中只有 advice 而没有 pointcut, 包括 pointcut 的子类型叫做 AspectJPointcutAdvisor。每个注解生成一个 Advisor,Advisor 的 pointcut就是注解的内容,它的 advice 部分就是函数的实现,函数的实现引用方法,比如 LoggingService 中的那些方法就是 advice
- JdkDynamicProxy 是 InvocationHandler 和 AopProxy 的子类,它的成员变量有 AdvisedSupport,所以每一个 JdkDynamicProxy 实例对应一个Bean
- advisor holding advice(action to take at joint point, and a filter to determining the capability of the advice)
- pointcut is compose of a class filter and method matcher
- ReflectiveInterceptor.proceed. 无论是 cglib 还是 jdkDynamic 都是转化成 MethodInterceptor
看代码的时候还是有不理解的地方
- 为什么要有 BeanDefinition? 为什么不能省掉这一层? A BeanDefinition describes a bean instance, whichhas property values, constructor argument values, and further information supplied by concreteimplementations.
其实 Cglib 构造动态代理的方式还是蛮简单,不需要过多分析。它就是找到各种 advisor 然后各种 set enhancer 就行了,但是反观 JdkDynamicProxy 就复杂些,因为JdkDynamicProxy 的构造方法有些复杂。首先,JdkDynamicProxy 需要用到 advisedSupport, 在里面填好 advisor 和 targetClass 然后 JdkDynamicProxy 继承InvocationHandler
原文:大专栏 AOP spring source code analysis