springboot-自动配置原理
目錄
一、@SpringBootApplication
二、@EnableAutoConfiguration
1.AutoConfigurationPackages.Registrar.class?批量注冊組件
2.AutoConfigurationImportSelector.class
三、按需開啟自動配置
1.得益于按條件裝配@Conditional
2.例如AOP:
四、總結
五、springboot快速配置
一、@SpringBootApplication
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration // 就是@Configuration。代表當前是一個配置類 @EnableAutoConfiguration // 開啟自動配置 // 指定掃描哪些,Spring注解 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {二、@EnableAutoConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage // @Import(AutoConfigurationImportSelector.class) // 給容器中導入一個組件 public @interface EnableAutoConfiguration {1.AutoConfigurationPackages.Registrar.class?批量注冊組件
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) //給容器中導入一個組件 public @interface AutoConfigurationPackage { static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 注冊注解包名下面所有的組件register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic Set<Object> determineImports(AnnotationMetadata metadata) {return Collections.singleton(new PackageImports(metadata));}}2.AutoConfigurationImportSelector.class
(1)、利用getAutoConfigurationEntry(annotationMetadata);給容器中批量導入一些組件
(2)、調用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)獲取到所有需要導入到容器中的配置類
(3)、利用工廠加載 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的組件
(4)、從META-INF/spring.factories位置來加載一個文件。
????默認掃描我們當前系統里面所有META-INF/spring.factories位置的文件
?? ?文件里面寫死了spring-boot一啟動就要給容器中加載的所有配置類
????spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
????
雖然我們127個場景的所有自動配置啟動的時候默認全部加載。xxxxAutoConfiguration
按照條件裝配規則(@Conditional),最終會按需配置。
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = getConfigurationClassFilter().filter(configurations);fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions); }三、按需開啟自動配置
1.得益于按條件裝配@Conditional
可修飾在類、方法上。 @ConditionalOnBean // 當容器中存在指定的組件時才。。。 @ConditionalOnMissingBean // 當容器中不存在指定的組件時才。。。 @ConditionalOnClass // 當容器中有某一個類時才。。。 @ConditionalOnMissingClass // 當容器中沒有某一個類時才。。。 @ConditionalOnResource // 當項目類路徑存在某個資源時才。。。 @ConditionalOnJava // 當項目環境是指定的java版本號時才。。。 @ConditionalOnWebApplication // 當應用是一個web應用時才。。。 @ConditionalOnSingleCandidate // 當容器中只有一個實例或者有用@Primary修飾的默認bean。 @ConditionalOnProperty // 當配置文件中配置了某個屬性時才。。。@ConditionalOnBean(name = "tom") @ConditionalOnMissingBean(name = "tom")?
2.例如AOP:
假如說配置文件沒有spring.aop.auto=true,matchIfMissing默認就是true,默認就是開啟AOP。
我們可以看到里面有大量使用@Conditional判斷。
package org.springframework.boot.autoconfigure.aop;import org.aspectj.weaver.Advice; import org.springframework.aop.config.AopConfigUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy;/** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration * Auto-configuration} for Spring's AOP support. Equivalent to enabling * {@link EnableAspectJAutoProxy @EnableAspectJAutoProxy} in your configuration. * <p> * The configuration will not be activated if {@literal spring.aop.auto=false}. The * {@literal proxyTargetClass} attribute will be {@literal true}, by default, but can be * overridden by specifying {@literal spring.aop.proxy-target-class=false}. * * @author Dave Syer * @author Josh Long * @since 1.0.0 * @see EnableAspectJAutoProxy */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Advice.class)static class AspectJAutoProxyingConfiguration {@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",matchIfMissing = false)static class JdkDynamicAutoProxyConfiguration {}@Configuration(proxyBeanMethods = false)@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class CglibAutoProxyConfiguration {}}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.aspectj.weaver.Advice")@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",matchIfMissing = true)static class ClassProxyingConfiguration {ClassProxyingConfiguration(BeanFactory beanFactory) {if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}}} } @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) public class DispatcherServletAutoConfiguration {/** The bean name for a DispatcherServlet that will be mapped to the root URL "/"*/public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";/** The bean name for a ServletRegistrationBean for the DispatcherServlet "/"*/public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";@Configuration(proxyBeanMethods = false)@Conditional(DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)protected static class DispatcherServletConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());return dispatcherServlet;}//給容器中加入了文件上傳解析器@Bean@ConditionalOnBean(MultipartResolver.class)//容器中有這個類型組件@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)//容器中沒有這個名字 multipartResolver 的組件public MultipartResolver multipartResolver(MultipartResolver resolver) {//給@Bean標注的方法傳入了對象參數,這個參數的值就會從容器中找。//SpringMVC multipartResolver。防止有些用戶配置的文件上傳解析器不符合規范// Detect if the user has created a MultipartResolver but named it incorrectlyreturn resolver;}}@Configuration(proxyBeanMethods = false)@Conditional(DispatcherServletRegistrationCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties(WebMvcProperties.class)@Import(DispatcherServletConfiguration.class)protected static class DispatcherServletRegistrationConfiguration {@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,webMvcProperties.getServlet().getPath());registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());multipartConfig.ifAvailable(registration::setMultipartConfig);return registration;}} @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 {private final Encoding properties;public HttpEncodingAutoConfiguration(ServerProperties properties) {this.properties = properties.getServlet().getEncoding();}@Bean@ConditionalOnMissingBeanpublic 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;}@Beanpublic LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {return new LocaleCharsetMappingsCustomizer(this.properties);}static class LocaleCharsetMappingsCustomizerimplements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {private final Encoding properties;LocaleCharsetMappingsCustomizer(Encoding properties) {this.properties = properties;}@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {if (this.properties.getMapping() != null) {factory.setLocaleCharsetMappings(this.properties.getMapping());}}@Overridepublic int getOrder() {return 0;}}}四、總結
* SpringBoot先加載所有的自動配置類 xxxxxAutoConfiguration
* 每個自動配置類按照條件進行生效(@Conditionalxxxx),默認都會綁定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件進行了綁定
* 生效的配置類就會給容器中裝配很多組件
* 只要容器中有這些組件,相當于這些功能就有了
* SpringBoot默認會在底層配好所有的組件。但是如果用戶自己配置了以用戶的優先(@ConditionalOnMissingBean)
*定制化配置:① 用戶直接自己@Bean替換底層的組件② 用戶去看這個組件是獲取的配置文件什么值就去修改。
xxxxxAutoConfiguration ---> 組件 ---> xxxxProperties里面拿值 ----> application.properties
五、springboot快速配置
1.引入場景依賴
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
2.查看自動配置了哪些(選做)
自己分析,引入場景對應的自動配置一般都生效了
配置文件中debug=true開啟自動配置報告。Negative(不生效)\Positive(生效)
3.修改配置項
(1)參照文檔修改配置項
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
(2)自己分析。xxxxProperties綁定了配置文件的哪些。
4.自定義加入或者替換組件
@Bean、@Component。。。
5.自定義器 XXXXXCustomizer
總結
以上是生活随笔為你收集整理的springboot-自动配置原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring系列-注解驱动原理及源码-s
- 下一篇: springBoot-springMVC