使用Java配置的Spring root和servlet上下文

时间:2015-02-03 07:21:21

标签: spring spring-mvc

我在Servlet 3.0+环境中运行Spring应用程序,以编程方式使用所有Java配置配置servlet上下文。我的问题(详情如下):如何构建一个项目来支持根和Web应用程序上下文的组件扫描而不重复组件初始化?

据我了解,有两种情况可以注册Spring bean。首先,根上下文是非servlet相关组件所在的位置。例如批处理作业,DAO等。其次,servlet上下文是与servlet相关的组件,例如控制器,过滤器等。

我已经实现了一个WebApplicationInitializer来注册这两个上下文,就像WebApplicationInitializer中的JavaDoc使用AppConfig.class和DispatcherConfig.class指定一样。

我希望两者都能自动找到各自的组件,因此我已将@ComponentScan添加到两者中(这导致我的Hibernate实体被启动两次)。 Spring通过扫描一些指定的基础包来找到这些组件。这是否意味着我需要将所有与DAO相关的对象放在与控制器不同的包中?如果是这样,那将非常不方便,因为我通常喜欢按功能打包(而不是打字)。

代码段......

WebApplicationInitializer:

public class AppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) throws ServletException {
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext =
                new AnnotationConfigWebApplicationContext();
        rootContext.register(AppConfig.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherContext =
                new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(WebAppConfig.class);

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher =
                container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }

}

的AppConfig:

@Configuration
@ComponentScan
public class AppConfig {
}

WebAppConfig:

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@ComponentScan(basePackageClasses = AppConfig.class)
public class WebAppConfig extends WebMvcConfigurerAdapter {
}

2 个答案:

答案 0 :(得分:8)

只需在每个配置中定义要扫描的内容即可。通常,您的根配置应扫描除@Controller以外的所有内容,并且您的Web配置应仅检测@Controller s。

您可以使用includeFilters注释的excludeFilters@ComponentScan属性来完成此操作。使用包含过滤器时,在这种情况下,您还需要通过将useDefaultFilters设置为false来禁用默认过滤器。

@Configuration
@ComponentScan(excludeFilters={@Filter(org.springframework.stereotype.Controller.class)})
public class AppConfig {}

适用于您的WebConfig

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@ComponentScan(basePackageClasses = AppConfig.class, useDefaultFilters=false, includeFilters={@Filter(org.springframework.stereotype.Controller.class)})
public class WebAppConfig extends WebMvcConfigurerAdapter {}

此外,您需要导入@Filter注释:

import static org.springframework.context.annotation.ComponentScan.Filter;

答案 1 :(得分:1)

简短的回答是: 是的,您应该为每个上下文定义单独的组件扫描,从而以不同方式对项目进行建模,并将DAO提取到单独的名称空间(包)中。

较长的答案分为两部分,一部分关于servlet上下文,另一部分关于项目建模。

关于root和servlet上下文:您已正确定义了根上下文和servlet上下文的不同职责。这是大多数开发人员倾向于错过的理解,这非常重要。
只是为了澄清这个主题,您可以创建一个根上下文和几个(0+)servlet上下文。根上下文中定义的所有bean都可用于所有servlet上下文,但每个servlet上下文中定义的bean不会与其他servlet上下文(不同的spring容器)共享。

现在,由于您有多个弹簧容器并且在相同的包上使用组件扫描,因此每个容器创建两次bean。 为了避免这种情况,你可以做一些事情:

  1. 使用一个容器而不是两个容器,这意味着您只能定义根容器或仅定义servlet容器(我见过很多这样的应用程序)。
  2. 将您的bean分隔到不同的位置,并为每个容器提供独特的组件扫描。请记住,根容器中定义的所有bean都可以在servlet容器中使用(您不需要定义它们两次)。
  3. 最后,关于项目建模的几句话。我个人喜欢分层编写项目,因此将我的代码分成控制器(应用层),业务逻辑(bl层)和DAO(数据库层)。 这样的建模有很多方面,包括您遇到的root / servlet上下文问题。 有大量关于分层架构的信息,这里有一个快速的peed:wiki-Multilayered_architecture