Search code examples
springspring-mvcspring-webfluxspring-web

Run Spring WebFlux @Controller without Spring Boot


I'm trying to start Spring application from main without Spring Boot, I managed to get working router functions, but can't make to work classic @Controllers. I debugged and watched source of boot and built in configs, added RequestMappingHandlerMapping, and it registers my Controller, but anyway WebHandler can not see it. I can see that spring discovered my @Controller as a bean so it is not a problem. Can someone help me? Thanx.

When I run the application I get:

Apr 13, 2018 7:27:30 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@20e2cbe0: startup date [Fri Apr 13 19:27:30 EEST 2018]; root of context hierarchy Apr 13, 2018 7:27:31 PM org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping$MappingRegistry register INFO: Mapped "{[/test],methods=[GET]}" onto public java.lang.String test.wboot.Controller.test() Apr 13, 2018 7:27:31 PM org.springframework.web.reactive.result.method.AbstractHandlerMethodMapping$MappingRegistry register INFO: Mapped "{[/test],methods=[GET]}" onto public java.lang.String test.wboot.Controller.test() [DEBUG] (main) Using Console logging [DEBUG] (main) Default Epoll support : false [DEBUG] (main) Default KQueue support : false [DEBUG] (main) Connecting new channel: AbstractBootstrap$PendingRegistrationPromise@4a3329b9(incomplete) Press button to exit

public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        context.start();
    }
}

.

@Configuration
@ComponentScan("test.wboot")
public class AppConfig {

    public static final int port = 9099;

    @Bean
    public NettyContext servak(ApplicationContext applicationContext) throws IOException {


//        this handler works just perfect
//        HttpHandler httpHandler = RouterFunctions.toHttpHandler(routingFunction());

//        And this one can not see Controller
        HttpHandler httpHandler = WebHttpHandlerBuilder.applicationContext(applicationContext).build();

        ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
        NettyContext localhost = HttpServer.create("localhost", port).newHandler(adapter).block();
        System.out.println("Press button to exit");
        System.in.read();
        return localhost;
    }

    public RouterFunction<ServerResponse> routingFunction() {
        return nest(path("/test"), route(GET(""), new HandlerFunction<ServerResponse>() {
            public Mono<ServerResponse> handle(ServerRequest request) {
                return ServerResponse.ok().syncBody("Hello yopta");
            }
        }));
    }

    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping(ApplicationContext applicationContext) {
        RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
        requestMappingHandlerMapping.setApplicationContext(applicationContext);
        requestMappingHandlerMapping.afterPropertiesSet();
        return requestMappingHandlerMapping;
    }

    @Bean
    public WebHandler webHandler(ApplicationContext applicationContext) {
        DispatcherHandler webHandler = new DispatcherHandler();
        webHandler.setApplicationContext(applicationContext);
        return webHandler;
    }
}

.

@RestController
@RequestMapping("/test")
public class Controller {

    @GetMapping
    public String test() {
        return "work";
    }
}

Maven dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webflux</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>io.projectreactor.ipc</groupId>
        <artifactId>reactor-netty</artifactId>
        <version>0.7.6.RELEASE</version>
    </dependency>
</dependencies>

Solution

  • Why are you redifining everything? Spring Framework provides an @EnableWebFlux annotation that does this and more, so you should just add that annotation on a @Configuration class.

    You then just have to follow the instructions here to start your server using the WebHandler automatically created by the Framework; so for Reactor Netty, this would look like:

    HttpHandler handler = ...
    ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
    HttpServer.create(host, port).newHandler(adapter).block();