• Spring 整合嵌入式 Tomcat 容器


    导入依赖

    description = "Spring Web MVC"
    
    apply plugin: "kotlin"
    dependencies {
        compile(project(":spring-webmvc"))
        compile(group: "org.apache.tomcat.embed",name: "tomcat-embed-core",version: "8.5.64")
        compile(group: "org.apache.tomcat",name: "tomcat-jasper",version: "9.0.50")
        compile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    编写 Tomcat 启动服务

    /**
     * 启动这个 Tomcat 的时候注意版本,有的版本可能半天都跑不起来哦
     */
    public class SpringApplication {
    
    	public static void run() {
    		try {
    			Tomcat tomcat = new Tomcat();
    
    			tomcat.setPort(8888);
    
    			tomcat.setBaseDir("IdeaProjects/spring-framework/spring-test-boot/src/main");
    
    			tomcat.addWebapp("/boot", "IdeaProjects/spring-framework/spring-test-boot/src/main");
    
    			// 第一种方法:给 Tomcat 中注册一个 Servlet 和 ServletMapping
    			// tomcat.addServlet("/boot", "myHttpServlet", new MyHttpServlet()).addMapping("/hello");
    
    			// 第二种方法: 直接给利用 Tomcat SPI 机制,实现父子容器加载,这种方式直接可以写 Controller 了
    			// 就不用上面这么麻烦了还有自己注册 Servlet 参考 QuickStartWebApplicationInitializer 这个类
    
    			tomcat.start();
    
    			tomcat.getServer().await();
    		} catch (LifecycleException e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    实现 Servlet

    可以将 MyHttpServlet 注册到 Tomcat 中,然后访问测试下看效果。

    public class MyHttpServlet extends HttpServlet implements Serializable {
    	/** use serialVersionUID from Spring 1.2 for interoperability. */
    	private static final long serialVersionUID = 7099057708183571937L;
    
    	@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 {
    		System.out.println("doGet........");
    		resp.getWriter().write("

    Hello Tomcat.....

    "
    ); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    通过 Tomcat 中的第一种方式就可以把 Servlet 注册到 Tomcat 中,然后进行访问,效果如下:

    在这里插入图片描述

    Config 配置

    但是第一种注册 Servlet 太麻烦了,第二种方式通过 Tomcat SPI 机制加载配置类来启动 Spring IOC 容器。

    // mvc_tag: 表示我不扫描标有 web 注解的组件
    // 这个就是父容器配置类,父子容器就是这么来的
    @ComponentScan(value = "com.gwm.springboot",excludeFilters = {
    		@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)
    })
    @Configuration
    public class SpringConfig {
    
    }
    
    
    // mvc_tag: 表示只扫描 SpringMVC 组件,注意要禁用默认过滤行为
    // 这个就是子容器配置类,父子容器就是这么来的
    @ComponentScan(value = "com.gwm.springboot",includeFilters = {
    		@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)
    }, useDefaultFilters = false)
    @Configuration
    public class SpringMVCConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    实现 AbstractAnnotationConfigDispatcherServletInitializer 接口

    Tomcat SPI 机制中暴露了一个接口 ServletContainerInitializer,在 Tomcat 启动生命周期过程中会去加载加载该接口的所有实现类,并且还会解析 @HandlersTypes 注解,解析到的结果存放到一个 Set> c 集合中传给 onStartup() 方法,先看下 ServletContainerInitializer 代码如下:

    public interface ServletContainerInitializer {
        void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
    }
    
    • 1
    • 2
    • 3

    其中 spring-web 模块就去实现了这个 ServletContainerInitializer 接口,如图所示:

    在这里插入图片描述

    然后进入到这个实现类如下:

    @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements ServletContainerInitializer {
    	/**
    	 * 此处的 onStartup() 方法会被 tomcat 服务启动调用
    	 * 这个方法主要是为 Web 容器
    	 * 1、添加 WebServlet 请求
    	 * 2、添加 WebFilter 过滤器
    	 * 3、添加 WebListener 监听器
    	 * @Param  webAppInitializerClasses 表示加载所有被注解:@HandlesTypes(WebApplicationInitializer.class) 修饰的类装载进容器
    	 * @Param servletContext Web 容器上下文
    	 * @throws ServletException
    	 */
    	@Override
    	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
    			throws ServletException {
    		// ...省略
    		for (WebApplicationInitializer initializer : initializers) {
    			initializer.onStartup(servletContext);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Tomcat 启动过程中会调用 onStartup() 方法,然后被注解 @HandlesType 修饰的子类都会被添加到 onStartup() 方法中的第一个参数 Set 集合中,进行挨个执行 onStartup() 方法。

    AbstractAnnotationConfigDispatcherServletInitializer 类是 WebApplicationInitializer 的子类,所以最终 Tomcat 也会调用 onStartup() 方法执行里面的逻辑。

    而且实现这个类对于父子容器理解非常清晰,代码如下:

    public class QuickStartWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    	/**
    	 * mvc_tag: 获取 Spring 配置文件 在 mvc_fun_call1 被回调
    	 */
    	@Override
    	protected Class<?>[] getRootConfigClasses() {
    		return new Class<?>[]{SpringConfig.class};
    	}
    	/**
    	 * mvc_tag: 获取 SpringMVC 配置文件 在 mvc_fun_call2 被回调
    	 */
    	@Override
    	protected Class<?>[] getServletConfigClasses() {
    		return new Class<?>[]{SpringMVCConfig.class};
    	}
    	/**
    	 * mvc_tag: 获取 SpringMVC 根路径 在 mvc_fun_call3 被回调
    	 */
    	@Override
    	protected String[] getServletMappings() {
    		return new String[]{"/"};
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    写个 Controller 类测试即可,代码如下:

    
    @RestController
    public class HelloController {
    
    	@Autowired
    	private HelloService helloService;
    
    	@RequestMapping("/hello")
    	public String hello() {
    		System.out.println("hellow -----------------");
    		helloService.sayHello();
    		return "hellow";
    	}
    }
    
    @Service
    public class HelloServiceImpl implements HelloService {
    	@Override
    	public void sayHello() {
    		System.out.println("HelloServiceImpl execute....");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    前端效果如下:

    在这里插入图片描述

    后端效果如下:

    在这里插入图片描述

  • 相关阅读:
    vue + canvas 实现涂鸦面板
    singularity docker 拉取镜像 seurat和scapy spatial空转数据转换 cell2location
    Rust入门基础
    从数学老师转行到银行做开发,我都经历了什么……
    业务提前初始化执行
    Spring Cloud Gateway核心过滤器之请求限流详解
    webGL技术开发的软件类型
    VirtualBox安装增强功能
    进制A+B [牛客网]
    Java基础练习题(1.以阶乘为例演示方法调用、2.判断一个整数是否为回文数、3.随机生成一个在1-10范围之间的数)
  • 原文地址:https://blog.csdn.net/qq_35971258/article/details/126369478