web-dev-qa-db-fra.com

Spring Boot avec plusieurs DispatcherServlet, chacun ayant ses propres @Controllers

Fondamentalement, je veux diviser mon application en 2 parties. Chaque partie a ses propres éléments de sécurité et son propre @Controllers. Le @Services devrait être accessible depuis les deux parties.

J'ai donc pensé que je devrais obtenir 2 DispatcherServlet. On écoute /admin/* et le second écoutant tout le reste (/). Chacun de ceux-ci aura son propre AnnotationConfigWebApplicationContext afin que je puisse avoir un scan séparé des composants pour le @Controllers.

Et parce que Spring Boot fournit une DispatcherServlet écoute sur / sorti de la boîte, je pensais, je peux juste ajouter un deuxième:

@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;
    }
}

Le MyConfig2 classe, a seulement @Configuration et @ComponentScan. Dans le même package est un @Controller.

Lors du démarrage de l'application, je peux voir que le deuxième mappage de servlet est enregistré, mais le @Controller n'est pas. De plus, je peux maintenant accéder à tous@Controllers de /et/admin.


Une idée de comment je peux faire fonctionner ça?

18
Benjamin M

Je l'ai fait fonctionner d'une manière ou d'une autre!

Voici ma disposition de paquet:

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 n'empêche pas Spring Boot de créer son propre DispatcherServlet avec le mappage /. Vous pouvez supprimer cette ligne, si vous souhaitez ce mappage ou définir le vôtre.
  • Vous pouvez ajouter servletRegistrationBean.setLoadOnStartup(1) si vous souhaitez que vos servlets soient initialisés au démarrage de l'application. Sinon, il attendra la première demande pour ce servlet.
  • Il est important de définir servletRegistrationBean.setName(...), sinon les servlets se remplaceront mutuellement.

FooConfig.Java & BarConfig.Java:

@Configuration @ComponentScan @EnableWebMvc
public class FooConfig { }
  • @EnableWebMvc Active l'analyse des composants. Sans lui, il ne trouvera pas la classe @Controller.

Le code du contrôleur et du service n'est pas important. Vous devez juste savoir que si vous avez @RequestMapping("/foo") à l'intérieur FooController, la demande doit être GET /foo/foo Car le mappage d'URL du Servlet est /foo/*. Il n'est pas possible d'appeler l'URL GET /foo Car le mappage d'URL de servlet a besoin d'un / À la fin de son chemin (en d'autres termes: GET /foo Recherchera un servlet avec / Mappage!), Bien que @RequestMapping("") doive être appelée via GET /foo/. Et bien sûr, il n'était pas possible d'utiliser /foo Ou /foo* Comme mappage de servlet (ou je n'ai tout simplement pas trouvé les paramètres appropriés pour cela)

Portée: Les contrôleurs ne peuvent pas se voir , bien qu'il soit pas possible de @Autowired Les uns dans les autres. Le service ne peut pas non plus @Autowired Aucun des contrôleurs. Mais les contrôleurs peuvent @Autowired Le service.

Bien qu'il s'agisse d'une hiérarchie de contexte parent-enfant classique.

La seule "mauvaise" chose est que nous avons besoin de @EnableMvcConfig Et que nous n'obtenons pas le sucre configuré automatiquement depuis Spring Boot dans le contexte. Le contexte parent est configuré automatiquement. J'ai mis des trucs de base de données dans le application.properties Et j'ai fait une requête dans MyService qui a été appelée par FooController et ça a fonctionné parfaitement! :)

J'espère que cela peut aider certaines personnes!

36
Benjamin M