Spring 具有 Java 配置的根和 servlet 上下文
Spring root and servlet context with Java config
我是 运行 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 {
}
简短的回答是:
是的,你应该为每个上下文定义单独的组件扫描,从而以不同的方式建模你的项目并将 DAO 提取到一个单独的名称中 - space(包)。
较长的答案分为两部分,一部分关于 servlet 上下文,另一部分关于项目建模。
关于根上下文和 servlet 上下文:您已经正确定义了根上下文和 servlet 上下文的不同职责。大多数开发人员往往会忽略这一点,但这一点非常重要。
只是为了进一步阐明这个主题,您可以创建一个根上下文和多个 (0+) servlet 上下文。在根上下文中定义的所有 bean 都将可用于所有 servlet 上下文,但在每个 servlet 上下文中定义的 bean 将不会与其他 servlet 上下文(不同的 spring 容器)共享。
现在,由于您有多个 spring 容器,并且对相同的包使用组件扫描,因此将创建两次 bean,每个容器一次。
为避免这种情况,您可以做一些事情:
- 使用一个容器而不是两个,这意味着您可以只定义根容器或只定义 servlet 容器(我见过很多这样的应用程序)。
- 将您的 bean 分隔到不同的位置,并为每个容器提供其独特的组件扫描。请记住,在根容器中定义的所有 beans 都可以在 servlet 容器中使用(您不需要定义它们两次)。
最后,关于项目建模的几句话。我个人喜欢分层编写我的项目,从而将我的代码分为控制器(应用层)、业务逻辑(bl 层)和 DAO(数据库层)。
像这样的建模在很多方面都有帮助,包括您遇到的这个 root/servlet 上下文问题。
有大量关于分层架构的信息,这里是快速浏览:wiki-Multilayered_architecture
只需在每个配置中定义要扫描的内容。一般来说,您的根配置应该扫描除 @Controller
s 之外的所有内容,并且您的 Web 配置应该只检测 @Controller
s.
您可以使用 includeFilters
and excludeFilters
attributes of the @ComponentScan
annotation. When using include filters, in this case, you also need to disable using the default filters by setting 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;
我是 运行 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 {
}
简短的回答是: 是的,你应该为每个上下文定义单独的组件扫描,从而以不同的方式建模你的项目并将 DAO 提取到一个单独的名称中 - space(包)。
较长的答案分为两部分,一部分关于 servlet 上下文,另一部分关于项目建模。
关于根上下文和 servlet 上下文:您已经正确定义了根上下文和 servlet 上下文的不同职责。大多数开发人员往往会忽略这一点,但这一点非常重要。
只是为了进一步阐明这个主题,您可以创建一个根上下文和多个 (0+) servlet 上下文。在根上下文中定义的所有 bean 都将可用于所有 servlet 上下文,但在每个 servlet 上下文中定义的 bean 将不会与其他 servlet 上下文(不同的 spring 容器)共享。
现在,由于您有多个 spring 容器,并且对相同的包使用组件扫描,因此将创建两次 bean,每个容器一次。 为避免这种情况,您可以做一些事情:
- 使用一个容器而不是两个,这意味着您可以只定义根容器或只定义 servlet 容器(我见过很多这样的应用程序)。
- 将您的 bean 分隔到不同的位置,并为每个容器提供其独特的组件扫描。请记住,在根容器中定义的所有 beans 都可以在 servlet 容器中使用(您不需要定义它们两次)。
最后,关于项目建模的几句话。我个人喜欢分层编写我的项目,从而将我的代码分为控制器(应用层)、业务逻辑(bl 层)和 DAO(数据库层)。 像这样的建模在很多方面都有帮助,包括您遇到的这个 root/servlet 上下文问题。 有大量关于分层架构的信息,这里是快速浏览:wiki-Multilayered_architecture
只需在每个配置中定义要扫描的内容。一般来说,您的根配置应该扫描除 @Controller
s 之外的所有内容,并且您的 Web 配置应该只检测 @Controller
s.
您可以使用 includeFilters
and excludeFilters
attributes of the @ComponentScan
annotation. When using include filters, in this case, you also need to disable using the default filters by setting 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;