【Spring源码】- 09 扩展点之@Import注解

234 阅读14分钟
原文链接: mp.weixin.qq.com

这节我们主要来分析下@Import注解,其是在 Spring 3.0开始引入,是Spring中非常重要的一个注解,特别在第三方模块和 Spring进行整合场景下使用非常频繁,比如上节分析的mybatis-spring模块实现 mybatisspring整合,就是利用 @Import(MapperScannerRegistrar.class)引入MapperScannerRegistrar这个关键的 BeanDefinitionRegistryPostProcessor扩展类,实现扫描Mapper类,并注册到 IoC中,SpringBootSpringCloud等都有@Import注解大量使用场景。

基本使用

@Import注解定义如下:


                                @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented
                                    public @interface Import { Class<?>[] value();}
                                        

该注解只有一个Class类型属性,指定需要处理的类,主要分为三种类型:普通类、 ImportSelectorImportBeanDefinitionRegistrar

普通类

首先,其可以导入一个普通类,引入普通类是,Spring内部处理时只是把普通类当成一个 Configuration Class,并进行递归方式重新解析该配置类。这里有两种场景:

  • 普通类上没有注解,比如:@Import(TestService.class)TestService类上没有定义任何注解,这种是最简单的情形,Spring会把TestService导入并注册到IoC容器中;
  • @Import普通类时,Spring会把其当成一个配置类进行处理,如果其上面带有注解会继续被解析处理的,比如 @Import导入的Class上含有 @Configuration@ComponentScan,这些注解会被解析处理;

ImportSelector

@Import注解可以引入 ImportSelector类型,ImportSelector是从 Spring 3.1开始引入的接口。如下:可以利用编程方式动态向IoC容器中注册 BeanImportSelector可以读取 annotation的属性来决定要加载哪些Configuration。下面我们看一个 ImportSelector的具体实现:EnableConfigurationPropertiesImportSelector


                                                class EnableConfigurationPropertiesImportSelector 
                                                    implements ImportSelector {    @Override    
                                                        public String[] selectImports(AnnotationMetadata metadata) {              // 获取EnableConfigurationProperties注解的所有的属性值        MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(                EnableConfigurationProperties.class.getName(), 
                                                            false);             // 获取attributes中主键value的第一个值。        Object[] type = attributes == 
                                                                null ? null                : (Object[]) attributes.getFirst(
                                                                    "value");              // 根据条件,返回需要导入的类。        
                                                                        if (type == null || type.length == 0) {            
                                                                            return new String[] {                    ConfigurationPropertiesBindingPostProcessorRegistrar.class                            .getName() };        }        
                                                                                return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(),                ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };    }}
                                                                                    

该类来自于SpringBoot的一个实现,用于绑定外部配置文件中的属性到 Configuration类中。

注意:如果没有返回应该返回空数组return new String[0],而不能返回 null,否则报空指针异常。

下面我们就来使用ImportSelector实现类方式,实现SpringBoot中常见的@Enable***注解方式。

1、自定义一个ImportSelector实现类:


                                                                                            public class 
                                                                                                MyImportSelector implements ImportSelector { 
                                                                                                    @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) {  
                                                                                                        return new String[]{TestService.class.getName()}; }}
                                                                                                            

2、定义一个@Enable***注解,方便使用:


                                                                                                                @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)
                                                                                                                    @Documented@Import(MyImportSelector.class)public 
                                                                                                                        @interface EnableService {}
                                                                                                                            

3、将@Enable***注解添加到配置类上:


                                                                                                                                @Configuration@EnableService
                                                                                                                                    public class 
                                                                                                                                        TestConfiguration {}
                                                                                                                                            

通过上面简单的方式,就可以把ImportSelector#selectImports()方法返回的字符串数组对应的 Class注册到IoC容器中。

ImportBeanDefinitionRegistrar

ImportSelector是利用 selectImports()方法返回字符串数组进行IoC容器注册, ImportBeanDefinitionRegistra则更加灵活,可以获取到IoC容器,利用 registerBeanDefinition()方法直接注入Bean

如下,直接将Service2解析的 BeanDefinition注册到IoC容器中:


                                                                                                                                                            public class 
                                                                                                                                                                MyImportBeanDefinitionRegistrar implements 
                                                                                                                                                                    ImportBeanDefinitionRegistrar {    @Override    
                                                                                                                                                                        public void 
                                                                                                                                                                            registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        RootBeanDefinition beanDefinition = 
                                                                                                                                                                                new RootBeanDefinition(Service2.class);        registry.registerBeanDefinition(
                                                                                                                                                                                    "service2", beanDefinition);    }}
                                                                                                                                                                                        

源码解析

@Import标注的 ClassSpring中会被认为是配置类,配置类主要通过 ConfigurationClassPostProcessor这个类进行解析,所以@Import注解解析处理入口就是这个类。 ConfigurationClassPostProcessor内部会使用ConfigurationClassParser解析器去解析配置类,沿着这个方法一直跟踪下去发现 @Import注解是在processImports()方法中进行处理的:


                                                                                                                                                                                                                if (candidate.isAssignable(ImportSelector.class)) {
                                                                                                                                                                                                                    //方式一 Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,        
                                                                                                                                                                                                                        this.environment, 
                                                                                                                                                                                                                            this.resourceLoader, 
                                                                                                                                                                                                                                this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); 
                                                                                                                                                                                                                                    if (selectorFilter != 
                                                                                                                                                                                                                                        null) {  exclusionFilter = exclusionFilter.or(selectorFilter); } 
                                                                                                                                                                                                                                            if (selector 
                                                                                                                                                                                                                                                instanceof DeferredImportSelector) {  
                                                                                                                                                                                                                                                    this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } 
                                                                                                                                                                                                                                                        else {        
                                                                                                                                                                                                                                                            //这里会调用ImportSelector#selectImports()方法获取到返回数组  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                                                                                                                                                                                                                                                                //1.selectImports返回的是要动态注册的bean名称  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);  
                                                                                                                                                                                                                                                                    //这里递归调用,有为ImportSelector引入的可能不一定是普通类,还可以又是ImportSelector、ImportBeanDefinitionRegistrar,需要继续递归处理        
                                                                                                                                                                                                                                                                        //如果是普通类,则走到方式三处理,ImportBeanDefinitionRegistrar类型则走到方式二处理,ImportSelector则继续递归处理  processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, 
                                                                                                                                                                                                                                                                            false); }}
                                                                                                                                                                                                                                                                                else 
                                                                                                                                                                                                                                                                                    if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                                                                                                                                                                                                                                                                                        //方式二 Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar =  ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,     
                                                                                                                                                                                                                                                                                            this.environment, 
                                                                                                                                                                                                                                                                                                this.resourceLoader, 
                                                                                                                                                                                                                                                                                                    this.registry);    
                                                                                                                                                                                                                                                                                                        /** * 调用configClass.addImportBeanDefinitionRegistrar方法将ImportBeanDefinitionRegistrar实现类存入configClass的成员变量importBeanDefinitionRegistrars中, * 后面的ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中,this.reader.loadBeanDefinitions(configClasses);会调用这些ImportBeanDefinitionRegistrar实* 现类的registerBeanDefinitions方法: */ configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}
                                                                                                                                                                                                                                                                                                            else {
                                                                                                                                                                                                                                                                                                                //方式三 
                                                                                                                                                                                                                                                                                                                    this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                                                                                                                                                                                                                                                                                                                        //3.迭代调用processImport方法后,会直接到这里,configClass是步骤1返回的bean名称}
                                                                                                                                                                                                                                                                                                                            

从上面源码可以看出 @Import引入三种类型分别对应三种处理方式:

  1. 如果非 ImportSelectorImportBeanDefinitionRegistrar实现类,就会被当成普通方式处理,走方式三进入 processConfigurationClass()方法,即被当成配置类进行递归处理;
  2. 如果是ImportSelector类型,走方式一处理,调用 ImportSelector#selectImports()方法获取到返回数组,然后对返回值进行递归处理;
  3. 如果是ImportBeanDefinitionRegistrar类型,则走方式二处理流程,会创建出 ImportBeanDefinitionRegistrar实例然后缓存到configClass中,等待后续 reader.loadBeanDefinitions(configClasses)中进行处理。

reader.loadBeanDefinitions(configClasses)方法中 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars())这句就是用来处理上面解析的ImportBeanDefinitionRegistrar类型逻辑:


                        private void loadBeanDefinitionsFromRegistrars
                            (Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {  registrars.forEach((registrar, metadata) ->    registrar.registerBeanDefinitions(metadata, 
                                this.registry, this.importBeanNameGenerator));}
                                    

就是把IoC容器和 configClass的元信息传入进行回调ImportBeanDefinitionRegistrar#registerBeanDefinitions(AnnotationMetadata, BeanDefinitionRegistry, BeanNameGenerator),这样,你就可以获取到配置类上注解信息,然后根据这些配置向 IoC容器中注入BeanDefinition即可。

案例

上面对@Import注解的基本使用和源码解析逻辑进行了分析,下面我们通过一个案例拓展下你对 @Import注解使用场景的认识。

背景:Spring 3.0 之后分别引入了@EnableAsync@Async这两个注解,可以简单的就把一个普通方法变成异步机制执行,应用中通过@EnableAsync开启异步 @Async注解支持后,然后再方法上添加@Async注解后就变成了异步执行方法:


                                            @Componentpublic class 
                                                AsyncTask {    @Async    public 
                                                    void doTask(){        log.info("Thread {} ", Thread.currentThread().getName());        
                                                        try {            Thread.sleep(3000);        } 
                                                            catch (InterruptedException e) {            e.printStackTrace();        }        log.info(
                                                                "Thread {} ", Thread.currentThread().getName());    }}
                                                                    

通过两个简单的注解方法,就可以把一个同步方法变成异步执行方法,是不是很神奇,下面我们就来通过案例了解下这背后实现的原理。

1、先来定义两个注解,@MyEnableAsync控制功能开关, @MyAsync控制哪些方法需要该功能:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AsyncConfigImportRegistrar.class)public @interface MyEnableAsync {    //proxyTargetClass=true直接对类进行代理,即使用cglib;proxyTargetClass=false则使用jdk代理    boolean proxyTargetClass() default false;    //指定异步执行线程池    String executor() default "defaultExecutor";}
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MyAsync {}

2、@MyEnableAsync注解通过 @Import(AsyncConfigImportRegistrar.class)导入AsyncConfigImportRegistrar,下面来定义下这个类:


                                                                        public class AsyncConfigImportRegistrar 
                                                                            implements ImportBeanDefinitionRegistrar {    @Override    
                                                                                public void registerBeanDefinitions
                                                                                    (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        
                                                                                        /**         * importingClassMetadata封装了配置类元信息,通过getAnnotationAttributes()可以获取到配置类上指定注解属性Map         */        AnnotationAttributes attributes = AnnotationAttributes                .fromMap(importingClassMetadata.getAnnotationAttributes(MyEnableAsync.class.getName()));        Assert.notNull(attributes, 
                                                                                            "@MyEnableAsync attributes is null");        RootBeanDefinition beanDefinition = 
                                                                                                new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);        MutablePropertyValues pvs = beanDefinition.getPropertyValues();        pvs.addPropertyValue(
                                                                                                    "proxyTargetClass", attributes.getBoolean("proxyTargetClass"));        pvs.addPropertyValue(
                                                                                                        "executor", new RuntimeBeanReference(attributes.getString("executor")));        registry.registerBeanDefinition(
                                                                                                            "asyncAnnotationBeanPostProcessor", beanDefinition);    }}
                                                                                                                

这是一个ImportBeanDefinitionRegistrar实现类,该方法中主要注入 AsyncAnnotationBeanPostProcessor,并把注解相关配置通过PropertyValue方式注入进去。

3、接着我们就来定义AsyncAnnotationBeanPostProcessor:


                                                                                                                        public class 
                                                                                                                            AsyncAnnotationBeanPostProcessor extends ProxyConfig 
                                                                                                                                implements BeanPostProcessor {    
                                                                                                                                    private ExecutorService executor;    @Override    
                                                                                                                                        public Object postProcessAfterInitialization
                                                                                                                                            (Object bean, String beanName) {        
                                                                                                                                                //遍历出带有@MyAsync注解方法        MethodIntrospector.MetadataLookup<MyAsync> lookup                = method -> AnnotatedElementUtils.findMergedAnnotation(method, MyAsync.class);        Map<Method, MyAsync> methods = MethodIntrospector.selectMethods(bean.getClass(), lookup);        
                                                                                                                                                    /**         * 如果Bean含有@MyAsync注解方法,则对Bean进行代理,并对
                                                                                                                                                        @MyAsync注解方法进行增强         */        
                                                                                                                                                            if(methods != null && !methods.isEmpty()){            
                                                                                                                                                                //定义一个注解切点,只对@MyAsync注解方法进行切入            AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(MyAsync.class);            
                                                                                                                                                                    //定义一个Advice,封装增强逻辑代码            Advice advice = 
                                                                                                                                                                        new AsyncAnnotationAdvice(executor);            
                                                                                                                                                                            //使用一个Advisor将增强逻辑Advice和切点PointCut封装到一起            DefaultPointcutAdvisor advisor = 
                                                                                                                                                                                new DefaultPointcutAdvisor();            advisor.setPointcut(pointcut);            advisor.setAdvice(advice);            
                                                                                                                                                                                    //创建一个Spring代理工具类            ProxyFactory proxyFactory = 
                                                                                                                                                                                        new ProxyFactory();            
                                                                                                                                                                                            //指定代理目标实例            proxyFactory.setTarget(bean);            
                                                                                                                                                                                                if(!this.isProxyTargetClass()){                
                                                                                                                                                                                                    //使用jdk代理方式                proxyFactory.setInterfaces(bean.getClass().getInterfaces());            }            
                                                                                                                                                                                                        //advisor包含了织入点和织入逻辑,ProxyFactory就会根据这些创建出代理对象            proxyFactory.addAdvisor(advisor);            
                                                                                                                                                                                                            //使用当前类的属性            proxyFactory.copyFrom(
                                                                                                                                                                                                                this);            
                                                                                                                                                                                                                    return proxyFactory.getProxy();        }        
                                                                                                                                                                                                                        return bean;    }    
                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                void setExecutor
                                                                                                                                                                                                                                    (ExecutorService executor) {        
                                                                                                                                                                                                                                        this.executor = executor;    }}
                                                                                                                                                                                                                                            

AsyncAnnotationBeanPostProcessor是一个 BeanPostProcessor扩展点,之前分析过,其在Bean执行init-method前后进行扩展。这里主要利用Bean执行完init初始化方法后进行扩展:

  • 遍历 Bean方法,查找到带有 @MyAsync注解方法;
  • 查找到则对 Bean进行代理,否则返回原 Bean
  • 具体增强逻辑见上述代码注释;

4、步骤3中使用到了 AsyncAnnotationAdvice,就是对具体增强逻辑代码封装:


                                                                                                                                                                                                                                                    public 
                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                            class 
                                                                                                                                                                                                                                                                AsyncAnnotationAdvice 
                                                                                                                                                                                                                                                                    implements 
                                                                                                                                                                                                                                                                        MethodInterceptor {    
                                                                                                                                                                                                                                                                            private ExecutorService executor;    
                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                    public 
                                                                                                                                                                                                                                                                                        AsyncAnnotationAdvice
                                                                                                                                                                                                                                                                                            (ExecutorService executor)
                                                                                                                                                                                                                                                                                                {        
                                                                                                                                                                                                                                                                                                this.executor = executor;    }    
                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                        public Object 
                                                                                                                                                                                                                                                                                                            invoke
                                                                                                                                                                                                                                                                                                                (
                                                                                                                                                                                                                                                                                                                    final MethodInvocation invocation) 
                                                                                                                                                                                                                                                                                                                        throws Throwable {        
                                                                                                                                                                                                                                                                                                                            //对目标方法调用封装到一个Callable实例中        Callable<Object> task = () -> {            
                                                                                                                                                                                                                                                                                                                                try {                Object result = invocation.proceed();                
                                                                                                                                                                                                                                                                                                                                    if (result 
                                                                                                                                                                                                                                                                                                                                        instanceof Future) {                    
                                                                                                                                                                                                                                                                                                                                            return ((Future<?>) result).get();                }            }            
                                                                                                                                                                                                                                                                                                                                                catch (Throwable e) {                e.printStackTrace();            }            
                                                                                                                                                                                                                                                                                                                                                    return 
                                                                                                                                                                                                                                                                                                                                                        null;        };        
                                                                                                                                                                                                                                                                                                                                                            //然后放入到线程池中执行        
                                                                                                                                                                                                                                                                                                                                                                if(executor != 
                                                                                                                                                                                                                                                                                                                                                                    null){            executor.submit(task);        }
                                                                                                                                                                                                                                                                                                                                                                        else{            invocation.proceed();        }        
                                                                                                                                                                                                                                                                                                                                                                            return 
                                                                                                                                                                                                                                                                                                                                                                                null;    }}
                                                                                                                                                                                                                                                                                                                                                                                    

从上面代码可以看出,增强逻辑就是把对目标方法调用封装到一个 Callable实例中,然后放入到一个线程池中执行。所以,这样就可以把一个普通方法调用编程异步方法调用。

上面就把具体实现逻辑都完成了,下面我们来测试是否正常执行。

1、定义 Service接口及其实现类 TestService,定义接口主要是用于测试 jdk动态代理方式:

                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                    interface 
                                                                                                                                                                                                                                                                                                                                                                                                                                        Service {    
                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                    test01
                                                                                                                                                                                                                                                                                                                                                                                                                                                        ()
                                                                                                                                                                                                                                                                                                                                                                                                                                                            ;    
                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                    test02
                                                                                                                                                                                                                                                                                                                                                                                                                                                                        ()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            ;}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @Component
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    class 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        TestService 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            implements 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                Service
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    { 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    //测试异步调用    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @MyAsync    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        test01
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            ()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                {        System.out.println(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "TestService->test01 execute:"+Thread.currentThread().getName());    } 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    //没有注解,还是执行同步调用    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    test02
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        ()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            {        System.out.println(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "TestService->test02 execute:"+Thread.currentThread().getName());    }}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                

2、定义一个启动配置类:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                @Configuration
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @ComponentScan(basePackageClasses = TestConfiguration.class)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        @MyEnableAsync(proxyTargetClass = 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            true)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        class 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            TestConfiguration {    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                @Bean    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        public ExecutorService 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            defaultExecutor
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ()
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    {        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    final ThreadFactory threadFactory = 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        new ThreadFactoryBuilder()                .setNameFormat(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            "Async-executor-%d")                .setDaemon(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                true)                .build();        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    return Executors.newFixedThreadPool(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        5, threadFactory);    }}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

在启动配置类中使用 @MyEnableAsync(proxyTargetClass = true)开启功能, proxyTargetClass=true表示使用 cglib代理方式,默认 false即使用 jdk动态代理方式。 @MyEnableAsync注解另一个属性 executor可以用于指定使用哪个线程池,默认使用 defaultExecutor

3、定义测试类:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        class 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            AsyncTest {    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                @Test    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        public 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            void 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                test01
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    () 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        throws ExecutionException, InterruptedException {        AnnotationConfigApplicationContext context                = 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            new AnnotationConfigApplicationContext(TestConfiguration.class);        Service testService = context.getBean(Service.class);        System.out.println(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                "TestService Class:"+testService.getClass());        System.out.println(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    "当前测试线程:"+Thread.currentThread().getName());        testService.test01();        testService.test02();        TimeUnit.SECONDS.sleep(
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        2);    }}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

输出结果:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        TestService
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            Class:class
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            async.TestService?EnhancerBySpringCGLIB?26022d2a当前测试线程:mainTestService->test02
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            execute:mainTestService->test01
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            execute:Async-executor-0
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            

从输入结果可以看到, test01()方法使用线程池异步调用,而 test02()方法没有标注 @MyAsync注解,依然使用同步调用方式执行。

总结

@Import注解是 Spring中非常重要的一个扩展点,在 SpringBootSpringCloud中有大量的应用,并通过源码分析了 @Import注解在 Spring中的处理机制。

在分析过程中引入了两个案例,第一个案例了解了 SpringBoot中大量使用的 @EnableXXX注解的一个基本实现逻辑;第二个案例: @Import注解、 IoC容器扩展点、 AOP等技术融合大致了解下 Spring@EnableAsync@Async注解运行背后的原理。

长按识别关注, 持续输出原创