Как настроить несколько дочерних контекстов в приложении Spring Boot 2? - PullRequest
0 голосов
/ 14 июля 2020

Чтобы узнать больше об использовании нескольких контекстов в одном приложении, я решил создать очень базовый c пример приложения, управляющего ресурсами публикации и использующего 2 дочерних контекста: один для Rest API, а другой - для MVC.

Приложение развернуто на отдельном сервере Tomcat 8.5 и прослушивает порт 8081.

Вот структура multi-context приложения:

  • мульти-контекст
    • приложение
      • Post. java
      • PostService. java
      • MultiContextApplication. java
    • api
      • ApiConfig. java
      • PostApiController. java
    • mvc
      • MvcConfig. java
      • PostController. java

1) Пакет Api (первый дочерний контекст)

ApiConfig. java

@Configuration
@EnableWebMvc
@ComponentScan
public class ApiConfig {

}

Отвечает только за загрузку PostApiController в дочернем контексте API

PostApiController. java

@RestController
@AllArgsConstructor
@RequestMapping("/posts")
public class PostApiController {

    private final PostService postService;

    @PostMapping
    public ResponseEntity<Post> createPost(@RequestBody Post post) {
        return ok(postService.createPost(post.getTitle(), post.getContent()));
    }

    @GetMapping
    public ResponseEntity<List<Post>> getAllPosts() {
        return ok(postService.getAllPosts());
    }
}

2) Mvc пакет (второй родственный контекст)

MvcConfig. java

@Configuration
@EnableWebMvc
@ComponentScan
public class MvcConfig {

}

Отвечает только за загрузку MVC PostController в MVC дочернем контексте

PostController. java

@Controller
@AllArgsConstructor
@RequestMapping("/posts")
public class PostController {

    private final PostService postService;

    @GetMapping
    public ModelAndView getAllPostsView() {
        Map<String, Object> values = new HashMap<>();
        values.put("posts", postService.getAllPosts());
        return new ModelAndView("posts", values);
    }
}

3) Пакет приложения (root контекст приложения)

Пакет, отвечающий за инициализацию root контекста приложения (загрузка общих компонентов (PostService), совместно используемых в каждом дочернем контексте) и инициализацию из 2 дочерних контекстов (API и MVC).

MultiContextApplication. java

@SpringBootApplication
public class MultiContextApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(MultiContextApplication.class, args);
    }

    /**
     * Root context initialization
     */
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MultiContextApplication.class);
    }

    /**
     * Rest API dispatcher servlet initialization
     */
    @Bean(name = "apiDispatcherServlet")
    public DispatcherServlet apiDispatcherServlet(WebApplicationContext webApplicationContext) {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.setParent(webApplicationContext);
        applicationContext.register(ApiConfig.class);
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        dispatcherServlet.setApplicationContext(applicationContext);
        return dispatcherServlet;
    }

    /**
     * Rest API child context initialization
     */
    @Bean(name = "apiServletRegistrationBean")
    public ServletRegistrationBean apiServletRegistrationBean(
            @Qualifier("apiDispatcherServlet") DispatcherServlet apiDispatcherServlet) {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.addUrlMappings("/api/*");
        servletRegistrationBean.setServlet(apiDispatcherServlet);
        servletRegistrationBean.setLoadOnStartup(1);
        servletRegistrationBean.setName("api-servlet");
        return servletRegistrationBean;
    }

    /**
     * MVC dispatcher servlet initialization
     */
    @Bean(name = "mvcDispatcherServlet")
    public DispatcherServlet mvcDispatcherServlet(WebApplicationContext webApplicationContext) {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.setParent(webApplicationContext);
        applicationContext.register(MvcConfig.class);
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        dispatcherServlet.setApplicationContext(applicationContext);
        return dispatcherServlet;
    }

    /**
     * MVC child context initialization
     */
    @Bean(name = "mvcServletRegistrationBean")
    public ServletRegistrationBean mvcServletRegistrationBean(
            @Qualifier("mvcDispatcherServlet") DispatcherServlet mvcDispatcherServlet) {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.addUrlMappings("/mvc/*");
        servletRegistrationBean.setServlet(mvcDispatcherServlet);
        servletRegistrationBean.setLoadOnStartup(1);
        servletRegistrationBean.setName("mvc-servlet");
        return servletRegistrationBean;
    }
}

Как и следовало ожидать, если входящий запрос http://localhost: 8081 / multi-context / api / posts он будет обрабатываться apiDispatcherServlet (urlMapping = / api / *), и если входящий запрос http://localhost: 8081 / multi -context / mvc / posts он будет обработан mvcDispatcherServlet (urlMapping = /mvc/*).

Проблема в том, что у меня есть 404 для обоих случаев, и я действительно не Understa и почему.

GET http://localhost:8081/multi-context/api/posts
{
    "timestamp": "2020-07-14T00:28:33.461+00:00",
    "status": 404,
    "error": "Not Found",
    "message": "",
    "path": "/multi-context/api/posts"
}

Выходной журнал:

2020-07-14 02:46:01.203  INFO 11120 --- [on(3)-127.0.0.1] c.i.m.app.MultiContextApplication        : Starting MultiContextApplication v0.0.1-SNAPSHOT on L-5CD9474WTJ with PID 11120
2020-07-14 02:46:01.207  INFO 11120 --- [on(3)-127.0.0.1] c.i.m.app.MultiContextApplication        : No active profile set, falling back to default profiles: default
2020-07-14 02:46:02.162  INFO 11120 --- [on(3)-127.0.0.1] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 919 ms
2020-07-14 02:46:02.626  INFO 11120 --- [on(3)-127.0.0.1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-07-14 02:46:02.771  INFO 11120 --- [on(3)-127.0.0.1] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2020-07-14 02:46:02.904  INFO 11120 --- [on(3)-127.0.0.1] c.i.m.app.MultiContextApplication        : Started MultiContextApplication in 2.253 seconds (JVM running for 4.955)
2020-07-14 02:46:03.003  INFO 11120 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'api-servlet'
2020-07-14 02:46:03.084  INFO 11120 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 81 ms
2020-07-14 02:46:03.086  INFO 11120 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'mvc-servlet'
2020-07-14 02:46:03.138  INFO 11120 --- [on(3)-127.0.0.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 52 ms
2020-07-14 02:46:03.449  INFO 11120 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-07-14 02:46:03.451  INFO 11120 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms

Как мы видим, контекст api-servlet и контекст mvc-servlet загружены успешно.

Возможно конфликт с контекстом по умолчанию dispatcherServlet, но если это так, я не знаю, как решить проблему.

1 Ответ

0 голосов
/ 16 июля 2020

Я наконец нашел решение. Я просто забыл @EnableWebMvc из основного класса конфигурации. Вот почему классы PostController и PostApiController не были правильно загружены, что привело к ошибке 404.

AppConfig. java

@Configuration
@EnableWebMvc
public class AppConfig {

}

MultiContextApplication. java

@SpringBootApplication
@Import(AppConfig.class)
public class MultiContextApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(MultiContextApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MultiContextApplication.class);
    }

    @Bean
    public ServletRegistrationBean apiServletRegistrationBean() {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(ApiConfig.class);

        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        dispatcherServlet.setApplicationContext(applicationContext);

        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.addUrlMappings("/api/*");
        servletRegistrationBean.setServlet(dispatcherServlet);
        servletRegistrationBean.setLoadOnStartup(1);
        servletRegistrationBean.setName("api-servlet");
        return servletRegistrationBean;
    }

    @Bean
    public ServletRegistrationBean mvcServletRegistrationBean() {
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(MvcConfig.class);

        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        dispatcherServlet.setApplicationContext(applicationContext);

        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
        servletRegistrationBean.addUrlMappings("/mvc/*");
        servletRegistrationBean.setServlet(dispatcherServlet);
        servletRegistrationBean.setLoadOnStartup(1);
        servletRegistrationBean.setName("mvc-servlet");
        servletRegistrationBean.setEnabled(true);
        return servletRegistrationBean;
    }
}

Я наконец удалил бесполезную регистрацию DispatcherServlet bean. Важны только ServletRegistrationBean.

Спасибо зрителям. Надеюсь, этот пост будет кому-нибудь полезен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...