<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          長(zhǎng)文慎入!Spring Boot注解原理詳解

          共 29597字,需瀏覽 60分鐘

           ·

          2021-08-16 11:34

            Java大聯(lián)盟

            幫助萬(wàn)千Java學(xué)習(xí)者持續(xù)成長(zhǎng)

          關(guān)注


          cnblogs.com/cmt/p/14553189.html


          B 站搜索:楠哥教你學(xué)Java

          獲取更多優(yōu)質(zhì)視頻教程


          首先,先看SpringBoot的主配置類:
          @SpringBootApplication
          public class StartEurekaApplication
          {
              public static void main(String[] args)
              
          {
                  SpringApplication.run(StartEurekaApplication.classargs);
              }
          }

          點(diǎn)進(jìn)@SpringBootApplication來(lái)看,發(fā)現(xiàn)@SpringBootApplication是一個(gè)組合注解。

          @Target(ElementType.TYPE)
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Inherited
          @SpringBootConfiguration
          @EnableAutoConfiguration
          @ComponentScan(excludeFilters = {
                @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                @Filter(type 
          = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
          public @interface SpringBootApplication 
          {

          }

          首先我們先來(lái)看 @SpringBootConfiguration:

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Configuration
          public @interface SpringBootConfiguration {
          }

          可以看到這個(gè)注解除了元注解以外,就只有一個(gè)@Configuration,那也就是說這個(gè)注解相當(dāng)于@Configuration,所以這兩個(gè)注解作用是一樣的,它讓我們能夠去注冊(cè)一些額外的Bean,并且導(dǎo)入一些額外的配置。


          那@Configuration還有一個(gè)作用就是把該類變成一個(gè)配置類,不需要額外的XML進(jìn)行配置。所以@SpringBootConfiguration就相當(dāng)于@Configuration。進(jìn)入@Configuration,發(fā)現(xiàn)@Configuration核心是@Component,說明Spring的配置類也是Spring的一個(gè)組件。

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Component
          public @interface Configuration {
              @AliasFor(
                  annotation = Component.class
              )
              String value() default ""
          ;
          }

          繼續(xù)來(lái)看下一個(gè)@EnableAutoConfiguration,這個(gè)注解是開啟自動(dòng)配置的功能。

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Inherited
          @AutoConfigurationPackage
          @Import({AutoConfigurationImportSelector.class})
          public @interface EnableAutoConfiguration 
          {
              String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

              Class<?>[] exclude() default {};

              String[] excludeName() default {};
          }

          可以看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)這兩個(gè)而組成的,我們先說@AutoConfigurationPackage,他是說:讓包中的類以及子包中的類能夠被自動(dòng)掃描到spring容器中。

          @Target({ElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          @Inherited
          @Import({Registrar.class})
          public @interface AutoConfigurationPackage 
          {
          }

          使用@Import來(lái)給Spring容器中導(dǎo)入一個(gè)組件 ,這里導(dǎo)入的是Registrar.class。來(lái)看下這個(gè)Registrar:

          static class Registrar implements ImportBeanDefinitionRegistrarDeterminableImports {
                  Registrar() {
                  }

                  public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                      AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
                  }

                  public Set<Object> determineImports(AnnotationMetadata metadata) {
                      return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
                  }
              }

          就是通過以上這個(gè)方法獲取掃描的包路徑,可以debug查看具體的值:

          那metadata是什么呢,可以看到是標(biāo)注在@SpringBootApplication注解上的DemosbApplication,也就是我們的主配置類Application:

          其實(shí)就是將主配置類(即@SpringBootApplication標(biāo)注的類)的所在包及子包里面所有組件掃描加載到Spring容器。因此我們要把DemoApplication放在項(xiàng)目的最高級(jí)中(最外層目錄)。


          看看注解@Import(AutoConfigurationImportSelector.class),@Import注解就是給Spring容器中導(dǎo)入一些組件,這里傳入了一個(gè)組件的選擇器:AutoConfigurationImportSelector。

          可以從圖中看出AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector,ImportSelector有一個(gè)方法為:selectImports。將所有需要導(dǎo)入的組件以全類名的方式返回,這些組件就會(huì)被添加到容器中。

          public String[] selectImports(AnnotationMetadata annotationMetadata) {
              if (!this.isEnabled(annotationMetadata)) {
                  return NO_IMPORTS;
              } else {
                  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                  AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = 
                  this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
                  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
              }
          }

          會(huì)給容器中導(dǎo)入非常多的自動(dòng)配置類(xxxAutoConfiguration);就是給容器中導(dǎo)入這個(gè)場(chǎng)景需要的所有組件,并配置好這些組件。

          有了自動(dòng)配置類,免去了我們手動(dòng)編寫配置注入功能組件等的工作。那是如何獲取到這些配置類的呢,看看下面這個(gè)方法:

          protected AutoConfigurationImportSelector.AutoConfigurationEntry 
            getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) 
          {
              if (!this.isEnabled(annotationMetadata)) {
                  return EMPTY_ENTRY;
              } else {
                  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 new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
              }
          }

          我們可以看到getCandidateConfigurations()這個(gè)方法,他的作用就是引入系統(tǒng)已經(jīng)加載好的一些類,到底是那些類呢:

          protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
              List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
              Assert.notEmpty(configurations, 
              "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
              return configurations;
          }
          public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
              String factoryClassName = factoryClass.getName();
              return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
          }

          會(huì)從META-INF/spring.factories中獲取資源,然后通過Properties加載資源:

          private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
              MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
              if (result != null) {
                  return result;
              } else {
                  try {
                      Enumeration<URL> urls = classLoader != 
                    null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                      LinkedMultiValueMap result = new LinkedMultiValueMap();

                      while(urls.hasMoreElements()) {
                          URL url = (URL)urls.nextElement();
                          UrlResource resource = new UrlResource(url);
                          Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                          Iterator var6 = properties.entrySet().iterator();

                          while(var6.hasNext()) {
                              Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                              String factoryClassName = ((String)entry.getKey()).trim();
                              String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                              int var10 = var9.length;

                              for(int var11 = 0; var11 < var10; ++var11) {
                                  String factoryName = var9[var11];
                                  result.add(factoryClassName, factoryName.trim());
                              }
                          }
                      }

                      cache.put(classLoader, result);
                      return result;
                  } catch (IOException var13) {
                      throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
                  }
              }
          }

          可以知道SpringBoot在啟動(dòng)的時(shí)候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動(dòng)配置類導(dǎo)入到容器中,自動(dòng)配置類就生效,幫我們進(jìn)行自動(dòng)配置工作。以前我們需要自己配置的東西,自動(dòng)配置類都幫我們完成了。如下圖可以發(fā)現(xiàn)Spring常見的一些類已經(jīng)自動(dòng)導(dǎo)入。

          接下來(lái)看@ComponentScan注解,@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),這個(gè)注解就是掃描包,然后放入spring容器。

          @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}), 
            @Filter(type 
          = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})
          public @interface SpringBootApplication 
          {}

          總結(jié)下@SpringbootApplication:就是說,他已經(jīng)把很多東西準(zhǔn)備好,具體是否使用取決于我們的程序或者說配置。


          接下來(lái)繼續(xù)看run方法:

          public static void main(String[] args) {
                  SpringApplication.run(Application.classargs);
              }

          來(lái)看下在執(zhí)行run方法到底有沒有用到哪些自動(dòng)配置的東西,我們點(diǎn)進(jìn)run:

          public ConfigurableApplicationContext run(String... args) {
              //計(jì)時(shí)器
              StopWatch stopWatch = new StopWatch();
              stopWatch.start();
              ConfigurableApplicationContext context = null;
              Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
              this.configureHeadlessProperty();
              //監(jiān)聽器
              SpringApplicationRunListeners listeners = this.getRunListeners(args);
              listeners.starting();

              Collection exceptionReporters;
              try {
                  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                  ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
                  this.configureIgnoreBeanInfo(environment);
                  Banner printedBanner = this.printBanner(environment);
                  //準(zhǔn)備上下文
                  context = this.createApplicationContext();
                  exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class,                       new Class[]{ConfigurableApplicationContext.class}, context);
                  //預(yù)刷新context
                  this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                  //刷新context
                  this.refreshContext(context);
                  //刷新之后的context
                  this.afterRefresh(context, applicationArguments);
                  stopWatch.stop();
                  if (this.logStartupInfo) {
                      (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
                  }

                  listeners.started(context);
                  this.callRunners(context, applicationArguments);
              } catch (Throwable var10) {
                  this.handleRunFailure(context, var10, exceptionReporters, listeners);
                  throw new IllegalStateException(var10);
              }

              try {
                  listeners.running(context);
                  return context;
              } catch (Throwable var9) {
                  this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
                  throw new IllegalStateException(var9);
              }
          }

          那我們關(guān)注的就是 refreshContext(context); 刷新context,我們點(diǎn)進(jìn)來(lái)看。

          private void refreshContext(ConfigurableApplicationContext context) {
             refresh(context);
             if (this.registerShutdownHook) {
                try {
                   context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                   // Not allowed in some environments.
                }
             }
          }

          我們繼續(xù)點(diǎn)進(jìn)refresh(context);

          protected void refresh(ApplicationContext applicationContext) {
             Assert.isInstanceOf(AbstractApplicationContext.classapplicationContext);
             ((AbstractApplicationContext) applicationContext).refresh();
          }

          他會(huì)調(diào)用 ((AbstractApplicationContext) applicationContext).refresh();方法,我們點(diǎn)進(jìn)來(lái)看:

          public void refresh() throws BeansException, IllegalStateException {
             synchronized (this.startupShutdownMonitor) {
                // Prepare this context for refreshing.
                prepareRefresh();
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);

                try {
                   // Allows post-processing of the bean factory in context subclasses.
                   postProcessBeanFactory(beanFactory);
                   // Invoke factory processors registered as beans in the context.
                   invokeBeanFactoryPostProcessors(beanFactory);
                   // Register bean processors that intercept bean creation.
                   registerBeanPostProcessors(beanFactory);
                   // Initialize message source for this context.
                   initMessageSource();
                   // Initialize event multicaster for this context.
                   initApplicationEventMulticaster();
                   // Initialize other special beans in specific context subclasses.
                   onRefresh();
                   // Check for listener beans and register them.
                   registerListeners();
                   // Instantiate all remaining (non-lazy-init) singletons.
                   finishBeanFactoryInitialization(beanFactory);
                   // Last step: publish corresponding event.
                   finishRefresh();
                }catch (BeansException ex) {
                   if (logger.isWarnEnabled()) {
                      logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                   }
                   // Destroy already created singletons to avoid dangling resources.
                   destroyBeans();
                   // Reset 'active' flag.
                   cancelRefresh(ex);
                   // Propagate exception to caller.
                   throw ex;
                }finally {
                   // Reset common introspection caches in Spring's core, since we
                   // might not ever need metadata for singleton beans anymore...
                   resetCommonCaches();
                }
             }
          }

          由此可知,就是一個(gè)spring的bean的加載過程。繼續(xù)來(lái)看一個(gè)方法叫做 onRefresh():

          protected void onRefresh() throws BeansException {
             // For subclasses: do nothing by default.
          }

          他在這里并沒有直接實(shí)現(xiàn),但是我們找他的具體實(shí)現(xiàn):

          比如Tomcat跟web有關(guān),我們可以看到有個(gè)ServletWebServerApplicationContext:

          @Override
          protected void onRefresh() {
             super.onRefresh();
             try {
                createWebServer();
             }
             catch (Throwable ex) {
                throw new ApplicationContextException("Unable to start web server", ex);
             }
          }

          可以看到有一個(gè)createWebServer();方法他是創(chuàng)建web容器的,而Tomcat不就是web容器,那是如何創(chuàng)建的呢,我們繼續(xù)看:

          private void createWebServer() {
             WebServer webServer = this.webServer;
             ServletContext servletContext = getServletContext();
             if (webServer == null && servletContext == null) {
                ServletWebServerFactory factory = getWebServerFactory();
                this.webServer = factory.getWebServer(getSelfInitializer());
             }
             else if (servletContext != null) {
                try {
                   getSelfInitializer().onStartup(servletContext);
                }
                catch (ServletException ex) {
                   throw new ApplicationContextException("Cannot initialize servlet context",
                         ex);
                }
             }
             initPropertySources();
          }

          factory.getWebServer(getSelfInitializer());他是通過工廠的方式創(chuàng)建的。

          public interface ServletWebServerFactory {
             WebServer getWebServer(ServletContextInitializer... initializers);
          }

          可以看到 它是一個(gè)接口,為什么會(huì)是接口。因?yàn)槲覀儾恢故荰omcat一種web容器。

          我們看到還有Jetty,那我們來(lái)看TomcatServletWebServerFactory:

          @Override
          public WebServer getWebServer(ServletContextInitializer... initializers) {
             Tomcat tomcat = new Tomcat();
             File baseDir = (this.baseDirectory != null) ? this.baseDirectory
                   : createTempDir("tomcat");
             tomcat.setBaseDir(baseDir.getAbsolutePath());
             Connector connector = new Connector(this.protocol);
             tomcat.getService().addConnector(connector);
             customizeConnector(connector);
             tomcat.setConnector(connector);
             tomcat.getHost().setAutoDeploy(false);
             configureEngine(tomcat.getEngine());
             for (Connector additionalConnector : this.additionalTomcatConnectors) {
                tomcat.getService().addConnector(additionalConnector);
             }
             prepareContext(tomcat.getHost(), initializers);
             return getTomcatWebServer(tomcat);
          }

          那這塊代碼,就是我們要尋找的內(nèi)置Tomcat,在這個(gè)過程當(dāng)中,我們可以看到創(chuàng)建Tomcat的一個(gè)流程。


          如果不明白的話, 我們?cè)谟昧硪环N方式來(lái)理解下,大家要應(yīng)該都知道stater舉點(diǎn)例子。

          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-data-redis</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-freemarker</artifactId>
          </dependency>

          首先自定義一個(gè)stater。

          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.1.4.RELEASE</version>
              <relativePath/>
          </parent>
          <groupId>com.zgw</groupId>
          <artifactId>gw-spring-boot-starter</artifactId>
          <version>1.0-SNAPSHOT</version>

          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-autoconfigure</artifactId>
              </dependency>
          </dependencies>

          我們先來(lái)看maven配置寫入版本號(hào),如果自定義一個(gè)stater的話必須依賴spring-boot-autoconfigure這個(gè)包,我們先看下項(xiàng)目目錄。

          public class GwServiceImpl  implements GwService{
              @Autowired
              GwProperties properties;

              @Override
              public void Hello()
              
          {
                  String name=properties.getName();
                  System.out.println(name+"說:你們好啊");
              }
          }

          我們做的就是通過配置文件來(lái)定制name這個(gè)是具體實(shí)現(xiàn)。

          @Component
          @ConfigurationProperties(prefix = "spring.gwname")
          public class GwProperties {

              String name="zgw";

              public String getName() {
                  return name;
              }

              public void setName(String name) {
                  this.name = name;
              }
          }

          這個(gè)類可以通過@ConfigurationProperties讀取配置文件。

          @Configuration
          @ConditionalOnClass(GwService.class)  //掃描類
          @EnableConfigurationProperties(GwProperties.class) //讓配置類生效
          public class GwAutoConfiguration 
          {

              /**
              * 功能描述 托管給spring
              * @author zgw
              * @return
              */

              @Bean
              @ConditionalOnMissingBean
              public GwService gwService()
              
          {
                  return new GwServiceImpl();
              }
          }

          這個(gè)為配置類,為什么這么寫因?yàn)?,spring-boot的stater都是這么寫的,我們可以參照他仿寫stater,以達(dá)到自動(dòng)配置的目的,然后我們?cè)谕ㄟ^

          spring.factories也來(lái)進(jìn)行配置。

          org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration
          然后這樣一個(gè)簡(jiǎn)單的stater就完成了,然后可以進(jìn)行maven的打包,在其他項(xiàng)目引入就可以使用。

          推薦閱讀

          1、搞定數(shù)據(jù)庫(kù)索引,不怕面試官問了!

          2、Spring Boot+Vue項(xiàng)目實(shí)戰(zhàn)

          3、一文搞懂前后端分離

          4、快速上手Spring Boot+Vue前后端分離


          楠哥簡(jiǎn)介

          資深 Java 工程師,微信號(hào) southwindss

          《Java零基礎(chǔ)實(shí)戰(zhàn)》一書作者

          騰訊課程官方 Java 面試官今日頭條認(rèn)證大V

          GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)

          致力于幫助萬(wàn)千 Java 學(xué)習(xí)者持續(xù)成長(zhǎng)。




          有收獲,就點(diǎn)個(gè)在看 
          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天草天天干天天射 | 亚洲色图 欧美 | 日本黄色片网站视频 | 欧美三级又粗又硬 | 欧美一级黄色网 |