• 007-Spring Boot-@Enable*注解的工作原理-EnableConfigurationProperties、ImportSelector、ImportBeanDefinitionRegistrar


    一、@Enable* 启用某个特性的注解

    1、EnableConfigurationProperties

    回顾属性装配

    application.properties中添加

    tomcat.host=192.168.2.1
    tomcat.port=8080

    增加属性类TomcatProperties

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @Component
    @ConfigurationProperties(prefix = "tomcat")
    public class TomcatProperties {
        private String host;
        private String port;
    
        public String getHost() {
            return host;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        public String getPort() {
            return port;
        }
    
        public void setPort(String port) {
            this.port = port;
        }
    
        @Override
        public String toString() {
            return "TomcatProperties [host=" + host + ", port=" + port + "]";
        }
    
    }
    View Code

    在App中添加代码

    @SpringBootApplication
    public class App {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
            System.out.println(context.getBean(TomcatProperties.class));
            context.close();
        }
    }

    查看注解SpringBootApplication,其中使其起作用的是@EnableAutoConfiguration,继续追踪代码,其实生效的是:@EnableConfigurationProperties

    故以上代码配置

    @ComponentScan
    @EnableConfigurationProperties
    public class App {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
            System.out.println(context.getBean(TomcatProperties.class));
            context.close();
        }
    }

    注解:是用来启用一个特性的,特性可以把配置文件的属性注入到Bean类中,一般和@ConfigurationProperties一起使用

    2、EnableAsync

    增加一个异步打印类

    package com.lhx.spring.springboot_enable;
    
    import java.util.concurrent.TimeUnit;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Jeep implements Runnable {
    
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    System.out.println("---------------"+i);
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }
    View Code

    在App中调用

    @ComponentScan
    @EnableConfigurationProperties
    public class App {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
            System.out.println(context.getBean(TomcatProperties.class));
            context.getBean(Runnable.class).run();
            System.out.println("------end------");
            context.close();
        }
    }
    View Code

    发现此时并没有异步执行

    需要在异步打印类中启用@EnableAsync,并在具体方法上标注@Async即可

    package com.lhx.spring.springboot_enable;
    
    import java.util.concurrent.TimeUnit;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.stereotype.Component;
    
    @Component
    @EnableAsync
    public class Jeep implements Runnable {
    
        @Override
        @Async
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    System.out.println("---------------"+i);
                    TimeUnit.SECONDS.sleep(1);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    }
    View Code

    这时在调用App方法即可异步

    注解:启用异步,一般和Async一起使用

    二、原理

    1、注解Import

      跟踪EnableAsync或EnableConfigurationProperties进入发现,共同注解@Import。  

    /*
     * Copyright 2002-2016 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * Indicates one or more {@link Configuration @Configuration} classes to import.
     *
     * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
     * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
     * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
     * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).
     *
     * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be
     * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired}
     * injection. Either the bean itself can be autowired, or the configuration class instance
     * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly
     * navigation between {@code @Configuration} class methods.
     *
     * <p>May be declared at the class level or as a meta-annotation.
     *
     * <p>If XML or other non-{@code @Configuration} bean definition resources need to be
     * imported, use the {@link ImportResource @ImportResource} annotation instead.
     *
     * @author Chris Beams
     * @author Juergen Hoeller
     * @since 3.0
     * @see Configuration
     * @see ImportSelector
     * @see ImportResource
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    
        /**
         * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
         * or regular component classes to import.
         */
        Class<?>[] value();
    
    }
    View Code

      import作用:Indicates one or more {@link Configuration @Configuration} classes to import.

      用来导入一个或多个类(bean被spring容器托管)、或者配置类(配置类里面的Bean都会被spring容器托管)

      Enable*其实就是使用了Import,Import其实就是导入了配置类

    示例

    新建一个User类

    package com.lhx.spring.springboot_enable;
    
    public class User {
    
    }
    View Code

    新建一个Role类

    package com.lhx.spring.springboot_enable;
    
    public class Role {
    
    }
    View Code

    使用Import在App中导入

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    
    @ComponentScan
    @Import(User.class)
    public class App2 {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(App2.class, args);
            System.out.println(context.getBean(User.class));
            System.out.println(context.getBean(Role.class));
            context.close();
        }
    }
    View Code

    可以看到导入一个,另一个会失败

    也可以导入配置类

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.context.annotation.Bean;
    
    public class MyConfiguration {
        @Bean
        public Runnable createRunnable() {
            return () -> {
            };
        }
    
        @Bean
        public Runnable createRunnable2() {
            return () -> {
            };
        }
    }
    View Code

    导入

    @Import({User.class,Role.class,MyConfiguration.class})  

    2、ImportSelector接口

    作用:方法selectImports的返回值,必须是一个class(全称),改class会被Spring容器托管

    /*
     * Copyright 2002-2013 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.annotation;
    
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * Interface to be implemented by types that determine which @{@link Configuration}
     * class(es) should be imported based on a given selection criteria, usually one or more
     * annotation attributes.
     *
     * <p>An {@link ImportSelector} may implement any of the following
     * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
     * methods will be called prior to {@link #selectImports}:
     * <ul>
     * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
     * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
     * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
     * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>
     * </ul>
     *
     * <p>ImportSelectors are usually processed in the same way as regular {@code @Import}
     * annotations, however, it is also possible to defer selection of imports until all
     * {@code @Configuration} classes have been processed (see {@link DeferredImportSelector}
     * for details).
     *
     * @author Chris Beams
     * @since 3.1
     * @see DeferredImportSelector
     * @see Import
     * @see ImportBeanDefinitionRegistrar
     * @see Configuration
     */
    public interface ImportSelector {
    
        /**
         * Select and return the names of which class(es) should be imported based on
         * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
         */
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    
    }
    View Code

    示例

    创建一个MyImportSelector

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[] {"com.lhx.spring.springboot_enable.User",Role.class.getName(),MyConfiguration.class.getName()};
        }
    
    }
    View Code

    在App中使用

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportSelector;
    
    @ComponentScan
    @Import(MyImportSelector.class)
    public class App3 {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(App3.class, args);
            System.out.println(context.getBean(User.class));
            System.out.println(context.getBean(Role.class));
            System.out.println(context.getBeansOfType(Runnable.class));
            
            context.close();
        }
    }
    View Code

    增加一个Enable开头的注解,可以结合注解使用

    增加一个注解EnableLog

    package com.lhx.spring.springboot_enable;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.context.annotation.Import;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(MyImportSelector2.class)
    public @interface EnableLog {
        String name();
    }
    View Code

    增加一个ImportSelector实现MyImportSelector2

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector2 implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
            return new String[] { "com.lhx.spring.springboot_enable.User", Role.class.getName(),
                    MyConfiguration.class.getName() };
        }
    
    }
    View Code

    使用

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportSelector;
    
    @ComponentScan
    @EnableLog(name="my annon")
    public class App4 {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(App4.class, args);
            System.out.println(context.getBean(User.class));
            System.out.println(context.getBean(Role.class));
            System.out.println(context.getBeansOfType(Runnable.class));
            
            context.close();
        }
    }
    View Code

    3、ImportBeanDefinitionRegistrar 

    作用:方法的参数有一个BeanDefinitionRegistry,BeanDefinitionRegistry可以用来网Spring容器中注入Bean,如此,就可以动态注入bean

    /*
     * Copyright 2002-2013 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.context.annotation;
    
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    import org.springframework.core.type.AnnotationMetadata;
    
    /**
     * Interface to be implemented by types that register additional bean definitions when
     * processing @{@link Configuration} classes. Useful when operating at the bean definition
     * level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
     *
     * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
     * may be provided to the @{@link Import} annotation (or may also be returned from an
     * {@code ImportSelector}).
     *
     * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
     * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
     * methods will be called prior to {@link #registerBeanDefinitions}:
     * <ul>
     * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
     * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
     * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
     * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
     * </ul>
     *
     * <p>See implementations and associated unit tests for usage examples.
     *
     * @author Chris Beams
     * @since 3.1
     * @see Import
     * @see ImportSelector
     * @see Configuration
     */
    public interface ImportBeanDefinitionRegistrar {
    
        /**
         * Register bean definitions as necessary based on the given annotation metadata of
         * the importing {@code @Configuration} class.
         * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
         * registered here, due to lifecycle constraints related to {@code @Configuration}
         * class processing.
         * @param importingClassMetadata annotation metadata of the importing class
         * @param registry current bean definition registry
         */
        public void registerBeanDefinitions(
                AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
    
    }
    View Code

    自定义实现MyImportBeanDefinitionRegistrar

    package com.lhx.spring.springboot_enable;
    
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            registry.registerBeanDefinition("user",
                    BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition());
    
            registry.registerBeanDefinition("role",
                    BeanDefinitionBuilder.rootBeanDefinition(Role.class).getBeanDefinition());
            registry.registerBeanDefinition(MyConfiguration.class.getName(),
                    BeanDefinitionBuilder.rootBeanDefinition(MyConfiguration.class).getBeanDefinition());
    
        }
    
    }
    View Code

    此时就可以在EnableLog上使用

    package com.lhx.spring.springboot_enable;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.context.annotation.Import;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    //@Import(MyImportSelector2.class)
    @Import(MyImportBeanDefinitionRegistrar.class)
    public @interface EnableLog {
        String name();
    }
    View Code

    示例

    代码地址:https://github.com/bjlhx15/spring-boot.git 中的 springboot-enable即可

  • 相关阅读:
    rails 相关文件
    linux学习---vi进行多行的copy,cut
    设计
    互联网---现在正在变成过去
    testing
    TTl
    如何成为优秀的性能测试工程师
    linuX学习
    Programiz 中文系列教程·翻译完成
    Programiz C 语言教程·翻译完成
  • 原文地址:https://www.cnblogs.com/bjlhx/p/8331505.html
Copyright © 2020-2023  润新知