第四十讲-Tomcat内嵌容器

第四十讲-Tomcat内嵌容器

这一讲,我们来学习一下Tomcat内嵌容器。我们首先看一下Tomcat容器的重要组成:

image-20240822112714541

如上面的这张图,展示了Tomcat中一些重要的组件。

  1. Connector:连接器,连接器决定了将来请求是以什么协议,哪个端口来连接到Tomcat服务器上
  2. Engine:执行引擎,负责Tomcat运行

接下来我们通过编程的方式来创建内嵌的Tomcat

public class TestTomcat {
    /*
        Server
        └───Service
            ├───Connector (协议, 端口)
            └───Engine
                └───Host(虚拟主机 localhost)
                    ├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )
                    │   │   index.html
                    │   └───WEB-INF
                    │       │   web.xml (servlet, filter, listener) 3.0
                    │       ├───classes (servlet, controller, service ...)
                    │       ├───jsp
                    │       └───lib (第三方 jar 包)
                    └───Context2 (应用2)
                        │   index.html
                        └───WEB-INF
                                web.xml
     */
    @SuppressWarnings("all")
    public static void main(String[] args) throws LifecycleException, IOException {
        // 1.创建 Tomcat 对象
        Tomcat tomcat = new Tomcat();
        // 设置一个基础目录
        tomcat.setBaseDir("tomcat");

        // 2.创建项目文件夹(此处是临时目录), 即 docBase 文件夹
        File docBase = Files.createTempDirectory("boot.").toFile();
        // 当程序退出时删除该临时目录
        docBase.deleteOnExit();

        // 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context,默认路径为'/',这里只需要写空字符串即可
        Context context = tomcat.addContext("", docBase.getAbsolutePath());

        WebApplicationContext springContext = getApplicationContext();

        // 4.编程添加 Servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
                HelloServlet helloServlet = new HelloServlet();
                // 添加HelloServlet并为此田添加一个映射路径
                ctx.addServlet("aaa", helloServlet).addMapping("/hello");

//                DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
//                ctx.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");
                for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
                    registrationBean.onStartup(ctx);
                }
            }
        }, Collections.emptySet());

        // 5.启动 Tomcat
        tomcat.start();

        // 6.创建连接器, 设置监听端口-->底层使用NIO的HTTP1.1协议
        Connector connector = new Connector(new Http11Nio2Protocol());
        // 设置Tomcat端口号
        connector.setPort(8080);
        // 将连接器设置到Tomcat中
        tomcat.setConnector(connector);
    }
}
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().print("""
                <h3>hello</h3>
                """);
    }
}

启动依赖测试一下:

image-20240822135040514

接下来我们来看一下内嵌Tomcat与Spring是怎么结合在一起使用的?

首先编写一个Spring容器:

public static WebApplicationContext getApplicationContext() {
	// AnnotationConfigServletWebServerApplicationContext 
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    // 设置配置类
    context.register(Config.class);
    // 完成容器的初始化
    context.refresh();
    return context;
}

改造相关代码:

context.addServletContainerInitializer(new ServletContainerInitializer() {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        HelloServlet helloServlet = new HelloServlet();
        // 添加HelloServlet并为此田添加一个映射路径
        ctx.addServlet("aaa", helloServlet).addMapping("/hello");

		// DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
		// ctx.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");
        for (ServletRegistrationBean registrationBean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
            registrationBean.onStartup(ctx);
        }
    }
}, Collections.emptySet());

配置类如下:

@Configuration
static class Config {
    // DispatcherServlet Register Bean
    @Bean
    public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
        return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
    }

    // Dispatcher Servlet Bean
    @Bean
    // 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现
    public DispatcherServlet dispatcherServlet(WebApplicationContext applicationContext) {
        return new DispatcherServlet(applicationContext);
    }

    // 处理器适配器,并添加一个JSON格式消息转换器
    @Bean
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
        handlerAdapter.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
        return handlerAdapter;
    }

    // 一个简单的控制器
    @RestController
    static class MyController {
        @GetMapping("hello2")
        public Map<String,Object> hello() {
            return Map.of("hello2", "hello2, spring!");
        }
    }
}

测试运行如下:

image-20240822135846435

posted @   LilyFlower  阅读(4)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示