一般我们在开发springboot项目的时候通过在类上面标注@Aspect这个注释就可以实现一个AOP的功能,那这背后的实现原理是什么呢?
首先我们来写一个小样例
1、新建一个Aspect拦截类
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(* y.com.testapp.*.*(..))")
public void point(){}
@Before("point()")
public void before(){
System.out.println("before");
}
@After("point()")
public void after(){
System.out.println("after");
}
}
2、新建一个普通类用来测试是否能进行拦截
@Component
public class C {
public void sayHello(){
System.out.println("hello");
}
}
3、编写测试代码
@SpringBootApplication
public class TestappApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(TestappApplication.class, args);
C c=context.getBean(C.class);
c.sayHello();
}
}
执行查看结果
可以看到确实是在调用c.sayHello()方法过程中调用了拦截器
进一步分析看一下我们的这个c对象为什么在调用sayHello()的时候能够触发拦截器的拦截方法呢?首先看一下c这个对象的状态是什么样的
debug一下可以看到c实际上是一个代理对象,而且还是通过CGLib动态代理生成的。
接着执行会进入到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept()方法里面来,而DynamicAdvisedInterceptor也就是实现了CGLib代理的MethodInterceptor这个接口,对CGBlib有了解的就知道这个接口的intercept()方法就是动态代理对象在调用方法的时候会进入到的一个拦截方法,这个方法和Spring AOP的@befor、@after、@around等方法还有点像呢。
这个方法里面主要有这么一个很重要的实现过程
创建一个CglibMethodInvocation对象并调用其proceed()方法,这个方法内容如下
public Object proceed() throws Throwable { try { return super.proceed(); } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) { throw ex; } else { throw new UndeclaredThrowableException(ex); } } }
主要就是调用父类的proceed()方法,再看一下这个类的父类是什么
也就是说上面的方法会进入到org.springframework.aop.framework.ReflectiveMethodInvocation#proceed()这个方法中去,看一下这个方法的内容
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
上面的代码可以分成三块来看
第一块
这个地方的代码就是目标对象的实际执行的地方,也就是c.sayHello()的实际执行的调用的地方
第二块
这一块是一个递归调用
第三块
这一块就是我们的@before、@around、@after等注解注释的方法的执行调用的地方,这里@before、@around、@after等注解的方法都会被封装到不同的MethodInterceptor子类对象中去,也就是说MethodInterceptor子类对象里面会记录这些注解对应的方法的元数据信息,当调用MethodInterceptor#invoke的时候会根据这些元数据信息通过反射的方式调用实际对应的方法,也就是我们上面创建的MyAspect这个类的before()、after()这些方法。
总结:Spring AOP的底层简单来讲就是看bean容器里面有没有@Aspect标注的bean,如果有则看它的PointCut对应的规则,只要在创建bean的时候符合这个PointCut规则的,就用CBLib动态代理的方式创建代理对象作为bean放到容器中,当我们从bean容器中获取代理对象bean并调用它的方法的时候,因为这个bean是通过代理的方式创建的,所以必然会走org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept()方法,而这个方法也必然会执行org.springframework.aop.framework.ReflectiveMethodInvocation#proceed()这个方法,而这个方法就会根据上面说的执行过程依次执行不同的MethodInterceptor子类对象的invoke()方法,这个方法会根据元数据信息通过反射的方式调用代理对象对应的真正的对象的方法,例如我上面创建的MyAspect这个类的before()、after()这些方法。