扩展 AbstractAnnotationConfigDispatcherServletInitializer 时的 getServletConfigClasses() 与 getRootConfigClasses()

getServletConfigClasses() vs getRootConfigClasses() when extending AbstractAnnotationConfigDispatcherServletInitializer

扩展[=时getServletConfigClasses()getRootConfigClasses()有什么区别16=]。 从今天早上开始我一直在阅读很多资料,但我还没有对这些差异有任何清晰的了解:

请看一下这两个配置:

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

我看到 ConServletWebConfig(或多或少)都在做同样的事情,比如初始化视图:

但是为什么:

我阅读了文档

getRootConfigClasses() & getServletConfigClasses() 都用于

Specify @Configuration and/or @Component classes to be provided to.. (their differences )

但是为什么 ConServletWebConfig 做同样的事情(比如初始化视图),也许我误解了它。 simple term/example

中的根上下文和调度程序 servlet(我知道这个)实际上是什么

谢谢!

Root Config 类 实际上用于创建特定于应用程序且需要可用于过滤器的 Bean(因为过滤器不是 Servlet 的一部分)。

Servlet Config 类 实际上用于创建特定于 DispatcherServlet 的 Bean,例如 ViewResolvers、ArgumentResolvers、Interceptor 等。

Root Config 类 将首先加载,然后 Servlet Config 类 将被加载。

Root Config 类 将成为父上下文,它将创建一个 ApplicationContext 实例。其中 Servlet Config 类 将是父上下文的子上下文,它将创建一个 WebApplicationContext 实例。

在您的 ConServlet 配置中,您不需要指定 @EnableWebMvc 以及 InternalResourceViewResolver bean,因为它们仅在 WebConfig 处需要。

ApplicationContext 层次结构

Spring 的 ApplicationContext 提供了加载多个(分层)上下文的能力,允许每个上下文专注于一个特定的层,例如应用程序的 Web 层或 middle-tier 服务。

使用分层 ApplicationContext 的典型示例之一是当我们在 Web 应用程序中有多个 DispatcherServlet 时,我们将共享一些公共 bean,例如 datasources 他们之间。这样,我们可以定义一个包含所有公共 bean 的根 ApplicationContext 和从根上下文继承公共 bean 的多个 WebApplicationContext

在Web MVC框架中,每个DispatcherServlet都有自己的WebApplicationContext,它继承了根WebApplicationContext中已经定义的所有bean。这些继承的 bean 可以在 servlet-specific 范围内被覆盖,并且您可以在给定的 Servlet 实例本地定义新的 scope-specific bean。


Spring Web MVC 中的典型上下文层次结构(Spring 文档)

如果您生活在一个单一的 DispatherServlet 世界中,对于这种情况也可能只有一个根上下文:


Spring Web MVC 中的单根上下文(Spring 文档)

话不多说,给我看代码!

假设我们正在开发一个 Web 应用程序,我们将使用 Spring MVC、Spring 安全和 Spring 数据 JPA。对于这个简单的场景,我们至少有三个不同的配置文件。一个 WebConfig 包含我们所有的 web 相关配置,例如 ViewResolvers、Controllers、ArgumentResolvers 等。类似以下内容:

@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,其中包含所有数据访问工具,例如 DataSourceEntityManagerFactoryTransactionManager 等。它可能如下所示:

@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 { ... }
}

为了将所有这些粘合在一起,我们有两种选择。首先,我们可以通过在根上下文中添加 RepositoryConfigSecurityConfig 并在其子上下文中添加 WebConfig 来定义典型的层次结构 ApplicationContext

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 添加到根上下文并使 servlet 上下文为空:

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[] { "/" };
    }
}

进一步阅读

Skaffman 在 answer, which is highly recommended. Also, you can read Spring Documentation.

中很好地解释了 ApplicationContext 层次结构