前言
我們知道開發(fā)spring boot項目,在啟動類上添加注解@SpringBootApplication ,然后引入要自動注入的組件依賴,然后現(xiàn)application.properties中加上相應配置就可以自動注入這個組件,那么下面看看自動注入組件是如何實現(xiàn)的
一、@SpringBootApplication 注解
1、查看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 {
}
2、查看@EnableAutoConfiguration類
@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 {};
}
這個類又通過@Import({AutoConfigurationImportSelector.class}) 導入了
3、AutoConfigurationImportSelector這個bean,查看這個bean
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
}
4、這個AutoConfigurationImportSelector類繼承了DeferredImportSelector最終繼承了ImportSelector,重寫這個類的selectImports方法可以快速導入一個bean,查看selectImports方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
5、查看List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
這個方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> 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;
}
6、這個方法最終會調(diào)用loadSpringFactories方法,這個方法把META-INF/spring.factories定義的類全部讀到出來
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
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()) {
Entry<?, ?> entry = (Entry)var6.next();
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
7、最終spring會根據(jù)這些組件中定義的注入條件將這些組件自動注入,org.springframework.boot.autoconfigure下放了所有自動注入的組件,以aop這個組件為例:
@Configuration
//條件注入,當有 `EnableAspectJAutoProxy.class, Aspect.class, Advice.class,`這些class存在時才注入,也就是說當引入相關依賴包時注入
AnnotatedElement.class
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
AnnotatedElement.class })
//當配置文件中有spring.aop 配置時才注入
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
二、spring boot內(nèi)嵌tomcat
最簡單的tomcat集成
1、添加pom文件
<dependencies>
<!--Java語言操作tomcat -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.16</version>
</dependency>
<!-- tomcat對jsp支持 -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.16</version>
</dependency>
</dependencies>
2、新建一個servlet文件
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("this is index... tomcat");
}
}
3、新建一個啟動類
public class DTomcat {
private static int PORT = 8080;
private static String CONTEX_PATH = "/clock";
private static String SERVLET_NAME = "indexServlet";
public static void main(String[] args) throws LifecycleException, InterruptedException {
// 創(chuàng)建tomcat服務器
Tomcat tomcatServer = new Tomcat();
// 指定端口號
tomcatServer.setPort(PORT);
// 是否設置自動部署
tomcatServer.getHost().setAutoDeploy(false);
// 創(chuàng)建上下文
StandardContext standardContex = new StandardContext();
standardContex.setPath(CONTEX_PATH);
// 監(jiān)聽上下文
standardContex.addLifecycleListener(new Tomcat.FixContextListener());
// tomcat容器添加standardContex
tomcatServer.getHost().addChild(standardContex);
// 創(chuàng)建Servlet
tomcatServer.addServlet(CONTEX_PATH, SERVLET_NAME, new IndexServlet());
// servleturl映射
standardContex.addServletMappingDecoded("/index", SERVLET_NAME);
tomcatServer.start();
System.out.println("tomcat服務器啟動成功..");
// 異步進行接收請求
tomcatServer.getServer().await();
}
}
4、運行main,在瀏覽器輸入:
http://localhost:8080/clock/index
spring boot內(nèi)嵌tomcat
1、啟動一個spring boot項目,查看控制臺最下的日志:
可以看出spring boot在啟動的時候,啟動一個tomcat,實際上它啟動的方式也是上面那么啟動方式
2、tomcat加載流程
tomcat也是一個組件,那么它的引入方式也是通過spring.factories文件注入的
3、查看ServletWebServerFactoryAutoConfiguration這個類
ServletWebServerFactoryAutoConfiguration這個類用@import快速導入了EmbeddedTomcat類
4、查看EmbeddedTomcat類
這個類注入了TomcatServletWebServerFactory這個bean
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
4、查看TomcatServletWebServerFactory類
這個類有一個getWebServer方法如下:
這個方法啟動了一個tomcat,那么這個方法是在哪個地方調(diào)用的?可以在這個方法上打上斷點,查看它的調(diào)用鏈
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
return this.getTomcatWebServer(tomcat);
}
5、在getWebServer()方法,打斷點,然后啟動spring boot的main方法,查看調(diào)用鏈如下:
6、啟動流程分析
查看main里面的run方法,
這個方法主要new 了一個SpringApplication對象,然后執(zhí)行了run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
SpringApplication結(jié)構(gòu)方法:
加載了相關類,沒有執(zhí)行
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主類
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判斷當前是什么類型項目
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//從類路徑下找到META-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
run方法:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//從類路徑下META‐INF/spring.factories,取得SpringApplicationRunListeners;
SpringApplicationRunListeners listeners = getRunListeners(args);
//回調(diào)所有的獲取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封裝命令行參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//準備環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//創(chuàng)回調(diào)SpringApplicationRunListener.environmentPrepared();
//表示環(huán)境準備完成
//打印Banner
Banner printedBanner = printBanner(environment);
//根據(jù)環(huán)境創(chuàng)建context
context = createApplicationContext();
//錯誤的異常報表
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//準備上下文環(huán)境;
//將environment保存到ioc中;
//applyInitializers()調(diào)用所有的ApplicationContextInitializer的initialize方法
//調(diào)用所有的SpringApplicationRunListener的contextPrepared();
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//SpringApplicationRunListener的contextLoaded
//刷新容器
//掃描,創(chuàng)建,加載所有組件;
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//所有的SpringApplicationRunListener回調(diào)started方法
listeners.started(context);
//獲取所有的ApplicationRunner和CommandLineRunner進行調(diào)用
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//所有的SpringApplicationRunListener的running();
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
三、spring boot內(nèi)嵌tomcat,修改web容器
從spring boot啟動日志看,我們知道spring boot內(nèi)嵌的web容器是tomcat,那么如果我們不想用tomcat 也可以換別的web容器
1、修改pom
排除tomcat,引入undertow容器文章來源:http://www.zghlxwxcb.cn/news/detail-610103.html
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>
這個再啟動spring boot項目用的就是undertow容器文章來源地址http://www.zghlxwxcb.cn/news/detail-610103.html
到了這里,關于spring boot--自動化注入組件原理、內(nèi)嵌tomcat-1的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!