`
jinnianshilongnian
  • 浏览: 21433505 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2404875
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2997548
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5631359
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257535
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593116
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:248952
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5847389
Group-logo
跟我学Nginx+Lua开...
浏览量:698077
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780398
社区版块
存档分类
最新评论

context:component-scan扫描使用上的容易忽略的use-default-filters

 
阅读更多

问题

如下方式可以成功扫描到@Controller注解的Bean,不会扫描@Service/@Repository的Bean。正确

 

 <context:component-scan base-package="org.bdp.system.test.controller"> 
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 
</context:component-scan>

  

但是如下方式,不仅仅扫描@Controller,还扫描@Service/@Repository的Bean,可能造成一些问题

 

 <context:component-scan base-package="org.bdp"> 
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 
</context:component-scan>

 

这个尤其在springmvc+spring+hibernate等集成时最容易出问题的地,最典型的错误就是:

事务不起作用

 

这是什么问题呢?

分析

1、<context:component-scan>会交给org.springframework.context.config.ContextNamespaceHandler处理;

 

registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());

 

2、ComponentScanBeanDefinitionParser会读取配置文件信息并组装成org.springframework.context.annotation.ClassPathBeanDefinitionScanner进行处理;

3、如果没有配置<context:component-scan>的use-default-filters属性,则默认为true,在创建ClassPathBeanDefinitionScanner时会根据use-default-filters是否为true来调用如下代码:

 

    protected void registerDefaultFilters() {
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));
			logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}
		try {
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false));
			logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

 

 

可以看到默认ClassPathBeanDefinitionScanner会自动注册对@Component、@ManagedBean、@Named注解的Bean进行扫描。如果细心,到此我们就找到问题根源了。

 

 

4、在进行扫描时会通过include-filter/exclude-filter来判断你的Bean类是否是合法的:

 

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
				if (!metadata.isAnnotated(Profile.class.getName())) {
					return true;
				}
				AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
				return this.environment.acceptsProfiles(profile.getStringArray("value"));
			}
		}
		return false;
	}

 

首先通过exclude-filter 进行黑名单过滤;

然后通过include-filter 进行白名单过滤;

否则默认排除。

 

结论

<context:component-scan base-package="org.bdp"> 
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 
</context:component-scan>

 

为什么这段代码不仅仅扫描@Controller注解的Bean,而且还扫描了@Component的子注解@Service、@Reposity。因为use-default-filters默认为true。所以如果不需要默认的,则use-default-filters=“false”禁用掉。

 

 

请参考

《SpringMVC + spring3.1.1 + hibernate4.1.0 集成及常见问题总结》 

《第三章 DispatcherServlet详解 ——跟开涛学SpringMVC》中的ContextLoaderListener初始化的上下文和DispatcherServlet初始化的上下文关系。

 

如果在springmvc配置文件,不使用cn.javass.demo.web.controller前缀,而是使用cn.javass.demo,则service、dao层的bean可能也重新加载了,但事务的AOP代理没有配置在springmvc配置文件中,从而造成新加载的bean覆盖了老的bean,造成事务失效。只要使用use-default-filters=“false”禁用掉默认的行为就可以了。

 

问题不难,spring使用上的问题。总结一下方便再遇到类似问题的朋友参考。

分享到:
评论
27 楼 xyzc1988 2018-02-06  
这个尤其在springmvc+spring+hibernate等集成时最容易出问题的地,最典型的错误就是:

事务不起作用


牢牢记住涛涛这句话
26 楼 disichaoren 2016-09-26  
jinnianshilongnian 写道
sendreams 写道
jinnianshilongnian 写道
sendreams 写道
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?

可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理

不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢


比如使用springmvc时可能有两个容器:

1、父容器 ContextLoaderListener加载的 假设此时aop代理了

2、springmvc容器加载了,假设此时没有aop代理,那么 很明显 如果controller查找依赖时 查找的是2 而不是1,,这时候如果需要事务支持就失败了

zhufengxiang 写道
我试了, 在applicationContext和XX-servlet中都使用use-default-fileter=true,就是两个xml配置文件中的component-scan元素的配置一样, 事务还是有效的。 不知道是不是现在新版本有优化过这个问题,还是你的文章不太对啊。

spring getBean的时候会先从parent factroy中获取bean,正常配置时SpringMVC的web applicationContext都会继承applicationContext.xml中的root applicationContext
25 楼 Rainbow702 2016-05-16  
引用
指定了use-default-filters=“false” 是这样的
首先通过exclude-filter 进行黑名单过滤;

然后通过include-filter 进行白名单过滤;

否则默认排除


原文中对这个部分的说明,缺少了指定了use-default-filters=“false”这个前提,导致理解很混乱,还好在回复中看到了这个答案
24 楼 zhufengxiang 2016-02-03  
我试了, 在applicationContext和XX-servlet中都使用use-default-fileter=true,就是两个xml配置文件中的component-scan元素的配置一样, 事务还是有效的。 不知道是不是现在新版本有优化过这个问题,还是你的文章不太对啊。
23 楼 suxiaodongmu 2015-04-23  
这篇文章有点让人糊涂啊,所谓中心不突出导致的
作者本身应该是想说 use-default-filters="true"(默认值就是true)
它的作用就是扫描一些相关的注解,包括了@controller,@Component,@Service,@Repository等,

而use-default-filters="false",同时使用白名单context:include-filter对指定的注解进行扫描
如:
 <context:component-scan base-package="org.bdp.system.test.controller" use-default-filters="false">   
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
</context:component-scan> 

这就表示只扫描"org.bdp.system.test.controller"下的Controller注解

还有就是作者说到的事物失效,应该是applicationContext.xml中已经扫描过@Service和@Repository,而在xx-servlet.xml扫描@Controller时就不应该再去扫描@Service和@Repository,不然就会导致事物失效的情况

以上,纯属自己理解,如有错误请及时指正,以免继续误导人
22 楼 penwei 2014-11-19  
下面弄错了。是配置的问题
21 楼 penwei 2014-11-19  
<context:component-scan base-package="org.bdp.system.test.controller">   
     <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   
</context:component-scan>  


使用了“<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> ”会导致 <mvc:resources location="/resource/**" mapping="/resource/**" />报异常:
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.util.ArrayList<?> to type java.util.List<org.springframework.core.io.Resource> for value '[/upload/**]'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type java.lang.String to type org.springframework.core.io.Resource
	at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:169)
	at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:161)
	at org.springframework.beans.BeanWrapperImpl.convertIfNecessary(BeanWrapperImpl.java:448)
	at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:494)
	at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:488)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1433)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1392)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1128)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
	at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:651)
	at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:599)
	at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:665)
	at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:518)
	at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:459)
	at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
	at javax.servlet.GenericServlet.init(GenericServlet.java:158)
	at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284)
	at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1197)
	at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1087)
	at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5210)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5493)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:632)
	at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1247)
	at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1898)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
	at java.util.concurrent.FutureTask.run(FutureTask.java:262)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type java.lang.String to type org.springframework.core.io.Resource
	at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:276)
	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:172)
	at org.springframework.core.convert.support.CollectionToCollectionConverter.convert(CollectionToCollectionConverter.java:74)
	at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:35)
	... 40 more
20 楼 zhangnuoxi 2014-04-26  
菜鸟有问题,劳烦大神解答,为什么要把controller和service等其他注解分开注入?比如
<context:component-scan base-package="com"/>
<context:annotation-config/>
或者
<context:component-scan base-package="com.controller,com.service,com.dao"/>
<context:annotation-config/>
配置的时候一次全写,所有的包都扫描过去,和分开配置有什么区别?
19 楼 zhangnuoxi 2014-04-25  
菜鸟有问题,劳烦大神解答,为什么要把controller和service等其他注解分开注入?比如
<context:component-scan base-package="com"/>
<context:annotation-config/>
注入的时候直接写一次这样,所有的包都扫描过去,两者有什么区别?
18 楼 lr544463316 2014-03-25  
昨天,还在纠结这个include怎么不管用呢,原来是这个问题,
17 楼 在世界的中心呼喚愛 2013-11-11  
在世界的中心呼喚愛 写道
麻烦,开套胸下 
项目部分代码:
   
public class FileUploadServlet extends HttpServlet {
	
	private FileUploadAction fileUploadAction;


@Override
	public void init(ServletConfig config) throws ServletException {
 		ServletContext servletContext = config.getServletContext();
	    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
	    fileUploadAction = (FileUploadAction) webApplicationContext.getBean("fileUploadAction");
	    this.config = config;
	}
}


      <context:component-scan base-package="com.etong" >
   	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>


出现问题:fileUploadAction 并没有被实例化。。
我的理解:exclude-filter这里排除扫描controller,然后我把这exclude-filter删除,
fileUploadAction 就可以被实例化,这里FileUploadServlet  这个类是继承servlet,那servlet应该不属于controller吧,虽然有同样的意义:"跳转"..
但是奇怪,排除扫描controller,会影响到servlet?



是不是因为DispatcherServler又是继承Servlet的,所以会排除扫描此servlet,故此对象没有被spring注入。
16 楼 在世界的中心呼喚愛 2013-11-09  
麻烦,开套胸下 
项目部分代码:
   
public class FileUploadServlet extends HttpServlet {
	
	private FileUploadAction fileUploadAction;


@Override
	public void init(ServletConfig config) throws ServletException {
 		ServletContext servletContext = config.getServletContext();
	    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
	    fileUploadAction = (FileUploadAction) webApplicationContext.getBean("fileUploadAction");
	    this.config = config;
	}
}


      <context:component-scan base-package="com.etong" >
   	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
   </context:component-scan>


出现问题:fileUploadAction 并没有被实例化。。
我的理解:exclude-filter这里排除扫描controller,然后我把这exclude-filter删除,
fileUploadAction 就可以被实例化,这里FileUploadServlet  这个类是继承servlet,那servlet应该不属于controller吧,虽然有同样的意义:"跳转"..
但是奇怪,排除扫描controller,会影响到servlet?

15 楼 jinnianshilongnian 2013-09-03  
chenchangqun 写道
首先通过exclude-filter 进行黑名单过滤;

然后通过include-filter 进行白名单过滤;

否则默认排除


上面的描述, 从字面上不好理解。
我的理解是:
默认扫描指定包下的全部 @Component, exclude-filter 指定的不扫描,include-filter指定的扫描, include-filter和exclude-filter 没有指定的仍然扫描。

不知道我的理解对不,请指正。

默认扫描指定包下的全部 @Component, exclude-filter 指定的不扫描,include-filter指定的扫描, include-filter和exclude-filter 没有指定的仍然扫描。对;

指定了use-default-filters=“false” 是这样的
首先通过exclude-filter 进行黑名单过滤;

然后通过include-filter 进行白名单过滤;

否则默认排除
14 楼 chenchangqun 2013-09-03  
首先通过exclude-filter 进行黑名单过滤;

然后通过include-filter 进行白名单过滤;

否则默认排除


上面的描述, 从字面上不好理解。
我的理解是:
默认扫描指定包下的全部 @Component, exclude-filter 指定的不扫描,include-filter指定的扫描, include-filter和exclude-filter 没有指定的仍然扫描。

不知道我的理解对不,请指正。
13 楼 xanodu 2013-08-23  
好吧,我看到的三段配置代码几乎没有区别。
12 楼 jinnianshilongnian 2013-07-10  
sendreams 写道
sendreams 写道
jinnianshilongnian 写道
sendreams 写道
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?

可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理

不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢

我现在在搭一个开发框架,使用的是struts2.3.15+hibernate4.2.3+spring3.2.3,用spring单元测试,各种数据添加删除都没问题,可是到了web应用中,就出现:
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
at com.googlecode.genericdao.dao.hibernate.HibernateBaseDAO.getSession(HibernateBaseDAO.java:68)
at com.googlecode.genericdao.dao.hibernate.HibernateBaseDAO._get(HibernateBaseDAO.java:332)
at com.googlecode.genericdao.dao.hibernate.GenericDAOImpl.find(GenericDAOImpl.java:53)
at org.ware4u.sbp.service.impl.CategoryServiceImpl.findById(CategoryServiceImpl.java:78)
这个错误,实在不解。

请参考
http://jinnianshilongnian.iteye.com/blog/1423971
11 楼 sendreams 2013-07-10  
sendreams 写道
jinnianshilongnian 写道
sendreams 写道
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?

可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理

不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢

我现在在搭一个开发框架,使用的是struts2.3.15+hibernate4.2.3+spring3.2.3,用spring单元测试,各种数据添加删除都没问题,可是到了web应用中,就出现:
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
at com.googlecode.genericdao.dao.hibernate.HibernateBaseDAO.getSession(HibernateBaseDAO.java:68)
at com.googlecode.genericdao.dao.hibernate.HibernateBaseDAO._get(HibernateBaseDAO.java:332)
at com.googlecode.genericdao.dao.hibernate.GenericDAOImpl.find(GenericDAOImpl.java:53)
at org.ware4u.sbp.service.impl.CategoryServiceImpl.findById(CategoryServiceImpl.java:78)
这个错误,实在不解。
10 楼 jinnianshilongnian 2013-07-10  
sendreams 写道
jinnianshilongnian 写道
sendreams 写道
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?

可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理

不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢


比如使用springmvc时可能有两个容器:

1、父容器 ContextLoaderListener加载的 假设此时aop代理了

2、springmvc容器加载了,假设此时没有aop代理,那么 很明显 如果controller查找依赖时 查找的是2 而不是1,,这时候如果需要事务支持就失败了
9 楼 sendreams 2013-07-10  
jinnianshilongnian 写道
sendreams 写道
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?

可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理

不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢
8 楼 jinnianshilongnian 2013-07-10  
sendreams 写道
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?

可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理

相关推荐

Global site tag (gtag.js) - Google Analytics