(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*特性

猜你喜欢

转载自www.cnblogs.com/javasl/p/11827838.html
今日推荐