• (008)Spring Boot之Enable*注解的工作原理及Import注解详解


    1、简单示例,读取application.properties中属性

      pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.edu.spring</groupId>
        <artifactId>springboot</artifactId>
        <version>1.0.0</version>
        <packaging>jar</packaging>
    
        <name>springboot</name>
        <!-- FIXME change it to the project's website -->
        <url>http://www.example.com</url>
        
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>2.1.6.RELEASE</version>
                    <scope>import</scope>
                    <type>pom</type>
                </dependency>
            </dependencies>
        </dependencyManagement>
        
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
        </dependencies>
    
    </project>
    View Code

      application.properties

    tomcat.host=192.168.1.100
    tomcat.port=8080
    View Code

      TomcatProperties.java使用@ConfigurationProperties读取属性

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

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @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();
        }
    }
    View Code

      运行结果如下:

    2、SpringBootApplication包含EnableAutoConfiguration(启动自动配置),因此使用@EnableAutoConfiguration也可以启动读取配置文件,如下

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    @EnableAutoConfiguration
    @ComponentScan
    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();
        }
    }
    View Code

      运行结果如下:

     3、EnableAutoConfiguration装配了EnableConfigurationProperties,启动配置文件起作用的是EnableConfigurationProperties,如下

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    @EnableConfigurationProperties
    @ComponentScan
    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();
        }
    }
    View Code

      运行结果如下:

    4、@EnableAsync、@Async异步执行,先看下面的同步执行

      Jeep.java

    package com.edu.spring.springboot;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Jeep {
        
        public void method() {
            for(int i=0;i<5;i++){
                System.out.println("--------------:"+i);
            }
        }
    
    }
    View Code

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class App 
    {
        public static void main( String[] args )
        {
            ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
            context.getBean(Jeep.class).method();
            System.out.println("------------------end");
            context.close();
        }
    }
    View Code

      运行结果如下:

       可以看出是同步执行的,异步执行只需在启动类上添加@EnableAsync,在方法上添加@Async,如下:

      Jeep.java

    package com.edu.spring.springboot;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    @Component
    public class Jeep {
        
        @Async
        public void method() {
            for(int i=0;i<5;i++){
                System.out.println("--------------:"+i);
            }
        }
    
    }
    View Code

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.scheduling.annotation.EnableAsync;
    
    @SpringBootApplication
    @EnableAsync
    public class App 
    {
        public static void main( String[] args )
        {
            ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
            context.getBean(Jeep.class).method();
            System.out.println("------------------end");
            context.close();
        }
    }
    View Code

      运行结果如下,可以看到先输出了end

       通过上面上面的例子可以看出:

      启动读取属性配置需要在启动类上添加@EnableConfigurationProperties,在读取的类上添加@ConfigurationProperties

      启动异步执行需要在启动类上添加@EnableAsync,在需要异步的方法上添加@Async。可以看到这两个Enable*注解都含有@Import

    5、import注解导入类,该类会被spring容器托管,导入配置类,该配置类里的bean会被spring容器托管,如下:

      User.java

    package com.edu.spring.springboot;
    
    public class User {
    
    }
    View Code

      Role.java

    package com.edu.spring.springboot;
    
    public class Role {
    
    }
    View Code

      MyConfig.java

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

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    
    @SpringBootApplication
    @Import({User.class,Role.class,MyConfig.class})
    public class App 
    {
        public static void main( String[] args )
        {
            ConfigurableApplicationContext context=SpringApplication.run(App.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

      运行结果如下:

     6、Import导入实现ImportSelector接口的类。该接口的方法selectImports返回一个数组,该数组中所有的类型都会被springboot装配成bean,如下:

      MyImportSelector.java

    package com.edu.spring.springboot;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // TODO Auto-generated method stub
            return new String[]{"com.edu.spring.springboot.User",Role.class.getName(),MyConfig.class.getName()};
        }
    }
    View Code

      User.java

    package com.edu.spring.springboot;
    
    public class User {
    
    }
    View Code

      Role.java

    package com.edu.spring.springboot;
    
    public class Role {
    
    }
    View Code

      MyConfig.java

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

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Import;
    
    @SpringBootApplication
    @Import(MyImportSelector.class)
    public class App 
    {
        public static void main( String[] args )
        {
            ConfigurableApplicationContext context=SpringApplication.run(App.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

      运行结果如下:

      通过selectImports方法可以获取Enable*注解的属性,如下:

      新增注解类Enablelog.java

    package com.edu.spring.springboot;
    
    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(MyImportSelector.class)
    public @interface Enablelog {
        String name();
    }
    View Code

      MyImportSelector.java,输出注解类的属性

    package com.edu.spring.springboot;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector implements ImportSelector {
    
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            System.out.println(importingClassMetadata.getAnnotationAttributes(Enablelog.class.getName()));
            return new String[]{"com.edu.spring.springboot.User",Role.class.getName(),MyConfig.class.getName()};
        }
    }
    View Code

      运行结果如下:

     7、Import导入实现ImportBeanDefinitionRegistrar接口的类。该接口的方法registerBeanDefinitions可以动态注入bean如下:

      User.java

    package com.edu.spring.springboot;
    
    public class User {
    
    }
    View Code

      Role.java

    package com.edu.spring.springboot;
    
    public class Role {
    
    }
    View Code

      MyConfig.java

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

      MyImportBeanDefinitionRegistrar.java,实现ImportBeanDefinitionRegistrar接口

    package com.edu.spring.springboot;
    
    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) {
            BeanDefinitionBuilder bdb=BeanDefinitionBuilder.rootBeanDefinition(User.class);
            registry.registerBeanDefinition("user",bdb.getBeanDefinition());
            
            BeanDefinitionBuilder bdb2=BeanDefinitionBuilder.rootBeanDefinition(Role.class);
            registry.registerBeanDefinition("role",bdb2.getBeanDefinition());
            
            BeanDefinitionBuilder bdb3=BeanDefinitionBuilder.rootBeanDefinition(MyConfig.class);
            registry.registerBeanDefinition(MyConfig.class.getName(),bdb3.getBeanDefinition());
    
        }
    
    }
    View Code

      Enablelog.java

    package com.edu.spring.springboot;
    
    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(MyImportBeanDefinitionRegistrar.class)
    public @interface Enablelog {
        String name();
    }
    View Code

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    @Enablelog(name="this is springboot")
    public class App 
    {
        public static void main( String[] args )
        {
            ConfigurableApplicationContext context=SpringApplication.run(App.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

      运行结果如下:

       同ImportSelector接口一样,ImportBeanDefinitionRegistrar同样可以获取注解的属性,情况下面的简单列子:

      Cat.java

    package com.edu.spring.springboot.bean;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Cat {
    
    }
    View Code

      Dog.java

    package com.edu.spring.springboot.bean2;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class Dog {
    
    }
    View Code

      EchoBeanPostProcessor.java

    package com.edu.spring.springboot;
    
    import java.util.List;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class EchoBeanPostProcessor implements BeanPostProcessor {
        
        private List<String> packages;
        
        public Object postProcessBeforeInitialization(Object bean, String beanName)
                throws BeansException {
            for(String pack:packages){
                if(bean.getClass().getName().startsWith(pack)){
                    System.out.println("echo bean :"+bean.getClass().getName()+"  "+pack);
                }
            }
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException {
            return bean;
        }
        
        public List<String> getPackages() {
            return packages;
        }
    
        public void setPackages(List<String> packages) {
            this.packages = packages;
        }
    
    }
    View Code

      EchoImportBeanDefinitionRegistrar.java

    package com.edu.spring.springboot;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    
    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 EchoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
            // TODO Auto-generated method stub
            Map<String,Object> attr=(Map<String,Object>)importingClassMetadata.getAnnotationAttributes(EnableEcho.class.getName());
            String[] packArr=(String[])attr.get("packages");
            List<String> packages=Arrays.asList(packArr);
            
            System.out.println("packages: "+packages);
            
            BeanDefinitionBuilder bdb=BeanDefinitionBuilder.rootBeanDefinition(EchoBeanPostProcessor.class);
            bdb.addPropertyValue("packages",packages);
            registry.registerBeanDefinition(EchoBeanPostProcessor.class.getName(),bdb.getBeanDefinition());
        }
    
    }
    View Code

      EnableEcho.java

    package com.edu.spring.springboot;
    
    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(EchoImportBeanDefinitionRegistrar.class)
    public @interface EnableEcho {
        String[] packages();
    }
    View Code

      App.java

    package com.edu.spring.springboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    @ComponentScan
    @EnableEcho(packages={"com.edu.spring.springboot.bean","com.edu.spring.springboot.bean2"})
    public class App 
    {
        public static void main( String[] args )
        {
            ConfigurableApplicationContext context=SpringApplication.run(App.class,args);
            context.close();
        }
    }
    View Code

      目录结果如下:

       运行结果如下:

       总结:springboot中在启动类上添加Enable*注解是启动springboot的某个特性,同时需要在指定的类或者方法上添加对应的注解。

            SpringBootApplication注解包含了EnableAutoConfiguration注解。

         EnableAutoConfiguration注解中的EnableConfigurationProperties注解是启动解析配置属性,对应ConfigurationProperties

            Import注解可以装配bean。

            Import注解中导入实现ImportSelector、或者ImportBeanDefinitionRegistrar接口的类来实现Enable*特性

  • 相关阅读:
    SSH整合
    JQuery
    MVC框架与增强
    通用分页(二)
    通用分页(一)
    自定义标签
    反射(一)
    Xml解析作业与Xml建模andXml建模作业
    Xml与Map之间的相互转换
    Xml解析
  • 原文地址:https://www.cnblogs.com/javasl/p/11827838.html
Copyright © 2020-2023  润新知