【发布时间】:2015-02-18 06:08:57
【问题描述】:
在我的previous question(感谢@Andy Wilkinson)中,我发现对undertowEmbeddedServletContainer 的所有传入请求都由工作线程处理(阻塞操作)。
根据 Andy 的说法,我尝试添加 UndertowBuilderCustomizer 以覆盖 ServletInitializerHandler 以使用非阻塞处理程序处理传入请求。
@Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory(){
UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory = new UndertowEmbeddedServletContainerFactory();
undertowEmbeddedServletContainerFactory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Undertow.Builder builder) {
builder.setHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send("test");
}
});
}
});
return undertowEmbeddedServletContainerFactory;
}
在这个定制器中,我为 NIO 处理程序设置了构建器 rootHandler。
但它在启动阶段被UndertowEmbeddedServletContainer 覆盖,并带有ServletInitializerHandler:
private Undertow createUndertowServer() {
try {
HttpHandler servletHandler = this.manager.start();
this.builder.setHandler(getContextHandler(servletHandler));
return this.builder.build();
}
catch (ServletException ex) {
throw new EmbeddedServletContainerException(
"Unable to start embdedded Undertow", ex);
}
}
正如这个问题的标题所说:我试图同时拥有阻塞和非阻塞处理程序,其中阻塞处理程序通过@Controller 注释进行管理,而 NIO 处理程序由 Spring 管理。
我找到了一个解决方案,但作为一个初学者,我不知道它是否是一个好的解决方案。
HandlerPath 注释
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
public @interface HandlerPath {
public String path() default "";
}
创建实现 HttpHandler 的 bean
@Component
@HandlerPath(path = "/hello-nio")
public class HelloHandler implements HttpHandler{
@Autowired
HelloService helloService;
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send(helloService.sayHello("Josselin"));
}
}
创建一个简单的控制器
@Controller
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String sayHello(){
return "hello";
}
}
创建一个实现 ServletExtension 的类
public class NonBlockingHandlerExtension implements ServletExtension{
@Override
public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext) {
deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() {
@Override
public HttpHandler wrap(final HttpHandler handler) {
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Map<String, Object> handlers = ctx.getBeansWithAnnotation(HandlerPath.class);
PathHandler rootHandler = new PathHandler();
rootHandler.addPrefixPath("/", handler);
for(Map.Entry<String, Object> handlerEntry : handlers.entrySet()){
if(handlerEntry.getValue() instanceof HttpHandler){
HttpHandler httpHandler = (HttpHandler) handlerEntry.getValue();
String path = httpHandler.getClass().getAnnotation(HandlerPath.class).path();
rootHandler.addPrefixPath(path, httpHandler);
}
}
return rootHandler;
}
});
}
}
在该方法中,默认的ServletInitializerhandler绑定到“/”上下文,由spring管理,所以所有阻塞请求都可以由@Controller(s)处理。
然后我尝试发现所有使用@HandlerPath 注释的bean,然后基于@HandlerPath.path 属性将新的prefixPath 添加到rootHandler。
终于
创建目录 META-INF.services
创建一个文件 io.undertow.servlet.ServletExtension 并添加行:
org.me.undertow.NonBlockingHandlerExtension
结果
一切都像魅力一样工作,当绑定 URL 被命中时,NIO 处理程序被调用,阻塞处理程序也是如此。
谁能告诉我这个解决方案是否可以以任何方式改进?
此外,由于 NIO 处理程序 URL 不是由 Spring 管理的,我想我必须使用 globaleMethodSecurity 并设置 @PreAuthorize 来保护 NIO 处理程序?
【问题讨论】:
标签: spring-mvc spring-boot undertow