栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot-自动配置原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 {

   @Override
   public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
        // 注册注解包名下面所有的组件
      register(registry, new Packageimports(metadata).getPackageNames().toArray(new String[0]));
   }

   @Override
   public Set determineimports(Annotationmetadata metadata) {
      return Collections.singleton(new Packageimports(metadata));
   }

} 

2.AutoConfigurationimportSelector.class

(1)、利用getAutoConfigurationEntry(annotationmetadata);给容器中批量导入一些组件

(2)、调用List configurations = getCandidateConfigurations(annotationmetadata, attributes)获取到所有需要导入到容器中的配置类

(3)、利用工厂加载 Map> 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 configurations = getCandidateConfigurations(annotationmetadata, attributes);
   configurations = removeDuplicates(configurations);
   Set 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;


@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 {

   
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "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 incorrectly
         return 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 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
   @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;
   }

   @Bean
   public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
      return new LocaleCharsetMappingsCustomizer(this.properties);
   }

   static class LocaleCharsetMappingsCustomizer
         implements WebServerFactoryCustomizer, Ordered {

      private final Encoding properties;

      LocaleCharsetMappingsCustomizer(Encoding properties) {
         this.properties = properties;
      }

      @Override
      public void customize(ConfigurableServletWebServerFactory factory) {
         if (this.properties.getMapping() != null) {
            factory.setLocaleCharsetMappings(this.properties.getMapping());
         }
      }

      @Override
      public 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

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号