<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      本文包含:SpringBoot的自動(dòng)配置原理及如何自定義SpringBootStar等

      我們知道,在使用SpringBoot的時(shí)候,我們只需要如下方式即可直接啟動(dòng)一個(gè)Web程序:
      @SpringBootApplication public class DemoApplication { public static void
      main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
      和我們之前使用普通Spring時(shí)繁瑣的配置相比簡(jiǎn)直不要太方便,那么你知道SpringBoot實(shí)現(xiàn)這些的原理么

      首先我們看到類上方包含了一個(gè)@SpringBootApplication注解
      @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(
      excludeFilters = {@Filter( type = FilterType.CUSTOM, classes =
      {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes =
      {AutoConfigurationExcludeFilter.class} )} ) public @interface
      SpringBootApplication { @AliasFor( annotation = EnableAutoConfiguration.class )
      Class<?>[] exclude() default {}; @AliasFor( annotation =
      EnableAutoConfiguration.class ) String[] excludeName() default {}; @AliasFor(
      annotation = ComponentScan.class, attribute = "basePackages" ) String[]
      scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class,
      attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default
      {}; }
      這個(gè)注解上邊包含的東西還是比較多的,咱們先看一下兩個(gè)簡(jiǎn)單的熱熱身

      @ComponentScan 注解
      @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes =
      TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes =
      AutoConfigurationExcludeFilter.class) })
      這個(gè)注解咱們都是比較熟悉的,無非就是自動(dòng)掃描并加載符合條件的Bean到容器中,這個(gè)注解會(huì)默認(rèn)掃描聲明類所在的包開始掃描,例如:
      類cn.shiyujun.Demo類上標(biāo)注了@ComponentScan 注解,則cn.shiyujun.controller、
      cn.shiyujun.service等等包下的類都可以被掃描到

      這個(gè)注解一共包含以下幾個(gè)屬性:
      basePackages:指定多個(gè)包名進(jìn)行掃描 basePackageClasses:對(duì)指定的類和接口所屬的包進(jìn)行掃
      excludeFilters:指定不掃描的過濾器 includeFilters:指定掃描的過濾器 lazyInit:是否對(duì)注冊(cè)掃描的bean設(shè)置為懶加載
      nameGenerator:為掃描到的bean自動(dòng)命名 resourcePattern:控制可用于掃描的類文件 scopedProxy:指定代理是否應(yīng)該被掃描
      scopeResolver:指定掃描bean的范圍
      useDefaultFilters:是否開啟對(duì)@Component,@Repository,@Service,@Controller的類進(jìn)行檢測(cè)
      @SpringBootConfiguration注解

      這個(gè)注解更簡(jiǎn)單了,它只是對(duì)Configuration注解的一個(gè)封裝而已
      @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented
      @Configuration public @interface SpringBootConfiguration { }
      EnableAutoConfiguration注解

      這個(gè)注解可是重頭戲了,SpringBoot號(hào)稱的約定大于配置,也就是本文的重點(diǎn)自動(dòng)裝配的原理就在這里了
      @Import({AutoConfigurationImportSelector.class}) public @interface
      EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY =
      "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {};
      String[] excludeName() default {}; }
      簡(jiǎn)單概括一下,這個(gè)注解存在的意義就是:利用@Import注解,將所有符合自動(dòng)裝配條件的bean注入到IOC容器中,關(guān)于@Import
      注解原理這里就不再闡述,感興趣的同學(xué)可以參考此篇文章:Spring @Import注解源碼解析
      <https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2FdNOBwMPHKdccmeJFWzzTOg>

      進(jìn)入類AutoConfigurationImportSelector,觀察其selectImports
      方法,這個(gè)方法執(zhí)行完畢后,Spring會(huì)把這個(gè)方法返回的類的全限定名數(shù)組里的所有的類都注入到IOC容器中
      public String[] selectImports(AnnotationMetadata annotationMetadata) { if
      (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else {
      AutoConfigurationMetadata autoConfigurationMetadata =
      AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
      AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
      List<String> configurations =
      this.getCandidateConfigurations(annotationMetadata, attributes); configurations
      = this.removeDuplicates(configurations); Set<String> exclusions =
      this.getExclusions(annotationMetadata, attributes);
      this.checkExcludedClasses(configurations, exclusions);
      configurations.removeAll(exclusions); configurations =
      this.filter(configurations, autoConfigurationMetadata);
      this.fireAutoConfigurationImportEvents(configurations, exclusions); return
      StringUtils.toStringArray(configurations); } }
      觀察上方代碼:

      * 第一行if時(shí)會(huì)首先判斷當(dāng)前系統(tǒng)是否禁用了自動(dòng)裝配的功能,判斷的代碼如下: protected boolean
      isEnabled(AnnotationMetadata metadata) { return this.getClass() ==
      AutoConfigurationImportSelector.class ?
      (Boolean)this.getEnvironment().getProperty("spring.boot.enableautoconfiguration",
      Boolean.class, true) : true; }
      * 如果當(dāng)前系統(tǒng)禁用了自動(dòng)裝配的功能則會(huì)返回如下這個(gè)空的數(shù)組,后續(xù)也就無法注入bean了 private static final String[]
      NO_IMPORTS = new String[0];
      *
      此時(shí)如果沒有禁用自動(dòng)裝配則進(jìn)入else分枝,第一步操作首先會(huì)去加載所有Spring預(yù)先定義的配置條件信息,這些配置信息在
      org.springframework.boot.autoconfigure包下的
      META-INF/spring-autoconfigure-metadata.properties文件中

      *
      這些配置條件主要含義大致是這樣的:如果你要自動(dòng)裝配某個(gè)類的話,你覺得先存在哪些類或者哪些配置文件等等條件,這些條件的判斷主要是利用了
      @ConditionalXXX注解,關(guān)于@ConditionalXXX系列注解可以參考這篇文章:SpringBoot條件注解@Conditional
      <https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fmp.weixin.qq.com%2Fs%2FRXYIh_g5iU1e3liK-8n5zA>

      *
      這個(gè)文件里的內(nèi)容格式是這樣的:

      org.springframework.boot.actuate.autoconfigure.web.servlet.WebMvcEndpointChildContextConfiguration.ConditionalOnClass=org.springframework.web.servlet.DispatcherServlet
      org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,io.micrometer.core.instrument.MeterRegistry
      org.springframework.boot.actuate.autoconfigure.flyway.FlywayEndpointAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
      * 具體的加載代碼就不列出了,無法就是個(gè)讀取配置文件
      * 這里放個(gè)加載之后的結(jié)果圖:

      * 獲取@EnableAutoConfiguration注解上的exclude、excludeName屬性,這兩個(gè)屬性的作用都是排除一些類的
      *
      這里又是關(guān)鍵的一步,可以看到剛才圖片中spring-autoconfigure-metadata.properties文件的上方存在一個(gè)文件spring.factories,這個(gè)文件可就不止存在于
      org.springframework.boot.autoconfigure
      包里了,所有的包里都有可能存在這個(gè)文件,所以這一步是加載整個(gè)項(xiàng)目所有的spring.factories文件。這個(gè)文件的格式是這樣的
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthIndicatorAutoConfiguration,\org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration,\org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration
      *
      這里存在一個(gè)知識(shí)點(diǎn),SpringBoot中的star就是依靠這個(gè)文件完成的,假如我們需要自定義一個(gè)SpringBoot的Star,就可以在我們的項(xiàng)目的META-INF文件夾下新建一個(gè)spring.factories文件
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.shiyujun.TestAutoConfiguration
      這樣當(dāng)別的項(xiàng)目依賴我們的項(xiàng)目時(shí)就會(huì)自動(dòng)把我們的TestAutoConfiguration類注入到Spring容器中

      * 刪除重復(fù)的自動(dòng)配置類
      * 下面三行就是去除我們指定排除的配置類
      * 接著這一行的邏輯稍微復(fù)雜一些,主要就是根據(jù)加載的配置條件信息來判斷各個(gè)配置類上的@ConditionalXXX系列注解是否滿足需求
      * 最后就是發(fā)布自動(dòng)裝配完成事件,然后返回所有能夠自動(dòng)裝配的類的全限定名

      到了這里我們已經(jīng)把SpringBoot自動(dòng)裝配的原理搞清楚了,但是總感覺差點(diǎn)什么,那我們從這些自動(dòng)裝配的類里面挑一個(gè)我們比較熟悉的關(guān)于Servlet的類來看看咋回事吧:
      @Configuration @ConditionalOnWebApplication( type = Type.SERVLET ) public
      class ServletEndpointManagementContextConfiguration { public
      ServletEndpointManagementContextConfiguration() { } @Bean public
      ExposeExcludePropertyEndpointFilter<ExposableServletEndpoint>
      servletExposeExcludePropertyEndpointFilter(WebEndpointProperties properties) {
      Exposure exposure = properties.getExposure(); return new
      ExposeExcludePropertyEndpointFilter(ExposableServletEndpoint.class,
      exposure.getInclude(), exposure.getExclude(), new String[0]); } @Configuration
      @ConditionalOnClass({ResourceConfig.class})
      @ConditionalOnMissingClass({"org.springframework.web.servlet.DispatcherServlet"})
      public class JerseyServletEndpointManagementContextConfiguration { public
      JerseyServletEndpointManagementContextConfiguration() { } @Bean public
      ServletEndpointRegistrar servletEndpointRegistrar(WebEndpointProperties
      properties, ServletEndpointsSupplier servletEndpointsSupplier) { return new
      ServletEndpointRegistrar(properties.getBasePath(),
      servletEndpointsSupplier.getEndpoints()); } } @Configuration
      @ConditionalOnClass({DispatcherServlet.class}) public class
      WebMvcServletEndpointManagementContextConfiguration { private final
      ApplicationContext context; public
      WebMvcServletEndpointManagementContextConfiguration(ApplicationContext context)
      { this.context = context; } @Bean public ServletEndpointRegistrar
      servletEndpointRegistrar(WebEndpointProperties properties,
      ServletEndpointsSupplier servletEndpointsSupplier) {
      DispatcherServletPathProvider servletPathProvider =
      (DispatcherServletPathProvider)this.context.getBean(DispatcherServletPathProvider.class);
      String servletPath = servletPathProvider.getServletPath(); if
      (servletPath.equals("/")) { servletPath = ""; } return new
      ServletEndpointRegistrar(servletPath + properties.getBasePath(),
      servletEndpointsSupplier.getEndpoints()); } } }
      自上而下觀察整個(gè)類的代碼,你會(huì)發(fā)現(xiàn)這些自動(dòng)裝配的套路都是一樣的

      * 如果當(dāng)前是Servlet環(huán)境則裝配這個(gè)bean
      * 當(dāng)存在類ResourceConfig以及不存在類DispatcherServlet時(shí)裝配
      JerseyServletEndpointManagementContextConfiguration
      * 當(dāng)存在DispatcherServlet類時(shí)裝配WebMvcServletEndpointManagementContextConfiguration
      * 接下來如果還有面試官問你,你會(huì)了么?

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          好爽好大好舒服 | 波多野结衣一区二区三区中文字幕 | 久操视频网站 | 美女操逼逼逼 | 国产一级婬乱A片无遮精产品 | 乱伦第一页 | 国产精品久久久久久久久久中字幕 | 免费无码黄网站在线播放 | 啃咬揉捏胸乳bl 啊啊啊好爽好深 | 小泬BBBBBB免费看 |