spring boot如何抛弃tomcat容器
我们在上一篇文章 spring boot单元测试 中提到了spring单元测试为SpringApplication指定spring容器实现类,从而达到抛弃tomcat容器的目的,我们这篇文章的目就是反其道而行,让spring boot抛弃tomcat容器。但是有些地方确实需要这么做,我们想利用spring boot为我们提供的便利,但是又不希望引入sevlet容器。解决方法网上提供了很多方法,..
我们在上一篇文章 spring boot单元测试 中提到了spring单元测试为SpringApplication
指定spring容器实现类,从而达到抛弃tomcat容器的目的,我们这篇文章的目就是反其道而行,让spring boot抛弃tomcat容器。但是有些地方确实需要这么做,我们想利用spring boot为我们提供的便利,但是又不希望引入sevlet容器。
解决方法
网上提供了很多方法,无外乎是去掉 servlet
或者 spring-web
jar包依赖,如果我们项目中不需要这些倒也无所谓,如果模块之间有依赖怎么办,这将是个很麻烦的事。在此,我提供了一种更简单有效的办法,不需要去掉额外的依赖,废话不多说,先上代码:
@SpringBootApplication
public class NoneTomcatApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication application = new SpringApplication( NoneTomcatApplication.class );
// 如果是web环境,默认创建AnnotationConfigEmbeddedWebApplicationContext,因此要指定applicationContextClass属性
application.setApplicationContextClass( AnnotationConfigApplicationContext.class );
application.run( args );
// 如果不想让spring容器退出,可以使用以下代码
CountDownLatch latch = new CountDownLatch( 1 );
latch.await();
}
}
代码如上所示,我们为SpringApplication
指定初始化的spring容器实现类AnnotationConfigApplicationContext
,这样spring boot便不会为我们创建servlet
容器了。spring boot在创建spring容器时,会判断当前classpath是否存在javax.servlet.Servlet
、org.springframework.web.context.ConfigurableWebApplicationContext
,如果同时存在,则默认会初始化可以内嵌servlet
容器的AnnotationConfigEmbeddedWebApplicationContext
。如果我们为spring boot指定spring容器的实现类时,spring boot便老老实实地为我们创建指定的spring容器
下面,我们分析下spring boot相关的源代码
spring boot源码分析
SpringApplication#run()
关键代码如下所示,包括了SpringApplicationRunListeners
事件通知、创建Environment
、打印Banner
文字、创建spring容器、准备上下文、刷新spring容器完成初始化,等等
public ConfigurableApplicationContext run(String... args) {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
}
创建spring容器的代码如下所示,其中webEnvironment
属性在SpringApplication
构造函数中便会进行自动检测,默认情况下是不指定applicationContextClass
属性的,因此会根据webEnvironment
属性来选择applicationContextClass
,非web环境下,创建AnnotationConfigApplicationContext
,web环境下创建AnnotationConfigEmbeddedWebApplicationContext
,如果我们指定,则会按照我们指定的class创建spring容器。因此,在实际的项目中,如果我们不想使用spring boot提供的内嵌servlet容器,只需要指定applicationContextClass
属性就好了,当然排除相关的jar包也是一种方法,不过比较麻烦。具体的代码,见前面的代码
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
下图是ApplicationContext
和BeanFactory
相关的类图,红色框内是上面提到的两个重要实现,spring容器在完成一系列的准备工作之后,会调用onRefresh
方法用于ApplicationContext
子类初始化额外的bean,这是个模板方法,不同spring容器可以实现自己的逻辑,便于扩展实现
由上图可知,AnnotationConfigEmbeddedWebApplicationContext
继承了 EmbeddedWebApplicationContext
,其onRefresh
方法的主要逻辑如下所示(省略了异常处理):
@Override
protected void onRefresh() {
super.onRefresh();
// 创建servlet容器
createEmbeddedServletContainer();
}
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
// 完成servlet容器的创建、启动
if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
getSelfInitializer().onStartup(localServletContext);
}
initPropertySources();
}
我们再来看看 AnnotationConfigApplicationContext
,在 onRefresh
时没有任何处理。两者的对比很明显了,前者会为我们启动servlet容器,后者do nothing
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
值得一提的,spring boot 2.0 移除了 AnnotationConfigEmbeddedWebApplicationContext
,使用 AnnotationConfigServletWebServerApplicationContext
替代,将原有的 org.springframework.boot.context.embedded.EmbeddedServletContainer
抽象成 org.springframework.boot.web.server.WebServer
,完全脱离了servlet环境的束缚
更多推荐
所有评论(0)