springboot-自动配置原理
目錄
一、@SpringBootApplication
二、@EnableAutoConfiguration
1.AutoConfigurationPackages.Registrar.class?批量注冊(cè)組件
2.AutoConfigurationImportSelector.class
三、按需開(kāi)啟自動(dòng)配置
1.得益于按條件裝配@Conditional
2.例如AOP:
四、總結(jié)
五、springboot快速配置
一、@SpringBootApplication
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration // 就是@Configuration。代表當(dāng)前是一個(gè)配置類(lèi) @EnableAutoConfiguration // 開(kāi)啟自動(dòng)配置 // 指定掃描哪些,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) // 給容器中導(dǎo)入一個(gè)組件 public @interface EnableAutoConfiguration {1.AutoConfigurationPackages.Registrar.class?批量注冊(cè)組件
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) //給容器中導(dǎo)入一個(gè)組件 public @interface AutoConfigurationPackage { static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 注冊(cè)注解包名下面所有的組件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);給容器中批量導(dǎo)入一些組件
(2)、調(diào)用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)獲取到所有需要導(dǎo)入到容器中的配置類(lèi)
(3)、利用工廠加載 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的組件
(4)、從META-INF/spring.factories位置來(lái)加載一個(gè)文件。
????默認(rèn)掃描我們當(dāng)前系統(tǒng)里面所有META-INF/spring.factories位置的文件
?? ?文件里面寫(xiě)死了spring-boot一啟動(dòng)就要給容器中加載的所有配置類(lèi)
????spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
????
雖然我們127個(gè)場(chǎng)景的所有自動(dòng)配置啟動(dòng)的時(shí)候默認(rèn)全部加載。xxxxAutoConfiguration
按照條件裝配規(guī)則(@Conditional),最終會(huì)按需配置。
// 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); }三、按需開(kāi)啟自動(dòng)配置
1.得益于按條件裝配@Conditional
可修飾在類(lèi)、方法上。 @ConditionalOnBean // 當(dāng)容器中存在指定的組件時(shí)才。。。 @ConditionalOnMissingBean // 當(dāng)容器中不存在指定的組件時(shí)才。。。 @ConditionalOnClass // 當(dāng)容器中有某一個(gè)類(lèi)時(shí)才。。。 @ConditionalOnMissingClass // 當(dāng)容器中沒(méi)有某一個(gè)類(lèi)時(shí)才。。。 @ConditionalOnResource // 當(dāng)項(xiàng)目類(lèi)路徑存在某個(gè)資源時(shí)才。。。 @ConditionalOnJava // 當(dāng)項(xiàng)目環(huán)境是指定的java版本號(hào)時(shí)才。。。 @ConditionalOnWebApplication // 當(dāng)應(yīng)用是一個(gè)web應(yīng)用時(shí)才。。。 @ConditionalOnSingleCandidate // 當(dāng)容器中只有一個(gè)實(shí)例或者有用@Primary修飾的默認(rèn)bean。 @ConditionalOnProperty // 當(dāng)配置文件中配置了某個(gè)屬性時(shí)才。。。@ConditionalOnBean(name = "tom") @ConditionalOnMissingBean(name = "tom")?
2.例如AOP:
假如說(shuō)配置文件沒(méi)有spring.aop.auto=true,matchIfMissing默認(rèn)就是true,默認(rèn)就是開(kāi)啟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)//容器中有這個(gè)類(lèi)型組件@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)//容器中沒(méi)有這個(gè)名字 multipartResolver 的組件public MultipartResolver multipartResolver(MultipartResolver resolver) {//給@Bean標(biāo)注的方法傳入了對(duì)象參數(shù),這個(gè)參數(shù)的值就會(huì)從容器中找。//SpringMVC multipartResolver。防止有些用戶(hù)配置的文件上傳解析器不符合規(guī)范// 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;}}}四、總結(jié)
* SpringBoot先加載所有的自動(dòng)配置類(lèi) xxxxxAutoConfiguration
* 每個(gè)自動(dòng)配置類(lèi)按照條件進(jìn)行生效(@Conditionalxxxx),默認(rèn)都會(huì)綁定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件進(jìn)行了綁定
* 生效的配置類(lèi)就會(huì)給容器中裝配很多組件
* 只要容器中有這些組件,相當(dāng)于這些功能就有了
* SpringBoot默認(rèn)會(huì)在底層配好所有的組件。但是如果用戶(hù)自己配置了以用戶(hù)的優(yōu)先(@ConditionalOnMissingBean)
*定制化配置:① 用戶(hù)直接自己@Bean替換底層的組件② 用戶(hù)去看這個(gè)組件是獲取的配置文件什么值就去修改。
xxxxxAutoConfiguration ---> 組件 ---> xxxxProperties里面拿值 ----> application.properties
五、springboot快速配置
1.引入場(chǎng)景依賴(lài)
https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
2.查看自動(dòng)配置了哪些(選做)
自己分析,引入場(chǎng)景對(duì)應(yīng)的自動(dòng)配置一般都生效了
配置文件中debug=true開(kāi)啟自動(dòng)配置報(bào)告。Negative(不生效)\Positive(生效)
3.修改配置項(xiàng)
(1)參照文檔修改配置項(xiàng)
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
(2)自己分析。xxxxProperties綁定了配置文件的哪些。
4.自定義加入或者替換組件
@Bean、@Component。。。
5.自定義器 XXXXXCustomizer
總結(jié)
以上是生活随笔為你收集整理的springboot-自动配置原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: spring系列-注解驱动原理及源码-s
- 下一篇: springboot-异常处理使用与原理