您的位置:首页 > 编程语言 > Java开发

SpringBoot自动装配详解---带源码

2020-05-10 04:15 1331 查看

前言

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.2
selectImports()
方法

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.3
selectImports()
方法中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:password@example.com: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
注解与全局配置文件中对应的属性进行绑定的

ChengHuanHuaning 原创文章 24获赞 68访问量 1万+ 关注 私信
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: