Spring Boot с несколькими DispatcherServlet, каждый из которых имеет свои собственные @Controllers

В основном я хочу разбить приложение на 2 части. Каждая часть имеет свои собственные вещи безопасности и собственные @Controller. @Services должны быть доступны из обеих частей.

Поэтому я подумал, что я должен получить 2 DispatcherServlet. Один прослушивает /admin/* а второй слушает все остальное (/). Каждый из них будет иметь свой собственный AnnotationConfigWebApplicationContext поэтому у меня может быть отдельное сканирование компонентов для @Controller s.

И потому, что весна загрузка обеспечивает один DispatcherServlet прослушивает / из коробки, я подумал, что я могу просто добавить второй:

@Configuration
public class MyConfig {
    @Bean(name="myDS")
    public DispatcherServlet myDS(ApplicationContext applicationContext) {
        AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        webContext.setParent(applicationContext);
        webContext.register(MyConfig2.class);
        // webContext.refresh();
        return new DispatcherServlet(webContext);
    }

    @Bean
    public ServletRegistrationBean mySRB(@Qualifier("myDS") DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet);
        servletRegistrationBean.addUrlMappings("/admin/*");
        servletRegistrationBean.setName("adminServlet");
        return servletRegistrationBean;
    }
}

Класс MyConfig2 имеет только @Configuration и @ComponentScan. Внутри того же пакета есть @Controller.

При запуске приложения я вижу, что второе сопоставление сервлета регистрируется, а @Controller - нет. Кроме того, теперь я могу получить доступ ко всем @Controllers из / и /admin.


Любая идея, как я могу заставить это работать?

Ответ 1

Я как-то работает!

Вот мой макет пакета:

test.foo.
         FooConfig.java
         FooController.java
test.bar.
         BarConfig.java
         BarController.java
test.app.
         Application.java
         MyService.java
src/main/resources/application.properties

Application.java:

@SpringBootApplication(exclude=DispatcherServletAutoConfiguration.class)
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
    @Bean
    public ServletRegistrationBean foo() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();   
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(FooConfig.class);
        dispatcherServlet.setApplicationContext(applicationContext);
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/foo/*");
        servletRegistrationBean.setName("foo");
        return servletRegistrationBean;
    }
    @Bean
    public ServletRegistrationBean bar() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(BarConfig.class);
        dispatcherServlet.setApplicationContext(applicationContext);
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/bar/*");
        servletRegistrationBean.setName("bar");
        return servletRegistrationBean;
    }
}
  • exclude не позволяет Spring Boot создавать собственный DispatcherServlet с / mapping. Вы можете удалить эту строку, если вы хотите, чтобы это сопоставление или определило вашу собственную.
  • Вы можете добавить servletRegistrationBean.setLoadOnStartup(1) если вы хотите, чтобы ваши сервлеты были инициализированы при запуске приложения. Иначе он будет ждать первого запроса на этот сервлет.
  • Важно установить servletRegistrationBean.setName(...), иначе сервлеты будут переопределять друг друга.

FooConfig.java & BarConfig.java:

@Configuration @ComponentScan @EnableWebMvc
public class FooConfig { }
  • @EnableWebMvc включит сканирование компонентов. Без этого он не найдет класс @Controller.

Код контроллера и службы не важен. Вам просто нужно знать, что если у вас есть @RequestMapping("/foo") внутри FooController, запрос должен быть GET/foo/foo поскольку сопоставление URL сервлета является /foo/*. Невозможно вызвать URL GET/foo потому что для URL-адреса сервлета требуется / в конце его пути (другими словами: GET/foo будет искать сервлет с / mapping!), Хотя @RequestMapping("") должен быть вызван через GET/foo/. И, конечно, невозможно было использовать /foo или /foo* качестве отображения сервлета (или я просто не нашел правильных настроек для этого)

Область применения: Контроллеры не могут видеть друг друга, хотя это не представляется возможным @Autowired их друг в друге. Также служба не может @Autowired любого из контроллеров. Но контроллеры могут @Autowired службы.

Хотя это классическая иерархия родительского дочернего контекста.

Единственное "плохое" - это то, что нам нужно @EnableMvcConfig и не получить автоматически настроенный сахар из Spring boot в контексте. Родительский контекст автоматически настраивается. Я поместил материал базы данных в application.properties и сделал запрос в MyService который вызвал FooController и он работал безупречно! :)

Надеюсь, это поможет некоторым людям!