一、原理剖析
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;
}
继续往下会看到另一个非常重要的方法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容器中。