10000 字講清楚 Spring Boot 注解原理
今日推薦
@SpringBootApplication
public?class?StartEurekaApplication
{
????public?static?void?main(String[]?args)
????{
????????SpringApplication.run(StartEurekaApplication.class,?args);
????}
}
點(diǎn)進(jìn)@SpringBootApplication來看,發(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?{
}
首先我們先來看 @SpringBootConfiguration:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public?@interface?SpringBootConfiguration?{
}
可以看到這個(gè)注解除了元注解以外,就只有一個(gè)@Configuration,那也就是說這個(gè)注解相當(dāng)于@Configuration,所以這兩個(gè)注解作用是一樣的,它讓我們能夠去注冊一些額外的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ù)來看下一個(gè)@EnableAutoConfiguration,這個(gè)注解是開啟自動配置的功能。
@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,他是說:讓包中的類以及子包中的類能夠被自動掃描到spring容器中。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public?@interface?AutoConfigurationPackage?{
}
使用@Import來給Spring容器中導(dǎo)入一個(gè)組件 ,這里導(dǎo)入的是Registrar.class。來看下這個(gè)Registrar:
static?class?Registrar?implements?ImportBeanDefinitionRegistrar,?DeterminableImports?{
????????Registrar()?{
????????}
????????public?void?registerBeanDefinitions(AnnotationMetadata?metadata,?BeanDefinitionRegistry?registry)?{
????????????AutoConfigurationPackages.register(registry,?(new?AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
????????}
????????public?Set{
????????????return?Collections.singleton(new?AutoConfigurationPackages.PackageImport(metadata));
????????}
????}
就是通過以上這個(gè)方法獲取掃描的包路徑,可以debug查看具體的值:

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

其實(shí)就是將主配置類(即@SpringBootApplication標(biāo)注的類)的所在包及子包里面所有組件掃描加載到Spring容器。因此我們要把DemoApplication放在項(xiàng)目的最高級中(最外層目錄)。
看看注解@Import(AutoConfigurationImportSelector.class),@Import注解就是給Spring容器中導(dǎo)入一些組件,這里傳入了一個(gè)組件的選擇器:AutoConfigurationImportSelector。

可以從圖中看出AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector,ImportSelector有一個(gè)方法為:selectImports。將所有需要導(dǎo)入的組件以全類名的方式返回,這些組件就會被添加到容器中。
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());
????}
}
會給容器中導(dǎo)入非常多的自動配置類(xxxAutoConfiguration);就是給容器中導(dǎo)入這個(gè)場景需要的所有組件,并配置好這些組件。

有了自動配置類,免去了我們手動編寫配置注入功能組件等的工作。那是如何獲取到這些配置類的呢,看看下面這個(gè)方法:
protected?AutoConfigurationImportSelector.AutoConfigurationEntry?
??getAutoConfigurationEntry(AutoConfigurationMetadata?autoConfigurationMetadata,?AnnotationMetadata?annotationMetadata)?{
????if?(!this.isEnabled(annotationMetadata))?{
????????return?EMPTY_ENTRY;
????}?else?{
????????AnnotationAttributes?attributes?=?this.getAttributes(annotationMetadata);
????????List?configurations?=?this.getCandidateConfigurations(annotationMetadata,?attributes);
????????configurations?=?this.removeDuplicates(configurations);
????????Set?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?getCandidateConfigurations(AnnotationMetadata?metadata,?AnnotationAttributes?attributes)? {
????List?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?loadFactoryNames(Class>?factoryClass,?@Nullable?ClassLoader?classLoader)? {
????String?factoryClassName?=?factoryClass.getName();
????return?(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,?Collections.emptyList());
}
會從META-INF/spring.factories中獲取資源,然后通過Properties加載資源:
private?static?Map>?loadSpringFactories(@Nullable?ClassLoader?classLoader)?{
????MultiValueMap?result?=?(MultiValueMap)cache.get(classLoader);
????if?(result?!=?null)?{
????????return?result;
????}?else?{
????????try?{
????????????Enumeration?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?????????????????????????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在啟動的時(shí)候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值作為自動配置類導(dǎo)入到容器中,自動配置類就生效,幫我們進(jìn)行自動配置工作。以前我們需要自己配置的東西,自動配置類都幫我們完成了。如下圖可以發(fā)現(xiàn)Spring常見的一些類已經(jīng)自動導(dǎo)入。

接下來看@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)備好,具體是否使用取決于我們的程序或者說配置。
接下來繼續(xù)看run方法:
public?static?void?main(String[]?args)?{
????????SpringApplication.run(Application.class,?args);
????}
來看下在執(zhí)行run方法到底有沒有用到哪些自動配置的東西,我們點(diǎn)進(jìn)run:
public?ConfigurableApplicationContext?run(String...?args)?{
????//計(jì)時(shí)器
????StopWatch?stopWatch?=?new?StopWatch();
????stopWatch.start();
????ConfigurableApplicationContext?context?=?null;
????Collection?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)來看。
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.class,?applicationContext);
???((AbstractApplicationContext)?applicationContext).refresh();
}
他會調(diào)用 ((AbstractApplicationContext) applicationContext).refresh();方法,我們點(diǎn)進(jìn)來看:
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ù)來看一個(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è)接口,為什么會是接口。因?yàn)槲覀儾恢故荰omcat一種web容器。

我們看到還有Jetty,那我們來看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è)流程。
如果不明白的話, 我們在用另一種方式來理解下,大家要應(yīng)該都知道stater舉點(diǎn)例子。
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
首先自定義一個(gè)stater。
<parent>
????<groupId>org.springframework.bootgroupId>
????<artifactId>spring-boot-starter-parentartifactId>
????<version>2.1.4.RELEASEversion>
????<relativePath/>
parent>
<groupId>com.zgwgroupId>
<artifactId>gw-spring-boot-starterartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
????<dependency>
????????<groupId>org.springframework.bootgroupId>
????????<artifactId>spring-boot-autoconfigureartifactId>
????dependency>
dependencies>
我們先來看maven配置寫入版本號,如果自定義一個(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+"說:你們好啊");
????}
}
我們做的就是通過配置文件來定制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á)到自動配置的目的,然后我們在通過spring.factories也來進(jìn)行配置。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration
鏈接:cnblogs.com/cmt/p/14553189.html
推薦文章
1、一款高顏值的 SpringBoot+JPA 博客項(xiàng)目 2、超優(yōu) Vue+Element+Spring 中后端解決方案 3、推薦幾個(gè)支付項(xiàng)目! 4、推薦一個(gè) Java 企業(yè)信息化系統(tǒng) 5、一款基于 Spring Boot 的現(xiàn)代化社區(qū)(論壇/問答/社交網(wǎng)絡(luò)/博客)
