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

前言

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注解与全局配置文件中对应的属性进行绑定的

原创文章 25 获赞 70 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ChengHuanHuaning/article/details/103416403