getServletConfigClasses() vs getRootConfigClasses() 当扩展 AbstractAnnotationConfigDispatcherServletInitializer 时关于层次结构的一点ApplicationContext 说话很便宜,给我看代码! 延伸阅读

在扩展 AbstractAnnotationConfigDispatcherServletLetLetInitializer 时,getServletConfigClasses()getRootConfigClasses() 之间的区别是什么?自今天早上以来,我一直在阅读很多资料,但我还没有对差异有任何清晰的理解:

请看一下这两种配置:

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(或多或少)都在做同样的事情,比如初始化视图:

但是为什么:

  • ConServletgetRootConfigClasses() 中返回
  • WebConfiggetServletConfigClasses() 中返回

我已阅读文档

getRootConfigClasses()getServletConfigClasses() 都适用于

指定要提供给@Configuration和/或@Component类。(它们的区别)

  • getRootConfigClasses()根应用程序上下文
  • getServletConfigClasses() 的调度程序 Servlet 应用程序上下文

但是为什么ConServletWebConfig做同样的事情(比如启动视图),也许我是那个误解它的人。在简单的术语/示例中,实际上是根上下文和调度程序 servlet(我知道这个)

谢谢!


答案 1

关于层次结构的一点ApplicationContext

Spring提供了加载多个(分层)上下文的功能,允许每个上下文都集中在一个特定的层上,例如应用程序的Web层或中间层服务。ApplicationContext

使用分层的规范示例之一是,当我们在Web应用程序中有多个s并且我们将共享一些常见的bean时,例如它们之间。这样,我们可以定义一个根,其中包含所有常见的bean和从根上下文中继承常见Bean的多个bean。ApplicationContextDispatcherServletdatasourcesApplicationContextWebApplicationContext

在Web MVC框架中,每个都有自己的 ,它继承了根中已经定义的所有bean。这些继承的 Bean 可以在特定于 servlet 的作用域中重写,并且您可以定义给定实例本地的特定于作用域的新 Bean。DispatcherServletWebApplicationContextWebApplicationContextServlet

Typical context hierarchy in Spring Web MVC
Spring Web MVC 中的典型上下文层次结构(Spring Documentation)

如果您生活在一个世界中,则此方案也可能只有一个根上下文:DispatherServlet

enter image description here
Spring Web MVC 中的单根上下文(Spring Documentation)

说话很便宜,给我看代码!

假设我们正在开发一个Web应用程序,我们将使用Spring MVC,Spring Security和Spring Data JPA。对于这个简单的场景,我们至少有三个不同的配置文件。A 包含我们所有与 Web 相关的配置,例如 s、s、s 等。如下所示:WebConfigViewResolverControllerArgumentResolver

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

在这里,我定义了一个基本上解决我普通的旧jsps,糟糕的生活决定。我们需要一个 ,它包含所有数据访问设施,如 、 、 等。它可能如下所示:ViewResolverRepositoryConfigDataSourceEntityManagerFactoryTransactionManager

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

要将所有这些粘合在一起,我们有两种选择。首先,我们可以定义一个典型的分层,通过在根上下文和它们的子上下文中添加 和:ApplicationContextRepositoryConfigSecurityConfigWebConfig

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

由于我们在这里有一个,我们可以将 添加到根上下文中,并使 servlet 上下文为空:DispatcherServletWebConfig

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在这个答案中很好地解释了层次结构,这是强烈推荐的。另外,您可以阅读春季文档ApplicationContext


答案 2

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

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

将首先加载根配置类,然后加载 Servlet 配置类。

根配置类将是父上下文,它将创建一个 instace。其中,Servlet 配置类将是父上下文的子上下文,它将创建一个实例。ApplicationContextWebApplicationContext

在配置中,您不需要同时指定 Bean,因为它们仅在 .ConServlet@EnableWebMvcInternalResourceViewResolverWebConfig


推荐