摘要:我們來看可以看到他繼承了我們繼續(xù)來看,這個類有一個方法這個類會幫你掃描那些類自動去添加到程序當中。我們看到還有,那我們來看那這塊代碼,就是我們要尋找的內(nèi)置,在這個過程當中,我們可以看到創(chuàng)建的一個流程。
溫馨提示,文章略長,看完需要耐心!!
今天跟大家來探討下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot為什么不需要XML,達到零配置
首先我們先來看段代碼
@SpringBootApplication
public class StartEurekaApplication
{
public static void main(String[] args)
{
SpringApplication.run(StartEurekaApplication.class, args);
}
}
我們點進@SpringBootApplication來看
@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 {
}
上面的元注解我們在這里不在做解釋,相信大家在開發(fā)當中肯定知道,我們要來說@SpringBootConfiguration @EnableAutoConfiguration 這兩個注解,到這里我們知道 SpringBootApplication注解里除了元注解,我們可以看到又是@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan的組合注解,官網(wǎng)上也有詳細說明,那我們現(xiàn)在把目光投向這三個注解。
首先我們先來看 @SpringBootConfiguration,那我們點進來看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
我們可以看到這個注解除了元注解以外,就只有一個@Configuration,那也就是說這個注解相當于@Configuration,所以這兩個注解作用是一樣的,那他是干嘛的呢,相信很多人都知道,它是讓我們能夠去注冊一些額外的Bean,并且導(dǎo)入一些額外的配置。那@Configuration還有一個作用就是把該類變成一個配置類,不需要額外的XML進行配置。所以@SpringBootConfiguration就相當于@Configuration。
那我們繼續(xù)來看下一個@EnableAutoConfiguration,這個注解官網(wǎng)說是 讓Spring自動去進行一些配置,那我們點進來看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
可以看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)這兩個而組成的,我們先說@AutoConfigurationPackage,他是說:讓包中的類以及子包中的類能夠被自動掃描到spring容器中。 我們來看@Import(EnableAutoConfigurationImportSelector.class)這個是核心,之前我們說自動配置,那他到底幫我們配置了什么,怎么配置的?
就和@Import(EnableAutoConfigurationImportSelector.class)息息相關(guān),程序中默認使用的類就自動幫我們找到。我們來看EnableAutoConfigurationImportSelector.class
public class EnableAutoConfigurationImportSelector
extends AutoConfigurationImportSelector {
@Override
protected boolean isEnabled(AnnotationMetadata metadata) {
if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
return getEnvironment().getProperty(
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
true);
}
return true;
}
}
可以看到他繼承了AutoConfigurationImportSelector我們繼續(xù)來看AutoConfigurationImportSelector,這個類有一個方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
這個類會幫你掃描那些類自動去添加到程序當中。我們可以看到getCandidateConfigurations()這個方法,他的作用就是引入系統(tǒng)已經(jīng)加載好的一些類,到底是那些類呢,我們點進去看一下
protected List getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), 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;
}
這個類回去尋找的一個目錄為META-INF/spring.factories,也就是說他幫你加載讓你去使用也就是在這個META-INF/spring.factories目錄裝配的,他在哪里?
image.png
我們點進spring.factories來看
image.png
image.png
可以看到他都已經(jīng)幫我們引入了進來,我看隨便拿幾個來看
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,
比如我們經(jīng)常用的security,可以看到已經(jīng)幫你配置好,所以我們的EnableAutoConfiguration主要作用就是讓你自動去配置,但并不是所有都是創(chuàng)建好的,是根據(jù)你程序去進行決定。 那我們繼續(xù)來看
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM,
classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
這個注解大家應(yīng)該都不陌生,掃描包,放入spring容器,那他在springboot當中做了什么策略呢?我們可以點跟煙去思考,幫我們做了一個排除策略,他在這里結(jié)合SpringBootConfiguration去使用,為什么是排除,因為不可能一上來全部加載,因為內(nèi)存有限。
那么我們來總結(jié)下@SpringbootApplication:就是說,他已經(jīng)把很多東西準備好,具體是否使用取決于我們的程序或者說配置,那我們到底用不用?那我們繼續(xù)來看一行代碼
public static void main(String[] args)
{
SpringApplication.run(StartEurekaApplication.class, args);
}
那們來看下在執(zhí)行run方法到底有沒有用到哪些自動配置的東西,比如說內(nèi)置的Tomcat,那我們來找找內(nèi)置Tomcat,我們點進run
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return new SpringApplication(sources).run(args);
}
然后他調(diào)用又一個run方法,我們點進來看
public ConfigurableApplicationContext run(String... args) {
//計時器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//監(jiān)聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
//準備上下文
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//預(yù)刷新context
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新context
refreshContext(context);
//刷新之后的context
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
那我們關(guān)注的就是 refreshContext(context); 刷新context,我們點進來看
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
我們繼續(xù)點進refresh(context);
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
他會調(diào)用 ((AbstractApplicationContext) applicationContext).refresh();方法,我們點進來看
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();
}
}
}
這點代碼似曾相識啊 沒錯,就是一個spring的bean的加載過程我在,解析springIOC加載過程的時候介紹過這里面的方法,如果你看過Spring源碼的話 ,應(yīng)該知道這些方法都是做什么的。現(xiàn)在我們不關(guān)心其他的,我們來看一個方法叫做 onRefresh();方法
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
他在這里并沒有實現(xiàn),但是我們找他的其他實現(xiàn),我們來找
image.png
我們既然要找Tomcat那就肯定跟web有關(guān),我們可以看到有個ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
我們可以看到有一個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);
}
可以看到 它是一個接口,為什么會是接口。因為我們不止是Tomcat一種web容器。
image.png
我們看到還有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,在這個過程當中,我們可以看到創(chuàng)建Tomcat的一個流程。因為run方法里面加載的東西很多,所以今天就淺談到這里。如果不明白的話, 我們在用另一種方式來理解下,
大家要應(yīng)該都知道stater舉點例子
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
所以我們不防不定義一個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-sraterartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-autoconfigureartifactId>
dependency>
dependencies>
我們先來看maven配置寫入版本號,如果自定義一個stater的話必須依賴spring-boot-autoconfigure這個包,我們先看下項目目錄
image.png
public class GwServiceImpl implements GwService{
@Autowired
GwProperties properties;
@Override
public void Hello()
{
String name=properties.getName();
System.out.println(name+"說:你們好啊");
}
}
我們做的就是通過配置文件來定制name這個是具體實現(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;
}
}
這個類可以通過@ConfigurationProperties讀取配置文件
@Configuration
@ConditionalOnClass(GwService.class) //掃描類
@EnableConfigurationProperties(GwProperties.class) //讓配置類生效
public class GwAutoConfiguration {
/**
* 功能描述 托管給spring
* @author zgw
* @return
*/
@Bean
@ConditionalOnMissingBean
public GwService gwService()
{
return new GwServiceImpl();
}
}
這個為配置類,為什么這么寫因為,spring-boot的stater都是這么寫的,我們可以參照他仿寫stater,以達到自動配置的目的,然后我們在通過spring.factories也來進行配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration
然后這樣一個簡單的stater就完成了,然后可以進行maven的打包,在其他項目引入就可以使用,在這里列出代碼地址
github.com/zgw14690398…
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/7318.html
摘要:啟動原理和執(zhí)行原理分析一的啟動原理我們打開,注意看下面兩個依賴我們第一步是繼承了父項目,然后在添加啟動器的依賴,項目就會自動給我們導(dǎo)入關(guān)于項目所需要的配置和。 上一篇我們看到,我們很輕松的完成了項目的構(gòu)建,那么SpringBoot是如何做到的呢,在使用的使用又有哪些通用配置和注意事項呢? 其實SpringBoot給我們做了大量的自動配置功能,我們只需要引入對應(yīng)的啟動器就可以直接使用,作...
摘要:問題來了,我們到底還在用嗎答案是,不全用。后者是初始化的配置,主要是的配置。啟動類測試啟動項目后,在瀏覽器里面輸入。通過查詢已裝載的,并且支持該而獲取的。按照前面對的描述,對于而言,這個必定是。的核心在的方法中。 之前已經(jīng)分析過了Spring的IOC(《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》)與AOP(《從源碼入手,一文帶你讀懂Spring AOP面向切面編程》)的源碼,本次...
閱讀 2571·2021-08-20 09:38
閱讀 1355·2019-08-30 15:43
閱讀 593·2019-08-29 17:13
閱讀 1601·2019-08-29 14:01
閱讀 1314·2019-08-29 13:29
閱讀 2322·2019-08-23 18:29
閱讀 2047·2019-08-23 17:51
閱讀 1915·2019-08-23 17:16