前言
SpringBoot中我们经常直接使用@SpringBootApplication
这个复合注解去标记为配置类,包含了SpringBoot常用的三个标签@Configuration
、@EnableAutoConfiguration
、@ComponentScan
.具有这三个标签的作用声明为一个配置类、自动配置、自动扫描
使用@SpringBootApplication
注解
我们直接进入这个注解中,查看它的源码。
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
有道翻译:
* 表示一个{@link Configuration configuration}类,该类声明一个或多个
* {@link Bean @Bean}方法,并且还触发{@link EnableAutoConfiguration
* 自动配置}和{@link ComponentScan组件扫描}。这是一个方便的注释,等同于声明{@code @Configuration},
* {@code @EnableAutoConfiguration}和{@code @ComponentScan}。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration <---------声明为配置类
@EnableAutoConfiguration <---------完成自动配置
@ComponentScan(excludeFilters = { <---------实现组件扫描
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
自动配置实现流程
1.@Import
标签
现在我们继续进入@EnableAutoConfiguration
标签里面我们可以看到,EnableAutoConfiguration
这个类,它使用了一个import导入了一个AutoConfigurationImportSelector.calss
自动导入选择器.所以我们完成自动配置的具体功能还在这里面。
@Import(AutoConfigurationImportSelector.class) <------导入选择器
public @interface EnableAutoConfiguration
@Import这个注解,能将继承了
ImportSelector
这个类的子类,调用子类中的:selectImports()
方法传入完全限定名,就可以将完全限定名对应的类导入SpringIOC容器中.会把第三方jar的类加载到当前spring容器
2.AutoConfigurationImportSelector
自动导入选择器
2.1继承DeferredImportSelector类
这个类的父类就继承了ImportSelector
。就刚好可以与上面的import
标签相对
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
public interface DeferredImportSelector extends ImportSelector
2.2selectImports()
方法
在AutoConfigurationImportSelector
这个类中就实现了这样一个selectImports()
这个方法(核心),可以帮助我们调用SpringFactoriesLoader.loadFactoryNames()
这个方法
//从这里可以看出该类实现很多的xxxAware和DeferredImportSelector,所有的aware都优先于selectImports
//方法执行,也就是说selectImports方法最后执行,那么在它执行的时候所有需要的资源都已经获取到了
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//1加载META-INF/spring-autoconfigure-metadata.properties文件
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//2获取注解的属性及其值(PS:注解指的是@EnableAutoConfiguration注解)
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//3.在classpath下所有的META-INF/spring.factories文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,并将其封装到一个List中返回
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//4.对上一步返回的List中的元素去重、排序
configurations = this.removeDuplicates(configurations);
//5.依据第2步中获取的属性值排除一些特定的类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//6对上一步中所得到的List进行过滤,过滤的依据是条件匹配。这里用到的过滤器是
//org.springframework.boot.autoconfigure.condition.OnClassCondition最终返回的是一个ConditionOutcome[]
//数组。(PS:很多类都是依赖于其它的类的,当有某个类时才会装配,所以这次过滤的就是根据是否有某个
//class进而决定是否装配的。这些类所依赖的类都写在META-INF/spring-autoconfigure-metadata.properties文件里)
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
2.3selectImports()
方法中list集合是整个的核心
//3.在classpath下所有的META-INF/spring.factories文件中查找org.springframework.boot.autoconfigure.EnableAutoConfiguration的值,并将其封装到一个List中返回
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
1.在这个语句中会调用自己的一个:SpringFactoriesLoader.loadFactoryNames()
这个方法,从类路径下得到一个资源
2.在这个SpringFactoriesLoader
类中的方法中,会找到META-INF/spring.factories
这个资源文件。而这个文件中全是键值对结构
public abstract class SpringFactoriesLoader {
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
3.那么扫描到的这些文件作用:是把这个文件的urls拿到之后并把这些urls每一个遍历,最终把这些文件整成一个properties对象。
4.最后通过开始的import
标签将spring.factories这个文件找到配置所有EnableAutoConfiguration的值加入到Spring容器中,而每一个xxxAutoConfiguration
类都是容器中的一个组件(90多个左右),并都加入到容器中。
加入到容器中之后的作用就是用它们来做自动配置,这就是Springboot自动配置之源,也就是自动配置的开始,只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动
3.自动配置生效
现在我们都能找到相应的自动配置xxxAutoConfiguration
类,然后Springboot在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:
@ConditionalOnBean:当容器里有指定的bean的条件下。
@ConditionalOnMissingBean:当容器里不存在指定bean的条件下。
@ConditionalOnClass:当类路径下有指定类的条件下。(常用)
@ConditionalOnMissingClass:当类路径下不存在指定类的条件下(常用)
@ConditionalOnProperty:指定的属性是否有指定的值
3.1 我们以其中的RedisAutoConfiguration
类为例
@Configuration//表示这是一个配置类,类似于以前编写的配置文件一样,也可以给容器中添加组件
@ConditionalOnClass(RedisOperations.class)//根据pom里是否引入了相应的jar包,如果没有就不会启动该配置类
@EnableConfigurationProperties(RedisProperties.class)//开启配置属性,会去找相应的配置文件类,导入这个已经绑定了属性的bean到spring容器中
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")//会检测容器中是否有redisTemplate类的对象,如果没有则生成一个。
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean//会检测容器中是否有TestService类的对象,如果没有则生成一个。
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
1.@EnableConfigurationProperties(RedisProperties.class)
这个注解可以找到相应的配置类,导入这个已经绑定了属性的bean到spring容器中。我们进入这个配置类中查看一些具体的信息
这里我们就截取一部分,他会自动帮我们配置一些如:端口=6379之类的信息
//这里我们就截取一部分,他会自动帮我们配置一些如:端口=6379之类的信息
@ConfigurationProperties(prefix = "spring.redis")//根据配置文件的前缀与相应的值进行绑定
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Connection URL. Overrides host, port, and password. User is ignored. Example:
* redis://user:[email protected]:6379
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
我们就可以得到一个具有一些属性的而且实例化好了的类。
4.总结
最后我们总结一下:
1.Spring Boot启动的时候会通过@EnableAutoConfiguration
注解,匹配到@Import(AutoConfigurationImportSelector.class)
2.然后再AutoConfigurationImportSelector.class
找到META-INF/spring.factories
配置文件中的所有自动配置类,并对其进行加载
3.而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类
4.它能通过以Properties
结尾命名的类中取得在全局配置文件中配置的属性如:server.port
,而XxxxProperties类是通过@ConfigurationProperties
注解与全局配置文件中对应的属性进行绑定的