前言
在上一篇我们分析了AOP生成代理对象的源码流程,我们知道,springaop中,生成代理对象一般有2种,当目标对象实现了接口时,默认使用JDK代理,否则就采用cglib代理,而且,最终调用目标对象的方法的时候,我们发现了是代理对象调用的,如下图所示
于是就好奇了,spring到底是怎么完成了代理过程的呢?即这个代理对象是通过什么样的方式完成的呢?由于本篇研究JDK动态代理,下面,我们先通过一段调试用的代码分析一下,需要通过实现接口的方式来完成,代码直接贴出来
1、提供一个接口
public interface Caculate {
int add(int numA, int numB);
}
2、接口实现类
@Component
public class ConggeCaculate implements Caculate {
@Override
public int add(int numA, int numB) {
System.out.println("执行目标方法 add");
//System.out.println(1/0);
return numA + numB;
}
}
3、全局bean扫描配置类
@ComponentScan(basePackages = "com.congge.v3")
@Component
@EnableAspectJAutoProxy
public class MainConfig {
}
4、AOP配置类
@Aspect
@Component
public class ConggeLogAspect {
@Pointcut("execution(* com.congge.v3.ConggeCaculate.*(..))")
public void pointCut(){
}
@Before(value = "pointCut()")
public void methodBefore(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("执行目标方法前置通知:" + methodName);
}
@After(value = "pointCut()")
public void methodAfter(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("执行目标方法后置通知:" + methodName);
}
@AfterReturning(value = "pointCut()",returning = "result")
public void methodReturning(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("执行目标方法返回通知:" + methodName+",返回结果:"+ result);
}
@AfterThrowing(value = "pointCut()")
public void methodThrowing(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("执行目标方法异常通知:" + methodName);
}
}
5、测试方法
public class ConggeMainClass {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Caculate caculate = (Caculate)context.getBean("conggeCaculate");
System.out.println(caculate.add(1,2));
}
}
通过上一篇的执行流程分析,我们知道最终生成代理对象的位置在下面这个方法中,该方法通过循环查找合适的后置处理器,最终生成代理对象,
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
//获取我们容器中所有的bean 的后置处理器
Object result = existingBean;
//AOP和事务都会在此处生成代理对象
//AOP的注解@EnableAspectJAutoProxy为我们的容器导入了AnnotationAwareAspectJAutoProxyCreator
//而通过调用关系链,可知最终实现了BeanPostProcessor接口
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
于是我们的疑问最终就定位到这里了,为什么通过回调某个后置处理器就完成了代理对象的生成呢?
我们知道,在本例中,实现AOP一个很重要的注解是在某个类中加了一个@EnableAspectJAutoProxy,也就是说加了这个注解之后,目标对象通过spring一系列的过程变成了代理对象,那就是说,解决问题的关键点就在这个注解中了
点到这个注解中去,要重点关注一下图中圈起来的@Import注解,这个注解的作用就是为容器添加一个bean,这就很厉害了,也就是说通过这个注解就把AspectJAutoProxyRegistrar这个类以bean的形式注册到容器中来了,这么说我们就把焦点集中到AspectJAutoProxyRegistrar这个类里面去
来到AspectJAutoProxyRegistrar这个类,该类实现了一个ImportBeanDefinitionRegistrar的接口,这个接口是spring内置提供的一个可以为容器注入bean的一种方式,即只要你的类实现了这个接口,那么spring的IOC容器启动的时候就会扫描到这个类,相当于是一种扩展bean的实现方式
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
/**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
举例说明,我们有一个Hello的类,
public class Hello {
public Hello(){
System.out.println("Hello class");
}
}
再写一个类实现ImportBeanDefinitionRegistrar接口,
public class BeanRegistry implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(Hello.class);
registry.registerBeanDefinition("hello",beanDefinition);
}
}
最后再在mainConfig中通过@Import的注解方式导入进去
@ComponentScan(basePackages = "com.congge.v3")
@Component
@EnableAspectJAutoProxy
@Import(BeanRegistry.class)
public class MainConfig {
}
通过这种方式,我们再测试一下代码,看看是否能够通过获取bean的方式拿到这个bean呢?通过结果可以看到,hello这个类已经变成了一个bean了,即可以通过上述的方式完成注入到spring容器中的功能
接着上面的代码,我们重点关注这段代码,很明显,通过字面意思也大概能明白意思是注册aspectj注解信息,大概就是看看哪些类上面标注了带有aspectj这样的注解信息,然后解析出来进行后面的处理
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
继续进入这个方法吧,我们最终来到下面的这段代码中
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
说白了,通过这个方法,最终可以读取到AOP配置类的元信息并将和后面的生成的代理对象做关联,而AnnotationAwareAspectJAutoProxyCreator这个类是不是就可以理解成我们上面的Hello这个类呢?继续进入这个registerOrEscalateApcAsRequired方法,我们注意到其中的二行代码
看到这里,是不是觉得就和上面注册Hello的过程很像呢?其实就是这么回事,最终通过传入的AspectJAwareAdvisorAutoProxyCreator然后在容器中注册一个bean了,就是说通过@EnableAspectJAutoProxy这个注解,最终为容器创建了一个AspectJAwareAdvisorAutoProxyCreator的bean
下面我们把研究的点放到AspectJAwareAdvisorAutoProxyCreator这个类上面,进入这个类,我们发现,这个类继承了AbstractAdvisorAutoProxyCreator这个类
调出该类的类继承关系图谱,我们惊奇的发现,这个类的顶级接口,竟然是BeanPostProcessor,这就回到了本文最开始的那一段代码
即最终代理对象的生成,是通过bean的后置处理器中完成,按照上一篇创建代理对象的流程,我们通过循环后置处理器找到合适的后置处理器创建代理对象,最终来到AbstractAutoProxyCreator这个类的下面的方法
进入下面的方法
通过断点继续进入下面这个方法
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//循环找出Aspect相关的信息并封装成一个advisor
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
进入这个buildAspectJAdvisors方法,该方法是从容器中获取切面的信息并保存到缓存中,我们通过断点发现,在aspectBeanNames这个集合在中已经保存了bean的名称信息,但问题是,在哪一步,切面信息被解析出来了并放到缓存的集合中了,我们通过断点的时候发现,当前这个方法进入了两次,不妨直接将断点断到此处看一下,
第一次,这个集合是空的,根据左边的调用栈,发现竟然是在创建bean的过程中,通过resolveBeforeInstantiation这个方法进来的,那也就是说,第一次走到这里的时候,会把带有AOP注解的类信息解析出来,然后放进上述的集合中,第二次生成代理对象的时候,就把缓存的相关信息取出来使用即可,只是走了不同的逻辑
通过断点我们看到,第一次解析到AOP的信息的时候,会通过beanName和class信息构建出注解的元信息,然后将beanName和对应的AOP通知信息放到缓存中,供后面使用
到了这里我们有个疑问,AOP的信息是在哪里解析的呢?我们知道,在aop的方法注解中,有@Before,@After,@AfterReturning等注解信息,在spring最终产生代理对象之后,调用代理对象的方法的时候,最终标注了相应注解的方法,可以获取到不同的目标方法的参数、结果等信息,那么这些注解是怎么解析的?以及在哪里解析的呢?
继续上面的步骤,按照官方的定义带有@Before、@After等这样注解的方法称之为通知,在Spring解析到AOP的类的时候,就会解析这些通知类,然后包装成一个个不同的通知对象,接着断点继续往下走,我们来到buildAspectJAdvisors方法中的下面这段代码
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
进入getAdvisors这个方法,该方法用于获取切面通知
实例化通知对象,有了方法名称和方法的元信息,aspectName以及aspectInstanceFactory工厂,就可以通过下面这个方法构建出一个个通知对象
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
//找到并构建方法上的切点表达式
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
//实例化通知对象
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
最关键的是最后一段代码,即这个new InstantiationModelAwarePointcutAdvisorImpl 这个对象,传入相关的参数就可以实现,我们进入这个方法,看一下到底是怎么创建出来的,我们最终来到下面的这个getAdvice方法,在这个方法中,根据解析到的注解,判断标注在方法上的注解类型,构建不同的通知对象
通知对象创建完毕并放进集合中以后,继续往下执行,返回到wrapIfNecessary方法中,走到下面的这个地方,通过getAdvicesAndAdvisorsForBean这个方法判断是否能够拿到通知信息
其实就是上一步解析得到的各类通知对象,
如果判断不为空,就通过createProxy具体创建代理对象,下面就具体来看创建代理对象逻辑,
最终走到下面的方法创建代理对象
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
由于本例中我们的被代理的类是实现了接口的形式,因此最终会通过JDK动态代理的方式创建一个代理对象
到这里,我们通过源码简单调试了一下AOP创建代理对象的完整的流程,下面是整个调试过程中主要走过的代码块的调用链路,可酌情参考