blog/java相关/springMvc整合undertow.md

9.9 KiB
Raw Permalink Blame History

title categories date tags
springMvc整合undertow容器 毕业设计 2023-02-09 10:39:24
java
spring mvc
undertow

        因为项目要求用springMvc但是默认以war包发布到tomact下每一次启动都会很慢所以希望能用一个嵌入式的web容器了解到undertow对于小型项目还不错就使用了undertow.

        最开始用的Jfinal-undertow但是突然看到出了spring6.0就想体验一下spring6.0对应的servlet版本比较高jfinal-undertow并不兼容所以就自己找到了undertow进行使用以下就是折腾的经历。

1. 依赖安装

    <!-- https://mvnrepository.com/artifact/io.undertow/undertow-servlet -->
    <dependency>
      <groupId>io.undertow</groupId>
      <artifactId>undertow-servlet</artifactId>
      <version>2.3.3.Final</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/io.undertow/undertow-core -->
    <dependency>
      <groupId>io.undertow</groupId>
      <artifactId>undertow-core</artifactId>
      <version>2.3.3.Final</version>
    </dependency>

        在项目原本的依赖下加入以上两个依赖版本需要对应一样分别是undertow的核心依赖和undertow的servlet相关实现。

2. 运行整合

2.1 添加servlet

// 创建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,然后需要两个参数,分别是DeploymentInfoServletContainer所以需要先创建DeploymentInfo然后创建ServletContainer因为ServletContainer也需要DeploymentInfo作为参数代码如下所示。

// 创建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项目所以使用HttpHandlerHttpHandler默认有很多实现类这里使用了最好理解的PathHandler,创建完之后添加到Undertow的Builder参数中作为一个Listener,然后启动Undertow。

        // 创建一个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容器还没有运行所以我才用的方法是手动读取文件并注入字段。

    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. 最后

完整代码如下

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);
    }
}