springboot 基于Tomcate的自启动流程
Springboot 内置了Tomcat的容器,我们今天来说一下Springboot的自启动流程。
一、Spring通过注解导入Bean大体可分为四种方式,我们主要来说以下Import的两种实现方法:

1、通过实现ImportSerlector接口,实现Bean加载:
public class TestServiceImpl {
public void testImpl() {
System.out.println("我是通过importSelector导入进来的service");
}
}
public class TestService implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.ycdhz.service.TestServiceImpl"};
}
}
@Configuration
@Import(value = {TestService.class})
public class TestConfig {
}
public class TestController {
@Autowired
private TestServiceImpl testServiceImpl;
@RequestMapping("testImpl")
public String testTuling() {
testServiceImpl.testImpl();
return "Ok";
}
}
2、 通过实现ImportBeanDefinitionRegistrar接口,实现Bean加载:
public class TestService {
public TestService() {
System.out.println("我是通过ImportBeanDefinitionRegistrar导入进来的组件");
}
}
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//定义一个BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestService.class);
//把自定义的bean定义导入到容器中
registry.registerBeanDefinition("testService",beanDefinition);
}
}
@Configuration
@Import(TestImportBeanDefinitionRegistrar.class)
public class TestConfig {
}
二、 Springboot启动过程中会自动装配,我们从spring-boot-autoconfigure-2.0.6.RELEASE.jar下搜索到Tomcat的相关配置,发现有两个自动装配类,分别包含了三个定制器(面向对象的单一职责原则),还有一个工厂类。

2.1、TomcatWebServerFactoryCustomizer:定制Servlet和Reactive服务器通用的Tomcat特定功能。
public class TomcatWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
@Override
public void customize(ConfigurableTomcatWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
PropertyMapper propertyMapper = PropertyMapper.get();
propertyMapper.from(tomcatProperties::getBasedir).whenNonNull()
.to(factory::setBaseDirectory);
propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull()
.as(Duration::getSeconds).as(Long::intValue)
.to(factory::setBackgroundProcessorDelay);
customizeRemoteIpValve(factory);
propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive)
.to((maxThreads) -> customizeMaxThreads(factory,
tomcatProperties.getMaxThreads()));
propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive)
.to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
propertyMapper.from(() -> determineMaxHttpHeaderSize()).when(this::isPositive)
.to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
maxHttpHeaderSize));
propertyMapper.from(tomcatProperties::getMaxHttpPostSize)
.when((maxHttpPostSize) -> maxHttpPostSize != 0)
.to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
maxHttpPostSize));
propertyMapper.from(tomcatProperties::getAccesslog)
.when(ServerProperties.Tomcat.Accesslog::isEnabled)
.to((enabled) -> customizeAccessLog(factory));
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull()
.to(factory::setUriEncoding);
propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
.to((connectionTimeout) -> customizeConnectionTimeout(factory,
connectionTimeout));
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
customizeStaticResources(factory);
customizeErrorReportValve(properties.getError(), factory);
}
}
2.2、ServletWebServerFactoryCustomizer:WebServerFactoryCustomizer 将ServerProperties属性应用于Tomcat web服务器。
public class ServletWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered { private final ServerProperties serverProperties; public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
} @Override
public int getOrder() {
return 0;
} @Override
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.serverProperties::getPort).to(factory::setPort);
map.from(this.serverProperties::getAddress).to(factory::setAddress);
map.from(this.serverProperties.getServlet()::getContextPath)
.to(factory::setContextPath);
map.from(this.serverProperties.getServlet()::getApplicationDisplayName)
.to(factory::setDisplayName);
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters)
.to(factory::setInitParameters);
}
}
2.3、ServletWebServerFactoryCustomizer :WebServerFactoryCustomizer 将ServerProperties属性应用于Tomcat web服务器。
public class TomcatServletWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered { private final ServerProperties serverProperties; public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this.serverProperties = serverProperties;
} @Override
public void customize(TomcatServletWebServerFactory factory) {
ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
factory.getTldSkipPatterns()
.addAll(tomcatProperties.getAdditionalTldSkipPatterns());
}
if (tomcatProperties.getRedirectContextRoot() != null) {
customizeRedirectContextRoot(factory,
tomcatProperties.getRedirectContextRoot());
}
if (tomcatProperties.getUseRelativeRedirects() != null) {
customizeUseRelativeRedirects(factory,
tomcatProperties.getUseRelativeRedirects());
}
}
}
三、有了TomcatServletWebServerFactory,相当于有了Spring加载的入口,通过AbstractApplicationContext#onReFresh()在IOC 容器中的带动tomcat启动,然后在接着执行 ioc容器的其他步骤。我们通过断点可以观察Tomcat加载的整个生命周期,以及三个定制器的加载过程。

@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);
//创建Tomcat引擎
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
//刷新上下文
prepareContext(tomcat.getHost(), initializers);
//准备启动
return getTomcatWebServer(tomcat);
}
private void initialize() throws WebServerException {
TomcatWebServer.logger
.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource())
&& Lifecycle.START_EVENT.equals(event.getType())) {
// Remove service connectors so that protocol binding doesn't
// happen when the service is started.
removeServiceConnectors();
}
});
// Start the server to trigger initialization listeners
this.tomcat.start();
// We can re-throw failure exception directly in the main thread
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(),
getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
备注: 在这个过程中我们需要了解Bean的生命周期,Tomcat的三个定制器均在BeanPostProcessorsRegistrar(Bean后置处理器)过程中加载;
构造方法-->Bean后置处理器Before-->InitializingBean-->init-method-->Bean后置处理器After
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//构造方法
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
} // Initialize the bean instance.
......
return exposedObject;
} protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
} Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//Bean后置处理器Before
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
} try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//Bean后置处理器After
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
} return wrappedBean;
}
ImportBeanDefinitionRegistrar
springboot 基于Tomcate的自启动流程的更多相关文章
- SpringBoot启动原理及相关流程
一.springboot启动原理及相关流程概览 springboot是基于spring的新型的轻量级框架,最厉害的地方当属自动配置.那我们就可以根据启动流程和相关原理来看看,如何实现传奇的自动配置 二 ...
- SpringBoot基于数据库实现简单的分布式锁
本文介绍SpringBoot基于数据库实现简单的分布式锁. 1.简介 分布式锁的方式有很多种,通常方案有: 基于mysql数据库 基于redis 基于ZooKeeper 网上的实现方式有很多,本文主要 ...
- springboot 基于JS-SDK实现微信分享(一)
本文主要介绍了SpringBoot 基于JS-SDK实现自定义微信分享,并通过本地测试的方式进行调试,文中通过微信实现分享流程及示例代码进行非常详细的介绍,希望本文对开发爱好者学习或者工作具有一定的参 ...
- 10.spring-boot基于角色的权限管理页面实现
10.spring-boot基于角色的权限管理页面实现
- 为某金融企业的IT技术部人员提供基于TFS的软件研发流程介绍
受莫金融企业IT信息技术部的邀请,为该金融企业的某省分公司.地市分公司的IT技术人员提供了一场基于TFS的软件研发流程的技术培训,希望可以借此提高该企业的软件研发.运维水平,同时推动企业软件研发信息化 ...
- SpringBoot基于websocket的网页聊天
一.入门简介正常聊天程序需要使用消息组件ActiveMQ或者Kafka等,这里是一个Websocket入门程序. 有人有疑问这个技术有什么作用,为什么要有它?其实我们虽然有http协议,但是它有一个缺 ...
- SpringBoot 基于web应用开发(请求参数获取,静态资源,webjars)
SpringBoot 基于web应用开发 一.Lombok使用 1.导入依赖库 <dependency> <groupId>org.projectlombok</g ...
- springboot基于CORS处理跨域问题
1. 为什么有跨域问题 跨域不一定都会有跨域问题. 因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击. 因此:跨域问 ...
- springboot 基于@Scheduled注解 实现定时任务
前言 使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式: 一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer) 前者相信大家都很熟悉, ...
随机推荐
- PTA - 堆栈模拟队列
设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q. 所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数: int IsFull(Stack S):判断堆栈S是否已满,返回1或0: in ...
- spring boot+spring security 使用随笔
本人最近接受的新任务是从零搭一个管理系统的权限管理模块,从零开始学习了spring security,已完成绝大部分设计和开发,和大家分享一些学习和开发心得. 首先是数据库的设计,这里是 按照产品的需 ...
- latex一些有用的写法
编辑博文的时候总是忘语法,然后到网上查-- 干脆记一下! 1.编辑漂亮的函数上下标 \(\sum\limits_{i=1}^n\) 对于原有的函数:$\sum\limits_{i=1}^n$ \(\m ...
- 60 个让程序员崩溃的瞬间,太TM真实了
前方高能!笑死人不偿命系列~ 表演即将开始,吃东西的请停下来,不然你会后悔的 1. 公司实习生找 Bug 2. 在调试时,将断点设置在错误的位置 3. 当我有一个很棒的调试想法时 4. 偶然间看到自己 ...
- 【java面试】Web篇
1.AJAX创建步骤 step1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象: step2. 创建一个新的HTTP请求,并指定改HTTP请求的方法.URL以及验证信息: s ...
- 常用API_1
API API(Application Programming Interface),应用程序编程接口.Java API是一本程序员的 字典 ,是JDK中提供给 我们使用的类的说明文档.这些类将底层的 ...
- django cms 5月第一弹
官方文档: ##http://django-cms.readthedocs.io/en/latest/index.html #截图 #生存的项目结构
- SEO 搜索 形成一个关联
- 数据结构——栈的应用 NOI2.2 括号匹配问题
栈是一种数据结构,相当于一个容器,将一个又一个变量从顶端压进去,需要使用时,又从顶端拿出来,其具体使用方法,下面是详细讲解: #include<stack>必须使用此头文件 stack&l ...
- apache和nginx那点事儿--阻塞和异步
先明白的事儿:当一个程序在执行的时候,一般会创建一个进程,也可以有多个进程.一个进程至少会创建一个线程,多个线程共享一个程序进程的内存.程序的运行最终是靠线程来完成操作的.线程的数量跟CPU核数有关, ...