getServletConfigClasses() против getRootConfigClasses() при расширении AbstractAnnotationConfigDispatcherServletInitializer

В чем разница между getServletConfigClasses() против getRootConfigClasses() при расширении AbstractAnnotationConfigDispatcherServletInitializer, С самого утра я читал много источников, но пока не получил четкого понимания различий:

Пожалуйста, посмотрите на эти две конфигурации:

1).

public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {         
        return new Class[] { ConServlet.class }; 
    }
    @Override
    protected Class<?>[] getServletConfigClasses() {                      
        return null;
    }
        ....
        ....    
        }

ConServlet.class ссылается на

@EnableWebMvc 
@Configuration
@ComponentScan({ "com" })
@Import({ SecurityConfig.class })
public class ConServlet {
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }   
}

2).

public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class }; 
    }
    .....
}

WebConfig.class ссылается на

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "....." })
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Bean
    public ViewResolver viewResolver() {

        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

Я вижу, что и ConServlet, и WebConfig (более или менее) выполняют одни и те же действия, например инициализация представления:

Но почему:

  • ConServlet возвращается в getRootConfigClasses()
  • в то время как WebConfig возвращается в getServletConfigClasses()

Я читаю документацию

оба getRootConfigClasses() и getServletConfigClasses() предназначены для

Укажите классы @Configuration и / или @Component, которые будут предоставлены.. (их различия)

  • корневой контекст приложения для getRootConfigClasses()
  • контекст приложения диспетчера сервлетов для getServletConfigClasses()

но почему тогда ConServlet & WebConfig делают то же самое (например, инициализирующий просмотр), может быть, я это неправильно понял. Что такое на самом деле корневой контекст и диспетчерские сервлеты (я знаю это) в простом термине / примере

Спасибо!

2 ответа

Решение

Немного на ApplicationContext Иерархии

Спринга ApplicationContext обеспечивает возможность загрузки нескольких (иерархических) контекстов, позволяя каждому из них быть сосредоточенным на одном конкретном уровне, таком как веб-уровень приложения или сервисы среднего уровня.

Один из канонических примеров использования иерархического ApplicationContext когда у нас есть несколько DispatcherServletS в веб-приложении, и мы собираемся поделиться некоторыми из распространенных компонентов, таких как datasources между ними. Таким образом, мы можем определить корень ApplicationContext которые содержат все общие бобы и несколько WebApplicationContexts, которые наследуют общие бины из корневого контекста.

В рамках Web MVC каждый DispatcherServlet имеет свой WebApplicationContext, который наследует все бины, уже определенные в корне WebApplicationContext, Эти унаследованные bean-компоненты могут быть переопределены в области, зависящей от сервлета, и вы можете определить новые bean-компоненты, зависящие от области, локально для данной области. Servlet пример.

Типичная контекстная иерархия в Spring Web MVC
Типичная контекстная иерархия в Spring Web MVC (Spring Documentation)

Если вы живете в одиночном DispatherServlet В этом сценарии также возможно иметь только один корневой контекст:


Единый корневой контекст в Spring Web MVC (Spring Documentation)

Обсуждение дешево, покажи мне код!

Предположим, мы разрабатываем веб-приложение и собираемся использовать Spring MVC, Spring Security и Spring Data JPA. Для этого простого сценария у нас было бы как минимум три разных конфигурационных файла. WebConfig который содержит все наши веб-связанные конфигурации, такие как ViewResolvers, Controllers, ArgumentResolverи т. д. Что-то вроде следующего:

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.so.web")
public class WebConfig extends WebMvcConfigurerAdapter {
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");

        return viewResolver;
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        final boolean DO_NOT_USE_SUFFIX_PATTERN_MATCHING = false;
        configurer.setUseSuffixPatternMatch(DO_NOT_USE_SUFFIX_PATTERN_MATCHING);
    }
}

Здесь я определяю ViewResolver чтобы решить мои старые добрые jsps, плохие жизненные решения, в основном. Нам понадобится RepositoryConfig, который содержит все средства доступа к данным, такие как DataSource, EntityManagerFactory, TransactionManagerи т. д. Это, вероятно, будет выглядеть следующим образом:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.so.repository")
public class RepositoryConfig {
    @Bean
    public DataSource dataSource() { ... }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() { ... }

    @Bean
    public PlatformTransactionManager transactionManager() { ... }
}

И SecurityConfig который содержит все вещи, связанные с безопасностью!

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { ... }

    @Override
    protected void configure(HttpSecurity http) throws Exception { ... }
}

Для склейки всего этого у нас есть два варианта. Во-первых, мы можем определить типичную иерархическую ApplicationContext, добавляя RepositoryConfig а также SecurityConfig в корневом контексте и WebConfig в их дочернем контексте:

public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RepositoryConfig.class, SecurityConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

Так как у нас есть один DispatcherServlet здесь мы можем добавить WebConfig в корневой контекст и сделайте контекст сервлета пустым:

public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RepositoryConfig.class, SecurityConfig.class, WebConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

Дальнейшее чтение

Скаффман проделал большую работу по объяснению ApplicationContext иерархии в этом ответе, что настоятельно рекомендуется. Также вы можете прочитать Spring Documentation.

Корневые классы конфигурации фактически используются для создания bean-компонентов, которые являются специфичными для приложения и которые должны быть доступны для фильтров (поскольку фильтры не являются частью сервлета).

Классы конфигурации сервлетов фактически используются для создания bean-компонентов, специфичных для DispatcherServlet, таких как ViewResolvers, ArgumentResolvers, Interceptor и т. Д.

Сначала будут загружены Root Config Classes, а затем будут загружены Servlet Config Classes.

Корневые классы конфигурации будут родительским контекстом и создадут ApplicationContext instace. Где в качестве классов конфигурации сервлета будет дочерний контекст родительского контекста, и он создаст WebApplicationContext пример.

В вашем ConServlet Конфигурация, вам не нужно указывать @EnableWebMvc а также InternalResourceViewResolver боб, поскольку они требуются только на WebConfig,

Другие вопросы по тегам