注:本系列源码剖析基于spring 5.2.2.RELEASE,本文的剖析基于 annotation 注解办法,gitee仓库链接:gitee.com/funcy/sprin….

前面的文章剖析了spring的发动流程,从本文开端,咱们来剖析spring aop功用。

1. 示例demo

1.1 预备demo

aop的示例demo依旧坐落spring-learn模块,包名是org.springframework.learn.demo02,目录结构如下:

spring aop(一):aop 示例 demo 及 @EnableAspectJAutoProxy

  1. 预备 annotation 首要,咱们预备一个annotation用来标注切面
package org.springframework.learn.demo02;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AopAnnotation {
}
  1. 编写切面办法
package org.springframework.learn.demo02;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
 * {这儿增加描绘}
 *
 * @author chengyan
 * @date 2020-07-19 6:00 下午
 */
@Aspect
@Component
public class AopAspectj {
    @Pointcut("@annotation(org.springframework.learn.demo02.AopAnnotation)")
    public void testAop(){
    }
    @Around("testAop()")
    public Object around(ProceedingJoinPoint p){
        System.out.println("before execute...");
        Object o = null;
        try {
            o = p.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("after execute...");
        return o;
    }
}

这个类表明,标注了 @AopAnnotation 的办法,将运转其运转前后运转一些代码,around(ProceedingJoinPoint)指定了运转前后的代码。

  1. 启用proxy
package org.springframework.learn.demo02;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@EnableAspectJAutoProxy
@Component
public class AopAnnotationConfig {
}

这个类要害在于上面的两个注解:@EnableAspectJAutoProxy@Component,前者表明启用署理,后者表明该类为spring bean.

  1. 预备三个类
  • 承继了接口且标有注解AopAnnotation的类AopBean1
  • 未承继接口但标有注解AopAnnotation的类AopBean2
  • 未承继接口也未标有注解AopAnnotation的类AopBean3

这几个类的内容别离如下:

IAopBean.java

package org.springframework.learn.demo02;
public interface IAopBean {
    void hello();
}

AopBean1.java

package org.springframework.learn.demo02;
import org.springframework.stereotype.Component;
@Component
public class AopBean1 implements IAopBean {
    @Override
    @AopAnnotation
    public void hello() {
        System.out.println("完成了接口IAopService的hello()");
    }
}

AopBean2.java

package org.springframework.learn.demo02;
import org.springframework.stereotype.Component;
@Component
public class AopBean2 {
    @AopAnnotation
    public void hello() {
        System.out.println("未完成接口IAopService的hello()");
    }
}

AopBean3.java

package org.springframework.learn.demo02;
import org.springframework.stereotype.Component;
@Component
public class AopBean3 {
    public void hello() {
        System.out.println("未完成接口IAopService、也未进行aop的hello()");
    }
}
  1. 发动类
package org.springframework.learn.demo02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo02Main {
    public static void main(String[] args) {
         ApplicationContext context = new AnnotationConfigApplicationContext(
              "org.springframework.learn.demo02");
         IAopBean obj1 = (IAopBean)context.getBean("aopBean1");
         AopBean2 obj2 = (AopBean2)context.getBean("aopBean2");
         AopBean3 obj3 = (AopBean3)context.getBean("aopBean3");
         System.out.println("obj1:" + obj1.getClass());
         obj1.hello();
         System.out.println("-----------------------");
         System.out.println("obj2:" + obj2.getClass());
         obj2.hello();
         System.out.println("-----------------------");
         System.out.println("obj3:" + obj3.getClass());
         obj3.hello();
    }
}

1.2 运转成果及说明

运转main(String[])办法,成果如下:

obj1:class com.sun.proxy.$Proxy19
before execute...
完成了接口IAopService的hello()
after execute...
-----------------------
obj2:class org.springframework.learn.demo02.AopBean2$$EnhancerBySpringCGLIB$$e068ccef
before execute...
未完成接口IAopService的hello()
after execute...
-----------------------
obj3:class org.springframework.learn.demo02.AopBean3
未完成接口IAopService、也未进行aop的hello()

首要,能够看到,AopBean1AopBean2在履行hello()办法前后均输出了内容,表明已正确完成了aop功用;AopBean3办法履行前后未完成任何内容,因为咱们并未在AopBean3hello()办法上增加@AopAnnotation

接着再来看的class:AopBean1的class是com.sun.proxy.$Proxy19,这是jdk供给的动态署理类;AopBean2的class是org.springframework.learn.demo02.AopBean2$$EnhancerBySpringCGLIB$$e068ccef,从称号上EnhancerBySpringCGLIB能够看出,这个类经过spring cglib类增强的类;AopBean3的class是org.springframework.learn.demo02.AopBean3,因为咱们并未在AopBean3hello()办法上增加@AopAnnotation

对以上现象,总结如下:

  1. spring 敞开了 aop 后,只会对需求署理的类创立署理目标。在上述代码中,经过@EnableAspectJAutoProxy敞开了aop功用,但并不是一切的类都创立了署理目标,只要AopBean1AopBean2创立了署理目标,AopBean3仍是本来的目标,可见署理目标也是“按需”创立的;

  2. 假如类完成了接口,那么spring在创立署理目标时,会选用jdk供给的动态署理功用;假如类未完成接口,spring会运用cglib来创立署理目标。

1.3 强制运用 cglib 创立署理目标

实际上,是否需求选用jdk供给的动态署理功用来创立署理目标,spring也供给了一个装备,该装备坐落@EnableAspectJAutoProxy中:

package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    /**
     * proxyTargetClass属性,默认false,尝试选用JDK动态署理织入增强(假如当前类没有完成接口则仍是会运用CGLIB);
     * 假如设为true,则强制选用CGLIB动态署理织入增强.
     */
    boolean proxyTargetClass() default false;
    boolean exposeProxy() default false;
}

假如咱们需求强制运用cglib创立署理目标,能够这样设置:

package org.springframework.learn.demo02;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
/**
 * {这儿增加描绘}
 *
 * @author chengyan
 * @date 2020-07-19 5:59 下午
 */
// 强制运用cglib创立动态署理
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Component
public class AopAnnotationConfig {
}

运转成果如下:

obj1:class org.springframework.learn.demo02.AopBean1$$EnhancerBySpringCGLIB$$cccd444c
before execute...
完成了接口IAopService的hello()
after execute...
-----------------------
obj2:class org.springframework.learn.demo02.AopBean2$$EnhancerBySpringCGLIB$$e068ccef
before execute...
未完成接口IAopService的hello()
after execute...
-----------------------
obj3:class org.springframework.learn.demo02.AopBean3
未完成接口IAopService、也未进行aop的hello()

能够看到,AopBean1也运用cglib来创立署理目标了。

2. @EnableAspectJAutoProxy

在spring中,@EnableXxx 表明启用某项功用,如@EnableAsync表明启用异步功用,@EnableCaching表明启用缓存功用,这儿的@EnableAspectJAutoProxy表明启用 aspectJ的主动署理。了解@EnableXxx注解功用的同伴们应该知道,EnableXxx类其上的@Import注解才是启用该功用的要害,如@EnableAspectJAutoProxy

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// Import 进来的类才是要害
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
    ...
}

这儿@Import进来的类是 AspectJAutoProxyRegistrar.class,咱们能够看看这个类做了什么。

假如不清楚 @EnableXxx 注解的功用,能够查看 spring探秘之spring是如何完成 @EnableXxx 功用的?

2.1 AspectJAutoProxyRegistrar 功用

AspectJAutoProxyRegistrar内容如下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {    
        //注册一个AOP署理完成的Bean
        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);
            }
        }
    }
}

AspectJAutoProxyRegistrar完成了ImportBeanDefinitionRegistrar接口,在ImportBeanDefinitionRegistrar供给的registerBeanDefinitions中,调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)往spring容器中注册了主动署理完成类。

咱们跟进AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)办法,看看这个类做了什么。一中跟下去,终究履行的办法为AopConfigUtils#registerOrEscalateApcAsRequired

// 主动署理创立类的称号
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
        "org.springframework.aop.config.internalAutoProxyCreator";
// 这儿的 cls 是 AnnotationAwareAspectJAutoProxyCreator.class
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
        Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    //假如已存在这个bean
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        //判别优先级,假如优先级较高则替换原先的bean
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
    //注册AnnotationAwareAspectJAutoProxyCreator到容器中,此类负责基于注解的AOP动态署理完成
    // 这儿的cls是AnnotationAwareAspectJAutoProxyCreator
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

这个办法主要做的事便是往spring容器中注册AnnotationAwareAspectJAutoProxyCreatorbeanDefinition,后续spring在处理bean时,就能实例化该bean了。

2.2 AspectJAutoProxyRegistrar 履行机遇

AspectJAutoProxyRegistrar中只要一个办法:registerBeanDefinitions(...),评论AspectJAutoProxyRegistrar的履行机遇,其实便是评论org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions的履行机遇。

咱们这儿经过打断点调度的办法来探索:

spring aop(一):aop 示例 demo 及 @EnableAspectJAutoProxy

调用栈如下:

|-AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(String...)
 |-AbstractApplicationContext#refresh
  // 注意这一行代码,这儿是ApplicationContext的refresh里的流程了
  |-AbstractApplicationContext#invokeBeanFactoryPostProcessors
   |-PostProcessorRegistrationDelegate
      #invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory, List)
    |-PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
     |-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
      |-ConfigurationClassPostProcessor#processConfigBeanDefinitions
       |-ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
        |-ConfigurationClassBeanDefinitionReader
          #loadBeanDefinitionsForConfigurationClass
         |-ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
          |-ImportBeanDefinitionRegistrar#registerBeanDefinitions(
            AnnotationMetadata, BeanDefinitionRegistry, BeanNameGenerator)
           |-AspectJAutoProxyRegistrar#registerBeanDefinitions(
             AnnotationMetadata, BeanDefinitionRegistry)

能够看到,AspectJAutoProxyRegistrar 履行是在AbstractApplicationContext#refreshinvokeBeanFactoryPostProcessors中履行的。

3. 总结

本文供给了一个aop的示例,一起剖析了@EnableAspectJAutoProxy 注解的作用,剖析就先到这儿了。


本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中不免有过错之处,欢迎指正!原创不易,商业转载请联系作者取得授权,非商业转载请注明出处。

本系列的其他文章

【spring源码剖析】spring源码剖析系列目录