一 .概述
在springboot之中最令我们喜欢的特性大概就是自动配置了.springboot根据自动配置帮助我们实现整个开发环境的配置,这可以让我们不需要每次都完成那些重复的配置工作了.
本此,我们就分析一下自动配置的原理.
二 .程序的启动类
@SpringBootApplication public class SpringbootRunnerClass { public static void main(String[] args) { SpringApplication.run(SpringbootRunnerClass.class, args); } }
在我们springboot的程序启动类之中,我们使用了一个注解@SpringBootApplicatiion注解.
我们下面看看这个注解帮助我们完成了什么.
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
我们发现在这个注解上面带有三个子注解,我们下面来看看这写注解是干什么用的.
@Configuration public @interface SpringBootConfiguration { }
第一个注解,我们发现就是一个配置类,只不过是springvoot特意定义的一个注解而已.
@Repeatable(ComponentScans.class) public @interface ComponentScan
第三个注解,我们非常的熟悉了,那就是扫包注解,在springboot之中,默认的扫包策略就是启动包的子包进行扫描.
现在,我们所有的注意都放在了第二个注解上面.
@AutoConfigurationPackage @Import(EnableAutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
我们发现在这个注解上面有两个子注解.我们看看他们是干什么用的.
/** * Indicates that the package containing the annotated class should be registered with * {@link AutoConfigurationPackages}. @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
从上面的注视之中,我们可以看到,它帮助我们注册该包的所有的bean.
我们再来看看帮助我们引入的那个类是干什么用的.
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector { @Override protected boolean isEnabled(AnnotationMetadata metadata) { if (getClass().equals(EnableAutoConfigurationImportSelector.class)) { return getEnvironment().getProperty( EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true); } return true; } }
我们看看它的父类:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } try { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); configurations = sort(configurations, autoConfigurationMetadata); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return configurations.toArray(new String[configurations.size()]); } catch (IOException ex) { throw new IllegalStateException(ex); } }
我们发现,这个就是一个导入bean的组件的@Import而已,我们现在需要关注的就是到底为我们导入了什么?
protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
我们发现从该配置之中加入了很多了bean.这些bean很多都是一些配置类,基本上都存在springboot的自动配置包下.也就是说,最终这个路径下的配置文件之中的bean大多都会注册到我们的bean之中.
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
另外我们发现也从这个配置文件之中引入了很多的组件.
也就是说,springboot会从上面的一些配置文件之中加载很多的bean.
三 .自动配置类
我们从上面看到了springboot几乎为我们加载了自动配置包下面的所有的自动配置类.
我们看看我们这个包下面到底有些什么东西.
里面所有的类都是自动配置类,我们看一个简单的就好了.
@Configuration @ConditionalOnClass(Gson.class) public class GsonAutoConfiguration { @Bean @ConditionalOnMissingBean public Gson gson() { return new Gson(); } }
我们发现上面就是一个简单的配置类,如果类路径下面由一个Gson类,而且容器之中没有gson对象,springboot就会帮助我们向容器之中注入一个bean.
四 [email protected]注解
在springboot之中(实际上在spring4之中引入),为我们引入了一些条件注解,这些注解会帮助我们在运行环境下确定是否向容器之中注册bean.
实际上就是一个boolean的判断,如果条件满足,就会注入,否则就不会注入.
我们看看几个常见的注解:
[1]ConditionalOnBean : 当容器之中含有这个bean的时候,就会注入
[2]ConditionalOnClass : 当类路径下有这个class的时候就会注册
[3]ConditionalOnProperty : 当环境变量之中有这个属性的时候就会注册
等等等,springboot就是通过这些注解完成的自动的装配.
五 .自动装配的分析
我们使用一个比较简单的配置类进行自动配置的一般流程的分析:
@Configuration @EnableConfigurationProperties(HttpEncodingProperties.class) @ConditionalOnWebApplication @ConditionalOnClass(CharacterEncodingFilter.class) @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { private final HttpEncodingProperties properties; public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean @ConditionalOnMissingBean(CharacterEncodingFilter.class) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; } @Bean public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { return new LocaleCharsetMappingsCustomizer(this.properties); } private static class LocaleCharsetMappingsCustomizer implements EmbeddedServletContainerCustomizer, Ordered { private final HttpEncodingProperties properties; LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) { this.properties = properties; } @Override public void customize(ConfigurableEmbeddedServletContainer container) { if (this.properties.getMapping() != null) { container.setLocaleCharsetMappings(this.properties.getMapping()); } } @Override public int getOrder() { return 0; } } }
[1]首先,我们发现这个是一个配置类.
[2]@ConditionalOnWebApplication 表示需要在web环境下才注册
[3]@ConditionalOnClass(CharacterEncodingFilter.class) 表示需要在类路径下有一个类才进行注册
[4]@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) 这个表示在配置文件下有这个属性才可以,但是在这里给出了默认值,也就是说一定成立的,除非用户主动取消掉这个配置.
[5]@EnableConfigurationProperties(HttpEncodingProperties.class) 这个注解表示完成对HttpEncodingProperties这个属性配置类进行属性的填充.
下面,我们来分析一下源代码了:
private final HttpEncodingProperties properties; public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; }
我们从上面的注解之中看到了会向容器之中注册一个HttpEncodingProperties类,然后本自动配置类就实例化了.也就是说,现在的HttpEncodingProperties 的属性被填充好了.
@Bean @ConditionalOnMissingBean(CharacterEncodingFilter.class) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; }
表示向容器之中添加一个字符编码过滤器.
@Bean public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() { return new LocaleCharsetMappingsCustomizer(this.properties); }
表示向容器之中添加一个自定义配置器.
六 .自动配置的基本流程
[1]一般会有一个属性配置类与属性文件进行绑定
[2]然后通过这个属性的对自动配置i类进行属性的填充
[3]然后就是创建不同bean到容器之之中,直到完成整个springboot的自动配置.
七 .自动配置报告
我们现在知道了springvoot的自动配置原理了,但是我们看到自动配置非常的复杂,我们如何好能确定一个bean是否已经被初始化好了呢?
springboot为我们提供了自动配置报告.
debug=true
我们在主配置文件之中,可以添加如上的配置,然后再启动springboot 的时候就会开启自动配置报告的生成.
在我们的控制台上就显示了不同的配置报告的信息.
从这里显示的就是springboot帮助我们添加的bean,也就是自动配置成功的类.
下面显示的就是没有自动成功的类.
原文地址:https://www.cnblogs.com/trekxu/p/9739548.html