--- title: springMvc整合undertow容器 categories: 毕业设计 date: 2023-02-09 10:39:24 tags: - java - spring mvc - undertow --- >         因为项目要求用springMvc,但是默认以war包发布到tomact下每一次启动都会很慢,所以希望能用一个嵌入式的web容器,了解到undertow对于小型项目还不错,就使用了undertow. >         最开始用的Jfinal-undertow,但是突然看到出了spring6.0,就想体验一下,spring6.0对应的servlet版本比较高,jfinal-undertow并不兼容,所以就自己找到了undertow进行使用,以下就是折腾的经历。 # 1. 依赖安装 ```xml io.undertow undertow-servlet 2.3.3.Final io.undertow undertow-core 2.3.3.Final ```         在项目原本的依赖下加入以上两个依赖,版本需要对应一样,分别是undertow的核心依赖和undertow的servlet相关实现。 # 2. 运行整合 ## 2.1 添加servlet ```java // 创建servletInfo,指定servlet的命名和servlet对应的class ServletInfo info = new ServletInfo("mvc", DispatcherServlet.class) // 添加映射路径 .addMapping("/") // 设置InitParam,制定了spring-mvc的配置文件位置 .addInitParam("contextConfigLocation","classpath:spring-config/spring-mvc.xml") // 设置什么时候进行初始化 .setLoadOnStartup(1); ``` ## 2.2 创建容器    undertow的一个web容器就是一个**DeploymentManager**,该接口的默认实现类就是**DeploymentManagerImpl**,然后需要两个参数,分别是**DeploymentInfo**和**ServletContainer**,所以需要先创建DeploymentInfo,然后创建ServletContainer,因为ServletContainer也需要DeploymentInfo作为参数,代码如下所示。 ```java // 创建DeploymentInfo 对象 DeploymentInfo deploymentInfo = new DeploymentInfo() // 指定类加载器 .setClassLoader(TestServer.class.getClassLoader()) // 设置contextPath .setContextPath("/") // 设置容器名称 .setDeploymentName("myUndertow") // 设置初始化参数,制定了spring的配置文件的位置 .addInitParameter("contextConfigLocation","classpath:spring-config/spring.xml") // 设置了一个监听器,用于初始化spring .addListener(new ListenerInfo(ContextLoaderListener.class)) // 添加servlet .addServlet(info) // 添加一个过滤器,放置乱码问题 .addFilter( new FilterInfo("Encoding", CharacterEncodingFilter.class) .addInitParam("encoding","utf-8") ); // 创建一个ServletContainer,并添加deploymentInfo ServletContainerImpl container = new ServletContainerImpl(); container.addDeployment(deploymentInfo); // 创建一个DeploymentManagerImpl DeploymentManagerImpl manager = new DeploymentManagerImpl(deploymentInfo, container); // 部署该容器,一定要部署否则会报各种空指针 manager.deploy(); ``` ## 2.3 添加HttpHandler         因为是web项目,所以使用HttpHandler,HttpHandler默认有很多实现类,这里使用了最好理解的**PathHandler**,创建完之后添加到**Undertow**的Builder参数中,作为一个Listener,然后启动Undertow。 ```java // 创建一个PathHandler,然后添加一个前缀匹配,DeploymentManagerImpl 调用start方法后会返回一个HttpHandler类型 PathHandler root = new PathHandler().addPrefixPath(deploymentInfo.getContextPath(), manager.start()); // 创建一个Undertow对象并配置启动的端口等参数,然后调用start方法启动 Undertow.Builder builder = Undertow.builder().addHttpListener(port, host, root); Undertow undertow = builder.build(); undertow.start(); logger.info(" the undertow success listen http://"+host+":"+port); ``` ## 2.4 读取配置文件         我也希望向springBoot项目一样可以直接读取端口等参数直接启动,但是因为容器运行时spring容器还没有运行,所以我才用的方法是手动读取文件并注入字段。 ```java public void initConfig(String configPath) throws IllegalAccessException { if (configPath.equals("")){ configPath = "config/application.yml"; } // 读取yaml文件并转换为propperties YamlPropertiesFactoryBean propertiesFactoryBean = new YamlPropertiesFactoryBean(); propertiesFactoryBean.setResources(new ClassPathResource(configPath)); // 配置log4j基本信息 PropertyConfigurator.configure(propertiesFactoryBean.getObject()); // 读取当前类的字段 Field[] fields = this.getClass().getDeclaredFields(); for (Field field:fields ) { // 检查是否包含Value注解 if (field.getAnnotation(Value.class) != null) { String value = field.getAnnotation(Value.class).value(); value = value.substring(2).substring(0, value.length() - 3); // 注入参数 field.set(this,propertiesFactoryBean.getObject().get(value)); } } } @Value("${server.host}") public String host; @Value("${server.port}") public Integer port; ``` # 3. 最后 完整代码如下 ```java package org.gjs; import io.undertow.Undertow; import io.undertow.server.handlers.PathHandler; import io.undertow.servlet.UndertowServletMessages; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.FilterInfo; import io.undertow.servlet.api.ListenerInfo; import io.undertow.servlet.api.ServletInfo; import io.undertow.servlet.core.DeploymentImpl; import io.undertow.servlet.core.DeploymentManagerImpl; import io.undertow.servlet.core.ManagedServlet; import io.undertow.servlet.core.ServletContainerImpl; import io.undertow.servlet.handlers.ServletHandler; import io.undertow.servlet.handlers.ServletInitialHandler; import io.undertow.servlet.handlers.ServletPathMatches; import io.undertow.servlet.spec.ServletContextImpl; import jakarta.servlet.ServletException; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.gjs.config.MyUndertowConfig; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ClassPathResource; import org.springframework.http.server.reactive.UndertowHttpHandlerAdapter; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.DispatcherServlet; import java.lang.reflect.Field; public class TestServer { private final Logger logger = Logger.getLogger(this.getClass()); public static void main(String[] args) throws ServletException, IllegalAccessException { TestServer server = new TestServer(); server.initConfig(""); server.start(); } public void initConfig(String configPath) throws IllegalAccessException { if (configPath.equals("")){ configPath = "config/application.yml"; } YamlPropertiesFactoryBean propertiesFactoryBean = new YamlPropertiesFactoryBean(); propertiesFactoryBean.setResources(new ClassPathResource(configPath)); // 配置log4j基本信息 PropertyConfigurator.configure(propertiesFactoryBean.getObject()); Field[] fields = this.getClass().getDeclaredFields(); for (Field field:fields ) { if (field.getAnnotation(Value.class) != null) { String value = field.getAnnotation(Value.class).value(); value = value.substring(2).substring(0, value.length() - 3); field.set(this,propertiesFactoryBean.getObject().get(value)); } } } @Value("${server.host}") public String host; @Value("${server.port}") public Integer port; public void start() throws ServletException { ServletInfo info = new ServletInfo("mvc", DispatcherServlet.class) .addMapping("/") .addInitParam("contextConfigLocation","classpath:spring-config/spring-mvc.xml") .setLoadOnStartup(1); DeploymentInfo deploymentInfo = new DeploymentInfo() .setClassLoader(TestServer.class.getClassLoader()) .setContextPath("/") .setDeploymentName("myUndertow") .addInitParameter("contextConfigLocation","classpath:spring-config/spring.xml") .addListener(new ListenerInfo(ContextLoaderListener.class)) .addServlet(info) .addFilter( new FilterInfo("Encoding", CharacterEncodingFilter.class) .addInitParam("encoding","utf-8") ); ServletContainerImpl container = new ServletContainerImpl(); container.addDeployment(deploymentInfo); DeploymentManagerImpl manager = new DeploymentManagerImpl(deploymentInfo, container); manager.deploy(); PathHandler root = new PathHandler().addPrefixPath(deploymentInfo.getContextPath(), manager.start()); Undertow.Builder builder = Undertow.builder().addHttpListener(port, host, root); Undertow undertow = builder.build(); undertow.start(); logger.info(" the undertow success listen http://"+host+":"+port); } } ```