手写springboot内嵌tomcat启动

外置和内嵌tomcat区别
外置的tomcat:
tomcat启动加载web.xml,通过web.xml配置初始化spring容器,并加载dispatcherservlet
内嵌tomcat:
springboot启动,初始化spring容器加载bean,然后启动内嵌tomcat,通过servlet3.1规范的 ServletContainerInitializer加载dispatcherservlet

代码示例及流程说明
我会一边贴出代码,一边解释下部分代码

git地址:https://gitee.com/chuanxin1123/yboot

项目结构:
在这里插入图片描述

首先是maven依赖:

org.springframework spring-webmvc 5.2.7.RELEASE org.apache.tomcat.embed tomcat-embed-jasper 8.5.23 那么我们在这里分析一下springboot启动流程:

1.初始化ioc

为什么要先初始化ioc,而不是先启动tomcat?因为ioc是必须的,而tomcat可以被其他中间件替换

2.启动tomcat

3.将dispatcherServlet放入到ioc容器,并加载到tomcat上下文中去

那么我们先实现第一步:初始化ioc

@ComponentScan(“com.chuan”)
public class StartApplication {
public static void main(String[] args) throws Exception{
//1.初始化ioc,
// 为什么要先初始化ioc,而不是先启动tomcat,因为ioc是必须的,而tomcat可以被其他中间件替换
AnnotationConfigWebApplicationContext ac=new AnnotationConfigWebApplicationContext();
ac.register(StartApplication.class); //手动去注册一个bean
ac.refresh(); //刷新

}

}
然后将dispatcherServlet注入到ioc中,这里为什么不能写在主方法中?因为写在主方法中就是强引用了,必须要使用servlet方式处理并且必须是web项目了。

@Configuration
public class MyConfig {
@Bean
public DispatcherServlet getDispatcherServlet(){
return new DispatcherServlet();
}

}
实现第二步:启动tomcat,写一个接口和tomcat实现类

public interface WebServerFactory {
//使用接口增加了扩展性,不一定非要用tomcat
void createServer() throws Exception;
}
@Component
public class MyTomcat implements WebServerFactory{
public void createServer() throws Exception {
Tomcat tomcat=new Tomcat();
tomcat.setPort(8081);
tomcat.addWebapp("/",“D://soft”);
tomcat.start();
//因为 tomcat.start();是非阻塞型的,所以要阻塞一下,不能让服务停止。
tomcat.getServer().await();
}
}
然后修改主方法,调用tomcat启动

@ComponentScan(“com.chuan”)
public class StartApplication {
public static void main(String[] args) throws Exception{
//1.初始化ioc,
// 为什么要先初始化ioc,而不是先启动tomcat,因为ioc是必须的,而tomcat可以被其他中间件替换
AnnotationConfigWebApplicationContext ac=new AnnotationConfigWebApplicationContext();
ac.register(StartApplication.class); //手动去注册一个bean
ac.refresh(); //刷新
//2.启动tomcat
WebServerFactory factory = ac.getBean(WebServerFactory.class);
factory.createServer();
//3.将dispatcherServlet放入到ioc容器,并加载到tomcat上下文中去
//注意dispatcherServlet不能放在启动里,因为也可以被reactive响应式编程替换掉servlet,也可以不是web项目

}

}
但现在tomcat和dispatcherServlet并没有关联起来,需要进行第三步操作:将dispatcherServlet放入到ioc容器,并加载到tomcat上下文中去

创建一个servlet作为顶层servlet,实现了一个ApplicationContextAware 接口。

ApplicationContextAware接口中setApplicationContext方法让Spring容器传递自己生成的ApplicationContext给我们使用,然后通过ac获取到DispatcherServlet的实例。

@Component
public class MyServlet implements ApplicationContextAware {
private static ApplicationContext context;

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    context=applicationContext;
}
//从ac中获取实例
public static DispatcherServlet getDispatcherServlet(){
    return context.getBean(DispatcherServlet.class);
}

}
然后通过子类将DispatcherServlet添加到tomcat上下文中去。

这里有两种写法:

1.实现WebApplicationInitializer接口(springboot封装了ServletContainerInitializer接口)

2.实现ServletContainerInitializer接口(spi3.0规范)

从servlet3.0开始,web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。每个框架要使
用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的
ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。
实现了onStartup后,在运行中会自动执行该方法,也正是该方法替代了spring中的web.xml,实现了无配置。

写法1:

public class MyServletInit extends MyServlet implements WebApplicationInitializer {

public void onStartup(ServletContext servletContext) throws ServletException {
    ServletRegistration.Dynamic dynamic =servletContext.addServlet("MyDispatcherServlet",super.getDispatcherServlet());
    dynamic.setLoadOnStartup(1);
    dynamic.addMapping("/");
}

}
写法2:

public class MyServletInit extends MyServlet implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic dynamic =servletContext.addServlet(“MyDispatcherServlet”,super.getDispatcherServlet());
dynamic.setLoadOnStartup(1);
dynamic.addMapping("/");
}
}
但需要多出一步,在resources下创建一个META-INF/services目录(不是一个叫META-INF.services的文件夹),在该目录下创建一个文件名为javax.servlet.ServletContainerInitializer的文件,里面内容为该实现类的全路径(我的spi3.0实现是简写了)

com.chuan.config.MyServletInit
最后写一个controller启动:

@RestController
public class MyController {
@RequestMapping("/test")
public String test(){
return “test”;
}
}
结果:启动成功,并且DispatcherServlet功能也正常。
在这里插入图片描述

什么替换了web.xml
比如通过web.xml方式启动:

org.springframework.web.context.ContextLoaderListener contextConfigLocation /WEB-INF/applicationContext.xml app org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springmvc.xml 1 app /* 其中的ContextLoaderListener可以被下面代码替代

AnnotationConfigWebApplicationContext ac=new AnnotationConfigWebApplicationContext();
ac.register(StartApplication.class); //手动去注册一个bean
ac.refresh();
而DispatcherServlet则可以被下面代码替代

ServletRegistration.Dynamic dynamic =servletContext.addServlet(“MyDispatcherServlet”,super.getDispatcherServlet());
dynamic.setLoadOnStartup(1);
dynamic.addMapping("/");

————————————————
版权声明:本文为CSDN博主「干了这杯柠檬多」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39404258/article/details/108464255

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值