Spring boot 注解原理分析(@EnableAutoConfiguration深入理解) 上

一、项目结构图:

在这里插入图片描述

二、关于读取配置文件属性和指定配置文件:

(1)、读取配置文件属性有以下几种方式:

<1>、使用@Value注解
<2>、使用Environment.getProperty方法

代码:
User.class:

public class User {
    //通过environment.getProperty("autoPassword")来获取配置属性
    @Autowired
    private Environment environment;
    //通过@Value来获取配置属性
    @Value("autoUserName")
    private String autoUserName;
    public void show(){
        System.out.println("show User-autoUserName:"+autoUserName+",autoPassword:"+environment.getProperty("autoPassword")+",pro.userName:"+environment.getProperty("pro.userName"));
    }
}

<3>、启动springBoot的特性注解@ConfigurationProperties,POJO的方式批量读取

@ConfigurationProperties(prefix = "log")
@Component
public class LogConfiguration {

    private String logUserName;

    private String logPassword;

    private Integer logAge;

    public void show(){
        System.out.println("LogConfiguration show-logUserName:"+logUserName+",logPassword:"+logPassword+",logAge:"+logAge);
    }

    public String getLogUserName() {
        return logUserName;
    }

    public void setLogUserName(String logUserName) {
        this.logUserName = logUserName;
    }

    public String getLogPassword() {
        return logPassword;
    }

    public void setLogPassword(String logPassword) {
        this.logPassword = logPassword;
    }

    public Integer getLogAge() {
        return logAge;
    }

    public void setLogAge(Integer logAge) {
        this.logAge = logAge;
    }
}

理解:@ConfigurationProperties(prefix = “log”),启用SpringBoot特性,将配置文件的属性注入到对应的类属性里面,(只能注入前缀为log的)

三、设置读取那个静态配置文件:

通过@PropertySource或者@PropertySources注解指定配置文件路径

DevConfiguration.class:

@Component
@PropertySource("classpath:application-dev.properties") //指定那个配置文件
//@PropertySources({@PropertySource(""),@PropertySource("")})
public class DevConfiguration {

    @Value("${dev.name}")
    private String devName;

    @Value("${dev.pass}")
    private  String devPass;

    public void show(){
        System.out.println("DevConfiguration show-devName:"+devName+",devPass:"+devPass);
    }
}

四、动态的读取外部的配置文件:

可以实现EnvironmentPostProcessor接口:

代码:

MyEnvironmentPostProcessor.class:

//动态的读取外部文件
@Component
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
        try(InputStream input=this.getClass().getClassLoader().getResourceAsStream("application-pro.properties")) {
            Properties properties = new Properties();
            properties.load(input);
            PropertiesPropertySource propertySource = new PropertiesPropertySource("myPost", properties);
            configurableEnvironment.getPropertySources().addLast(propertySource);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

理解:

通过postProcessEnvironment方法(configurableEnvironment:配置环境对象,springApplication:Spring容器应用)

1、通过this.getClass().getClassLoader().getResourceAsStream("application-pro.properties")读取到外部的配置文件。
2、properties.load(input); 创建一个Properties对象
3、PropertiesPropertySource propertySource = new PropertiesPropertySource("myPost", properties);
	new一个propertySource	对象
4、configurableEnvironment.getPropertySources().addLast(propertySource);加载到配置环境里面,
	这样就可以在spring容器中获取到这个外部配置文件的属性。

五、分析@SpringBootApplication注解:

@SpringBootApplication注解启动包含3个注解:

(1)、SpringBootConfiguration:标注这个类为注解类
(2)、@ComponentScan:配置自动扫描
(3)、@EnableAutoConfiguration //启动spring特性(注入配置文件字段,事务等),
    上面演示的@ConfigurationProperties,POJO的方式批量读取配置文件,就要依靠这个注解(先启动spring特性)

五、分析@EnableAutoConfiguration注解:

其实@EnableAutoConfiguration注解的作用是返回一些特定的bean给Spring容器,
这些bean来完成Spring的一些特定的功能,
那么是怎么返回的呢?
这个我们先了解一下@EnableAutoConfiguration注解里面的@Import注解。

(1)、@Import:

@Import注解是用来导入bean的,一般导入一个继承了ImportSelector接口的bean,ImportSelector接口的抽象方法:selectImports会返回一个String[],这个数组内存的是bean的名字,spring容器会装配这些bean

代码:

MyImportSelector.class

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //获取注解的属性信息
        System.out.println(annotationMetadata.getAllAnnotationAttributes(EnableLog.class.getName()));
        return new String[]{ImportRole.class.getName(),ImprotUser.class.getName()};
    }
}

EnableLog.class

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({MyImportSelector.class})
public @interface EnableLog {
    String name();
}

理解:

声明一个注解EnableLog ,这个注解上标注@Import({MyImportSelector.class}),
也就是导入MyImportSelector.class,MyImportSelector 实现了ImportSelector 接口,
通过接口方法,返回需要注入到Spring容器的bean。

(2)、写一个例子
演示@EnableAutoConfiguration是怎么启用SpringBoot的特性的?
比如为什么在配置类加上一个@EnableAutoConfiguration注解,就可以读取到配置文件!
这个例子我们实现的功能是:当spring容器装配我指定包的bean时,就打印这些bean的信息:

第一步:

创建一个实现BeanPostProcessor接口的类,
这个类的两个抽象方法会在每个bean装配时被调用,
在postProcessBeforeInitialization方法里面实现逻辑,当前bean属于packages包的时候,
打印 System.out.println("echo bean:"+bean);

EchoBeanPostProcessor.class

public class EchoBeanPostProcessor implements BeanPostProcessor {
    private List<String> packages;
    
    //spring容器装配每个bean,init之前
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //如果是这个包里面的,就打印
        for (String pack: packages){
            System.out.println(bean.getClass().getName());
            if (bean.getClass().getName().startsWith(pack)){
                System.out.println("echo bean:"+bean);
            }
        }
        return bean;
    }
    @Override
    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;
    }
}

第二步:

创建一个实现ImportBeanDefinitionRegistrar接口的方法,
registerBeanDefinitions这个接口方法会在Spring容器启动时调用,
启动时获取注解里面的参数信息,装配EchoBeanPostProcessor到spring容器,
获取到注解上的参数注入到EchoBeanPostProcessor里面。

EchoImportBeanDefinitionRegistrar.class

/**
 * Spring容器启动时候,动态注入bean(注入EchoBeanPostProcessor)
 */
public class EchoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        Map<String, Object> arrt= annotationMetadata.getAnnotationAttributes(EnableEcho.class.getName());
        String [] pack=(String[]) arrt.get("packages");
        List<String> packages= Arrays.asList(pack);
        BeanDefinitionBuilder bdb=BeanDefinitionBuilder.rootBeanDefinition(EchoBeanPostProcessor.class);
        bdb.addPropertyValue("packages",packages);
        System.out.println("packages:"+packages);
        beanDefinitionRegistry.registerBeanDefinition(EchoBeanPostProcessor.class.getName(), bdb.getBeanDefinition());
 }

第三步:

创建注解EnableEcho,这个注解导入EchoImportBeanDefinitionRegistrar.class,当标准此注解的时候,也就是启动了EchoImportBeanDefinitionRegistrar.class。

EnableEcho.class

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EchoImportBeanDefinitionRegistrar.class)
public @interface EnableEcho {
    String [] packages();
}

第四步:

引用@EnableEcho注解:

AppImport.class

@EnableEcho(packages={"com.miller.test"})
public class AppImport {
    public static void main(String[] args) {
        ConfigurableApplicationContext context=SpringApplication.run(AppImport.class,args);
        context.close();
    }
}

总结:

整个过程就是标注一个注解@EnableEcho(packages={"com.miller.test"}),
这个注解里@Import(EchoImportBeanDefinitionRegistrar.class),
EchoImportBeanDefinitionRegistrar实现了ImportBeanDefinitionRegistrar,
里面的registerBeanDefinitions抽象方法会在Spring容器启动时候调用,
在registerBeanDefinitions里面实现装配EchoBeanPostProcessor,
EchoBeanPostProcessor实现了BeanPostProcessor接口,
在每个bean装配的时候,就会调用此接口的抽象方法,
然后就可以在抽象方法里面判断是否是指定的包内的bean。

@EnableAutoConfiguration可以启用Spring特性
其实大概的原理就是这样,无非就是标注了这个@EnableAutoConfiguration注解,
这个注解里面@Import了某些bean,这些bean里面实现了ImportSelector 接口
或者ImportBeanDefinitionRegistrar接口,通过这些接口的抽象方法,
就可以返回一系统的jar包里面的bean或者classPath里面的bean装配到Spring容器里面去,
从而实现了一些Spring特性。


至于更加具体的过程,就请看我的下一篇博客吧。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值