java - 是否可以设置 Undertow 来服务 Spring REST 端点?
问题描述
我的主要目标是在我的应用程序中嵌入Redhat 的 Undertowweb.xml
,而无需任何Spring Boot。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 / 配置 Spring Boot中看到 Spring 提供了一种在 Boot 中使用 Undertow 的方法。我只是不需要 Spring Boot。而且我真的不热衷于深入研究 Spring 源代码以了解 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 邮件列表也无法搜索。
解决方案
我不得不硬着头皮浏览 Spring 源代码,看看 Pivotal 是如何将它们联系在一起的。
在提取相关部分之后,我重构了我所得到的并将其归结为基本要素。这首先是集成测试类。
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")));
}
我在公用事业课上做了 Undertow 管道。我将它与 Spring 完全分开——这就是我将 Spring 实现javax.servlet.ServletContainerInitializer
作为参数发送的原因。
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();
}
您必须实现 SpringAbstractAnnotationConfigDispatcherServletInitializer
并将其传递给 Undertow,以便ServletContainerInitializer
在 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);
}
}
这个 Spring servlet 初始化程序将通过您的实现调用 Spring 上下文初始化AnnotationConfigWebApplicationContext
(在该getRootConfigClasses
方法中):
@PropertySource("file:target/application-int.properties")
@Configuration
@ComponentScan(basePackages = { "org.adam.rest" })
@EnableWebMvc
public class MySpringWebApplicationContext
extends AnnotationConfigWebApplicationContext {
}
以这种方式在 Undertow 中启动整个 Spring REST 服务器大约需要 1 秒。有了 RESTassured 进行测试,一切都很顺利。
推荐阅读
- python - scipy 和 numpy 逆 fft 返回复数而不是浮点数,不能保存为 wav
- hibernate - 为什么从 saveAndFlush() 返回的实体的 OneToMany 字段映射为 null?
- c# - 发送消息 CTRL + 向上箭头
- python - 如何在 Python 3 中读取 Thorlabs 应变计读取器 KSG101
- python-3.x - MNIST 数据集异常检测
- ruby-on-rails - Ruby where 子句一起查找特定条件和 nil 值
- java - CrudRepository 保存方法不保存任何数据库
- php - 如何处理将数组或单个值传递给函数
- reactjs - 如何更新数组的状态
- amazon-web-services - 执行 aws s3 ls s3://[mybucketname:)] 时出现 InsecureRequestWarning --no-verify-ssl