先说说我们自己的应用程序中Bean加入容器的办法:
package com.ynunicom.dc.dingdingcontractapp;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author jinye.Bai
*/
@SpringBootApplication(
scanBasePackages ={"com.ynunicom.dc.dingdingcontractapp"}
)
public class DingdingContractAppApplication {
public static void main(String[] args) {
SpringApplication.run(DingdingContractAppApplication.class, args);
}
}
我们在应用程序的入口设置了 @SpringBootApplication标签,默认情况下他会扫描所有次级目录。
如果增加了 scanBasePackages属性,就会扫描所有被指定的路径及其次级目录。
那么它在扫描的是什么东西呢?
是这个:@Component
所有被扫描到的 @Component,都会成为一个默认的singleton(单例,即一个容器里只有一个对象实体)加入到容器中。
认识到以上这点,便于我们理解springboot自动配置的机制。
接下来让我们看看在自己的应用程序中实现配置的方法。
如图:
package com.ynunicom.dc.dingdingcontractapp.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author: jinye.Bai
* @date: 2020/5/22 15:51
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
这里我们设置了一个配置,往容器中加入了一个RestTemplate。
首先说 @Configuration,这个标签继承了 @Component标签,我们可以在标签内容看到:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
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;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
可以看到其中是有 @Component标签的,所以,@Configuration会被 @SpringBootApplication扫描到,进而把它和它下面的 @Bean加入容器,于是我们 RestTemplate的内容就配置完成了,在后续的使用中,我们就可以直接从容器中拿出RestTemplate使用它。
对于在maven中引用的其他外部包加入容器的过程,需要用到spring.factories。
在springboot运行时,SpringFactoriesLoader 类会去寻找
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
我们以mybatis-plus为例。
首先我们引入:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
然后去maven的依赖里看它的自动配置类MybatisPlusAutoConfiguration
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisPlusProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {
可以看到有上文提到的 @Configuration,还有从application.yml载入自动配置的 @EnableConfigurationProperties({MybatisPlusProperties.class})
这个注解的具体内容请查看我另一篇博文,对其进行了解释:
迅速学会@ConfigurationProperties的使用
也就是说,springboot只要能扫描到MybatisPlusAutoConfiguration类的 @Configuration注解,其中的所有配置就能自动加入到容器中,这一过程由上面提到的SpringFactoriesLoader 起作用,它会去寻找 “META-INF/spring.factories” 文件,我们可以在 mybatis-plus的依赖中找到它:
SpringFactoriesLoader为什么要读取它呢?因为它内部是这样的
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\
com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration
spring.factories用键值对的方式记录了所有需要加入容器的类,EnableAutoConfigurationImportSelector的selectImports方法返回的类名,来自spring.factories文件内的配置信息,这些配置信息的key等于EnableAutoConfiguration,因为spring boot应用启动时使用了EnableAutoConfiguration注解,所以EnableAutoConfiguration注解通过import注解将EnableAutoConfigurationImportSelector类实例化,并且将其selectImports方法返回的类名实例化后注册到spring容器。
以上内容是springboot获得这些类的方式,如果你想要实现自己的自动配置,就将你的类通过键值对的方式写在你的spring.factories即可,注意,值是你的自动配置类,键必须是org.springframework.boot.autoconfigure.EnableAutoConfiguration