Можно ли настроить Undertow для обслуживания конечных точек Spring REST? - PullRequest
0 голосов
/ 19 октября 2018

Моя главная цель - установить Redhat's Undertow , встроенный в мое приложение без web.xml и без Spring Boot.Undertow выглядит так, как будто он достаточно близок к контейнеру сервлета, чтобы удовлетворить мои требования, и в то же время суперэффективен и компактен.Что касается других микрофреймов, я также посмотрел на 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 предоставляет способ использования Undertowв Boot.Мне просто не нужен 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 тоже не поддается поиску.

1 Ответ

0 голосов
/ 01 ноября 2018

Мне пришлось прикусить пулю и просмотреть исходный код 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();
}

Вы должны реализовать AbstractAnnotationConfigDispatcherServletInitializer Spring и передать его в Undertow для вызова.ServletContainerInitializer на этапе запуска контейнера сервлета.

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 будет вызывать инициализацию контекста Spring через реализацию AnnotationConfigWebApplicationContext (в методе getRootConfigClasses там):

@PropertySource("file:target/application-int.properties")
@Configuration
@ComponentScan(basePackages = { "org.adam.rest" })
@EnableWebMvc
public class MySpringWebApplicationContext
        extends AnnotationConfigWebApplicationContext {
}

Запуск всего сервера Spring REST в Undertow таким способом занимает около 1 секунды.И с RESTassured, чтобы сделать тестирование, это все замечательно.

...