是否可以设置Undertow服务于Spring REST端点?

时间:2018-10-19 11:29:02

标签: java spring rest undertow

我的主要目的是在没有任何web.xml且没有Spring Boot的情况下,将Redhat's Undertow嵌入我的应用程序。 Undertow看起来距离servlet容器足够近,可以满足我的要求,同时性能卓越且精益。就其他微框架而言,我也研究了SparkJava,但由于其文档看起来更好,因此我首先尝试了Undertow。

因此,Undertow听起来不错,但是在/返回“ Hello World ”后,遇到的所有文档和教程都停止了。也许我能找到的最好的是StubbornJava/RestServer.java,其中所有端点都是硬编码的,例如:

public static final RoutingHandler ROUTES = new RoutingHandler()
    .get("/users", timed("listUsers", UserRoutes::listUsers))

我找不到什么显示了如何或是否有可能将Spring MVC / REST控制器注释与基本的Undertow结构链接起来。

我已经有一个带有在Spring注释中定义的一组端点的应用程序。

我对Spring和Undertow的知识缺少很多有关如何结合两者的知识,但是从Baeldung / Configuring Spring Boot可以看出,Spring提供了一种在Boot中使用Undertow的方法。我只是不需要Spring Boot。而且我真的不热衷于挖掘into the Spring source来了解Pivotal的工作方式,因为在我的情况下它可能无法复制。这是在Boot中实现它的方法:

@Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
    UndertowEmbeddedServletContainerFactory factory = 
      new UndertowEmbeddedServletContainerFactory();

    factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
        @Override
        public void customize(io.undertow.Undertow.Builder builder) {
            builder.addHttpListener(8080, "0.0.0.0");
        }
    });

    return factory;
}

我的猜测是,我必须以编程方式获取带注释的Spring REST控制器,并为每个控制器创建所需的Undertow资源。

似乎Undertow邮件列表也是不可搜索的。

2 个答案:

答案 0 :(得分:1)

我不得不咬牙切齿,遍历Spring源代码,以了解Pivotal如何将所有内容捆绑在一起。

取出相关部分后,我将所得到的内容进行了重构,并将其归结为基本要素。第一个是集成测试类。

afterEach(function() {
  console.log = originalLogFunction; // undo dummy log function
  if (this.currentTest.state === 'failed') {
    console.log(output);
  }
});

然后我在实用程序类中进行了Undertow管道工程。我将其与Spring完全分开,这就是为什么我将 private static Undertow server; @BeforeAll public static void startServer() throws ServletException { server = ServletUtils.buildUndertowServer( 8080, "localhost", "", SpringServletContainerInitializer.class, Collections.singleton( MySpringServletInitializer.class), MyTests.class.getClassLoader()); server.start(); } @AfterAll public static void stopServer() { try { if (server != null) { server.stop(); } } catch (Exception e) { e.printStackTrace(); } } @Test public void testIsAvailable() { Response response = get("/mystuff/isAvailable"); response.then().statusCode(200); ResponseBody body = response.getBody(); assertThat("Body", body.asString(), is(equalTo("ok"))); } 的Spring实现作为参数发送的原因。

javax.servlet.ServletContainerInitializer

您必须实现Spring的import io.undertow.servlet.api.ServletContainerInitializerInfo; import javax.servlet.ServletContainerInitializer; public static Undertow buildUndertowServer( int port, String address, String contextPath, Class<? extends ServletContainerInitializer> servletContainerInitializerClass, Set<Class<?>> initializers, ClassLoader classLoader ) throws ServletException { ServletContainerInitializerInfo servletContainerInitializerInfo = new ServletContainerInitializerInfo( servletContainerInitializerClass, initializers); DeploymentInfo deployment = Servlets.deployment(); deployment.addServletContainerInitializer( servletContainerInitializerInfo); deployment.setClassLoader(classLoader); deployment.setContextPath(contextPath); deployment.setDisplayName("adam"); deployment.setDeploymentName("int-test"); deployment.setServletStackTraces(ServletStackTraces.ALL); DeploymentManager manager = Servlets.newContainer().addDeployment(deployment); manager.deploy(); Undertow.Builder builder = Undertow.builder(); builder.addHttpListener(port, address); HttpHandler httpHandler = manager.start(); httpHandler = Handlers.path().addPrefixPath(contextPath, httpHandler); builder.setHandler(httpHandler); return builder.build(); } ,并将其传递给Undertow,以便在servlet容器启动阶段由AbstractAnnotationConfigDispatcherServletInitializer调用。

ServletContainerInitializer

此Spring servlet初始化程序将通过您的import javax.servlet.ServletContext; import javax.servlet.ServletException; public class MySpringServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { MySpringWebApplicationContext.class }; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } @Override public void onStartup(ServletContext servletContext) throws ServletException { logger.info("starting up"); super.onStartup(servletContext); } } 的实现(在那里的AnnotationConfigWebApplicationContext方法中)调用Spring上下文初始化:

getRootConfigClasses

以这种方式在Undertow中启动整个Spring REST服务器大约需要1秒钟。 有了RESTassured进行测试,一切就变了。

答案 1 :(得分:0)

我创建了一个使用Spring和Undertow构建的示例项目。它还与JSP和Open Api 3.0集成在一起。您可以在这里查看:https://github.com/essentialprogramming/undertow-spring-web