<context:component-scan.../> 和 <context:property-placeholder.../> 在分层上下文中的范围
Scope of <context:component-scan.../> and <context:property-placeholder.../> in hierarchical contexts
我已阅读:
Multiple component-scan
What is the difference between ApplicationContext and WebApplicationContext in Spring MVC?
@RequestMapping annotation not working if <context:component-scan /> is in application context instead of dispatcher context(稍后详细介绍)
和其他几个人,但其中 none 回答了问题:
当 <context:component-scan.../>
出现在 Spring MVC 应用程序的 ROOT 上下文中时,为什么它的范围受到限制?
我的理解是,它会导致扫描指定包中的所有 类,并实例化任何使用 @Component
或其任何子构造型(@Repository
、@Service
和 @Controller
).
给定:
applicationContext.xml(根上下文)
<beans...>
...
<context:component-scan base-package="com.myproject"/>
<context:property-placeholder
ignore-resource-not-found="true"
location="classpath:default.properties, file:///etc/gallery/gallery.properties"/>
</beans>
main-servlet.xml(servlet 上下文)
<beans ...>
...
<mvc:annotation-driven/>
<mvc:resources mapping="/image/**" location="file:/${gallery.location}" />
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/images/**" location="/images/"/>
...
</beans>
com/myproject/web/MainController.java
package com.myproject.web;
@Controller
public class MainController
{
...
@RequestMapping("/gallery/**")
public String gallery(ModelMap modelMap, HttpServletRequest req, HttpServletResponse resp) throws IOException
{
...
}
}
Spring 文档声明在根上下文中实例化的任何 bean 都是共享的,并且可用于各个 servlet 应用程序上下文。因此,根上下文中的两个 <context:...>
声明应该生成在 servlet 上下文中可见的 bean。但事实并非如此。我需要在 servlet 上下文中重复 <context:component-scan.../>
和 <context:property-placeholder.../>
。
在 servlet 上下文中省略 <context:component-scan.../>
会导致
Sep 15, 2015 10:08:16 AM org.springframework.web.servlet.PageNotFound noHandlerFound
WARNING: No mapping found for HTTP request with URI [/gallery/habitat/20150813] in DispatcherServlet with name 'main'
Sep 15, 2015 10:08:16 AM org.springframework.web.servlet.PageNotFound noHandlerFound
WARNING: No mapping found for HTTP request with URI [/error] in DispatcherServlet with name 'main'
表示 @Controller
没有解决。
省略 <context:property-placeholder.../>
会导致使用 属性 引用的 @Value
注释未被处理,在我的例子中会导致一些断开的链接。
由于这两个 <context:.../>
指令都会导致 bean 实例化,我很困惑为什么 bean 在子上下文中不可见,这与文档直接矛盾。另外,有两个 component-scan
语句不会导致控制器 bean 被实例化两次吗?
关于 @RequestMapping annotation not working if <context:component-scan /> is in application context instead of dispatcher context,我的应用上下文中确实有 <mvc:annotation-driven />
,这里的答案没有解释为什么需要两个 component-scan
语句。
使用 "magic" 我真的很不舒服,除非我完全理解它是如何工作的 并且可以预测当我调整某些东西时它将如何表现 。所以"just add it in both places and move on"的"solution"是不可接受的。
<context:property-placeholder />
<context:property-placeholder />
注册了一个 PropertySourcesPlaceholderConfigurer
,这是一个 [BeanFactoryPostProcessor
]。这里的关键是BeanFactory
,它作用于BeanFactory
(ApplicationContext
就是这样的东西)。准确地说,它在其定义的 BeanFactory
上运行。而不是在父上下文或子上下文上。因此,您需要在两种情况下都进行注册。
有 2 个相同的缺点 <context:property-placeholder />
两者都会加载相同的资源,并且您最终会加载相同的属性文件两次。要消除这种情况,请将 <context:property-placeholder />
与 <util:properties />
元素结合使用。后者加载属性文件并将它们作为 bean 在上下文中公开,您可以使用 properties-ref
属性将此 bean 连接到 <context:property-placeholder />
。您只需在根上下文中加载属性并在子上下文中简单地引用它们。
根上下文
<util:properties id="appProperties" location="classpath:default.properties, file:///etc/gallery/gallery.properties" ignore-resource-not-found="true" />
<context:property-placeholder properties-ref="appProperties" />
子上下文
<context:property-placeholder properties-ref="appProperties" />
<mvc:annotation-driven />
关于<mvc:annotation-driven />
,其中注册了一个RequestMappingHandlerMapper
,这个bean负责检测在@Controller
注解类中用@RequestMapping
注解的方法].默认情况下,它在 ApplicationContext
中执行此操作,它在父上下文中的 NOT 中定义。您可以将 detectHandlerMethodsInAncestorContexts
属性 设置为 true
来实现这一点。
根据经验,你可以说你的根上下文应该包含所有应用程序全局,如服务、存储库、基础设施 bean,如数据源等。子上下文(由 DispatcherServlet
加载应该只包含 web 相关@Controllers
等材料)。
只是复制粘贴 <context:component-scan />
,从字面上复制它,是一个糟糕(非常糟糕)的主意。因为这将导致将所有 bean 实例化两次,并可能导致事务、内存等问题。因为 <tx:annotation-driven />
与 AOP 一起使用,并且 AOP 也使用 BeanFactoryPostProcessor
和 BeanPostProcessor
应用就像 属性 支持一样。
根上下文
<context:component-scan base-package="com.myproject">
<context:exclude-filters type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
子上下文
<context:component-scan base-package="com.myproject" use-default-filters="false">
<context:include-filters type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
我已阅读:
Multiple component-scan
What is the difference between ApplicationContext and WebApplicationContext in Spring MVC?
@RequestMapping annotation not working if <context:component-scan /> is in application context instead of dispatcher context(稍后详细介绍)
和其他几个人,但其中 none 回答了问题:
当 <context:component-scan.../>
出现在 Spring MVC 应用程序的 ROOT 上下文中时,为什么它的范围受到限制?
我的理解是,它会导致扫描指定包中的所有 类,并实例化任何使用 @Component
或其任何子构造型(@Repository
、@Service
和 @Controller
).
给定:
applicationContext.xml(根上下文)
<beans...>
...
<context:component-scan base-package="com.myproject"/>
<context:property-placeholder
ignore-resource-not-found="true"
location="classpath:default.properties, file:///etc/gallery/gallery.properties"/>
</beans>
main-servlet.xml(servlet 上下文)
<beans ...>
...
<mvc:annotation-driven/>
<mvc:resources mapping="/image/**" location="file:/${gallery.location}" />
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/images/**" location="/images/"/>
...
</beans>
com/myproject/web/MainController.java
package com.myproject.web;
@Controller
public class MainController
{
...
@RequestMapping("/gallery/**")
public String gallery(ModelMap modelMap, HttpServletRequest req, HttpServletResponse resp) throws IOException
{
...
}
}
Spring 文档声明在根上下文中实例化的任何 bean 都是共享的,并且可用于各个 servlet 应用程序上下文。因此,根上下文中的两个 <context:...>
声明应该生成在 servlet 上下文中可见的 bean。但事实并非如此。我需要在 servlet 上下文中重复 <context:component-scan.../>
和 <context:property-placeholder.../>
。
在 servlet 上下文中省略 <context:component-scan.../>
会导致
Sep 15, 2015 10:08:16 AM org.springframework.web.servlet.PageNotFound noHandlerFound
WARNING: No mapping found for HTTP request with URI [/gallery/habitat/20150813] in DispatcherServlet with name 'main'
Sep 15, 2015 10:08:16 AM org.springframework.web.servlet.PageNotFound noHandlerFound
WARNING: No mapping found for HTTP request with URI [/error] in DispatcherServlet with name 'main'
表示 @Controller
没有解决。
省略 <context:property-placeholder.../>
会导致使用 属性 引用的 @Value
注释未被处理,在我的例子中会导致一些断开的链接。
由于这两个 <context:.../>
指令都会导致 bean 实例化,我很困惑为什么 bean 在子上下文中不可见,这与文档直接矛盾。另外,有两个 component-scan
语句不会导致控制器 bean 被实例化两次吗?
关于 @RequestMapping annotation not working if <context:component-scan /> is in application context instead of dispatcher context,我的应用上下文中确实有 <mvc:annotation-driven />
,这里的答案没有解释为什么需要两个 component-scan
语句。
使用 "magic" 我真的很不舒服,除非我完全理解它是如何工作的 并且可以预测当我调整某些东西时它将如何表现 。所以"just add it in both places and move on"的"solution"是不可接受的。
<context:property-placeholder />
<context:property-placeholder />
注册了一个 PropertySourcesPlaceholderConfigurer
,这是一个 [BeanFactoryPostProcessor
]。这里的关键是BeanFactory
,它作用于BeanFactory
(ApplicationContext
就是这样的东西)。准确地说,它在其定义的 BeanFactory
上运行。而不是在父上下文或子上下文上。因此,您需要在两种情况下都进行注册。
有 2 个相同的缺点 <context:property-placeholder />
两者都会加载相同的资源,并且您最终会加载相同的属性文件两次。要消除这种情况,请将 <context:property-placeholder />
与 <util:properties />
元素结合使用。后者加载属性文件并将它们作为 bean 在上下文中公开,您可以使用 properties-ref
属性将此 bean 连接到 <context:property-placeholder />
。您只需在根上下文中加载属性并在子上下文中简单地引用它们。
根上下文
<util:properties id="appProperties" location="classpath:default.properties, file:///etc/gallery/gallery.properties" ignore-resource-not-found="true" />
<context:property-placeholder properties-ref="appProperties" />
子上下文
<context:property-placeholder properties-ref="appProperties" />
<mvc:annotation-driven />
关于<mvc:annotation-driven />
,其中注册了一个RequestMappingHandlerMapper
,这个bean负责检测在@Controller
注解类中用@RequestMapping
注解的方法].默认情况下,它在 ApplicationContext
中执行此操作,它在父上下文中的 NOT 中定义。您可以将 detectHandlerMethodsInAncestorContexts
属性 设置为 true
来实现这一点。
根据经验,你可以说你的根上下文应该包含所有应用程序全局,如服务、存储库、基础设施 bean,如数据源等。子上下文(由 DispatcherServlet
加载应该只包含 web 相关@Controllers
等材料)。
只是复制粘贴 <context:component-scan />
,从字面上复制它,是一个糟糕(非常糟糕)的主意。因为这将导致将所有 bean 实例化两次,并可能导致事务、内存等问题。因为 <tx:annotation-driven />
与 AOP 一起使用,并且 AOP 也使用 BeanFactoryPostProcessor
和 BeanPostProcessor
应用就像 属性 支持一样。
根上下文
<context:component-scan base-package="com.myproject">
<context:exclude-filters type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
子上下文
<context:component-scan base-package="com.myproject" use-default-filters="false">
<context:include-filters type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>