SpringBoot自动配置原理

一、原理剖析

1、@SpringBootApplication

SpringBoot程序会有一个主程序启动类,这个类上会有一个注解 @SpringBootApplication

@SpringBootApplication
public class SpringBootQuickApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootQuickApplication.class, args);
    }

}

这个注解是一个复合注解,由以下注解组合而成

@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 
2、@EnableAutoConfiguration

我们现在说的自动配置是跟这里面的 @EnableAutoConfiguration息息相关,从字面意思可以看出,它的作用是开启自动配置,此注解也是一个复合注解,由以下注解组成

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
3、@Import(EnableAutoConfigurationImportSelector.class)

自动配置主要是由 @Import(EnableAutoConfigurationImportSelector.class)来完成的。这里需要说明一下,在SpringBoot1.0版本中EnableAutoConfigurationImportSelector是基于实现抽象类的,在2.0版本中是基于实现接口,不过最终都是基于实现同样的接口完成的功能,只不过2.0版本更加简化了,对比一下源码就清楚了

/**
这是1.0版本
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)//注意一下类名称
public @interface EnableAutoConfiguration

@Deprecated
public class EnableAutoConfigurationImportSelector
		extends AutoConfigurationImportSelector
    
public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Ordered
/**
这是2.0版本
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)//这里的类名称和1.0版本不一样
public @interface EnableAutoConfiguration

public class AutoConfigurationImportSelector
		implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
		BeanFactoryAware, EnvironmentAware, Orered
4、selectImports

现在已经进入到了AutoConfigurationImportSelector(基于2.0版本)类里面,这时候关于自动配置非常重要的一个方法出现了,它就是selectImports,告诉springboot都需要导入那些组件,通过返回值也可以看出,最终返回的是一个String类型的数组

public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
5、getCandidateConfigurations

这个方法又调用了getAutoConfigurationEntry方法,然后又调用getCandidateConfigurations方法,返回的是当前项目需要加载的所有配置类的全限定类名的集合,如下图可以看出当前项目自动配置了127个类

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

这是SpringBoot为当前项目自动导入的配置类
继续往下会看到另一个非常重要的方法SpringFactoriesLoader.loadFactoryNames(),这个方法在SpringBoot中的作用是扫描所有jar包类路径下META-INF/spring.factories ,并把这些文件内容封装成装成properties对象

方法里面有个参数getSpringFactoriesLoaderFactoryClass(),返回的是EnableAutoConfiguration.class

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}

EnableAutoConfiguration.class对应的就是META-INF/spring.factories里面名为EnableAutoConfiguration对应的值,如下图可以看出来当前项目都自动配置了哪些组件,到这位置SpringBoot已经把需要的组件添加到IOC容器中了
在这里插入图片描述

6、XxxxAutoConfiguration和XxxxProperties说明

在SpringBoot里面看到 XxxxAutoConfiguration这样的类代表它是配置类,功能是给IOC容器添加组件; XxxxProperties 这样的类代表它是与全局配置文件绑定的属性类,下面以一个例子说一下它的具体作用

二、自动配置生效

首先说明一下, 每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现 ,从org.springframework.boot.autoconfigure(对J2EE进行了大整合,常用组件都有)包中拿一个举例,HttpEncodingAutoConfiguration配置类为例

1、配置类都有哪些注解

(这里以HttpEncodingAutoConfiguration为例,不包括所有配置类上的注解)

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {}
1.1 @Configuration

表示当前类是一个配置类,用来给容器中添加组件

1.2 @EnableConfigurationProperties

开启指定类的ConfigurationProperties功能,把全局配置文件中自定义的值与ServerProperties类中的属性进行一一绑定并把Bean注入到IOC容器,比如在全局配置文件中指定端口号
在这里插入图片描述
调试结果为
在这里插入图片描述
如下只截取了部分源码,其他配置一个道理,但是有一点需要注意,prefix = "server"这里指定名称是server,在全局配置文件中就得写server.属性名=xxx,比如上面的指定端口号server.prot=8888

1.3 @ConfigurationProperties

这个注解就是SpringBoot约定大于配置最终落地的地方

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
	................................
	/**
	 * Server HTTP port.
	 */
	private Integer port;
    ................................
    public void setPort(Integer port) {
		this.port = port;
	}
    ................................
}
1.4 @ConditionalOnWebApplication

根据不同的条件判断当前配置类是否满足条件,如果满足条件则整个配置类生效,这里的判断条件是否为Servlet应用,它的底层是基于**@Conditional(OnWebApplicationCondition.class)**

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {}
1.5 @ConditionalOnClass

判断当前项目有没有把这个CharacterEncodingFilter类加载进来

1.6 @ConditionalOnProperty

判断全局配置文件中是否存在server.servlet.encoding.enabled=true这个配置,如果不存在,也可以加载

matchIfMissing = true决定了此配置不存在也可以加载

2、配置类都需要加载哪些组件

看源码就可以知道了,如下截取的部分源码

public class HttpEncodingAutoConfiguration {

	private final Encoding properties;

	public HttpEncodingAutoConfiguration(ServerProperties properties) {
		this.properties = properties.getServlet().getEncoding();
	}

	@Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		return filter;
	}
1. private final Encoding properties;

通过只有一个参数的构造方法HttpEncodingAutoConfiguration可以从容器中得到Encoding对象,在这之前,Encoding对象已经通过**@EnableConfigurationProperties(ServerProperties.class)**和全局配置文件进行了绑定,这时候所有配置都变成了自定义的

2. @Bean和@ConditionalOnMissingBean

@Bean的作用是给容器中注入组件,学过Spring的同学应该都知道,xml配置方式是

<bean id="ID" class="com.xxx.xxx.类名">
     <property name="属性名" value="属性值"></property>
</bean>

@ConditionalOnMissingBean作用是判断容器中有没有这个类,如果有,则从容器中获取

到这里,我们大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。

这仅仅是个人对SpringBoot自动配置的理解,如有问题,恳请指正

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值