通配符的匹配很全面, 但无法找到元素 的声明'context:component-scan' 的声明.

有人可以解决一下关于“ 通配符的匹配很全面, 但无法找到元素 'tx:advice' 的声明”的问题
作者:用户
浏览:2174 次
有人可以解决一下关于“通配符的匹配很全面,但无法找到元素'tx:advice'的声明”的问题xmlns="http://www.springframework.org/schema/b
有人可以解决一下关于“ 通配符的匹配很全面, 但无法找到元素 'tx:advice' 的声明”的问题
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"&
&!--配置sessionFactory 将数据源注入,并设置hibernate的基本配置
&bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource"&
&property name="driverClassName" value="com..jdbc.Driver"&&/property&
&property name="url" value="jdbc:mysql://localhost:3306/hiber_first"&&/property&
&property name="username" value="root"&&/property&
&property name="password" value="chen007"&&/property&
&bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"&
&property name="dataSource" ref="datasource"&&/property&
&property name="hibernateProperties"&
&prop key="hibernate.dialect"&
org.hibernate.dialect.MySQLDialect
&prop key="hibernate.show_sql"&true&/prop&
&prop key="hibernate.hbm2ddl.auto"&update&/prop&
&/property&
&!-- 配置映射文件, --&
&property name="mappingResources" value="cn/cc/vo/Person.hbm.xml"/&
&!--将SessionFactory 注入DAO,拥有sessionFactory容器后才容易进行presistent操作 --&
&bean id="userDao" class="cn.cc.springIntergratingHibernate.IUserDaoImpl"&
&property name="sessionFactory" ref="sessionFactory"&&/property&
&!--设置事务管理,需要注入sessionFactory
&bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"&
&property name="sessionFactory" ref="sessionFactory" /&
&!--配置事务增强处理器
&tx:advice id="txAdvice" transaction-manager="transactionManager"&
&tx:attributes&
所有以get开头的方法只有只读权限
&tx:method name="get*" read-only="true"/&
其他方法使用默认设置
&tx:method name="*"/&
&/tx:attributes&
&/tx:advice&
&aop:config&
&!--指定在IUserDao切入点
使用txAdvice事务增强处理--&
&aop:pointcut expression="bean(IUserDao)" id="userPointcut"/&
&aop:advisor advice-ref="txAdvice" pointcut-ref="userPointcut"/&
&/aop:config&
&!--配置事务拦截器 transactionInterceptor,需要注入TransactionManager --&
&bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"&
&!--为事务拦截器注入一个管理器
&property name="transactionManager" ref="transactionManager"&&/property&
&property name="transactionAttributes"&
&!--定义事务传播属性
&prop key="save"&PROPAGATION_REQUIRED&/prop&
&prop key="delete"&PROPAGATION_REQUIRED&/prop&
&prop key="check"&PROPAGATION_REQUIRED&/prop&
&/property&
&!--定义BeanNameAutoProxyCreator代理,自动创建相关bean
&bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"&
&!--指定哪些bean自动生成代理
&property name="beanNames"&
&value&userDao&/value&
&/property&
&property name="interceptorNames"&
&value&transactionInterceptor&/value&
&/property&
可以看出来nameSpace已经导入了相关的应用了,但还是找不到
xsi:schemaLocation 加上:
【云栖快讯】云栖社区技术交流群汇总,阿里巴巴技术专家及云栖社区专家等你加入互动,老铁,了解一下?&&
弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率
40+云计算产品,6个月免费体验
稳定可靠、可弹性伸缩的在线数据库服务,全球最受欢迎的开源数据库之一
云服务器9.9元/月,大学必备扫一扫体验手机阅读
解决Spring中使用context:component-scan命名空间配置错误
<span type="1" blog_id="2071954" userid='
39篇文章,3W+人气,0粉丝
大数据时代的微服务之路
¥51.00413人订阅
<span type="1" blog_id="2071954" userid='博客分类:
如下方式可以成功扫描到@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();
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.
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)) {
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.isAnnotated(Profile.class.getName())) {
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
return this.environment.acceptsProfiles(profile.getStringArray("value"));
首先通过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”禁用掉。
中的ContextLoaderListener初始化的上下文和DispatcherServlet初始化的上下文关系。
如果在springmvc配置文件,不使用cn.javass.demo.web.controller前缀,而是使用cn.javass.demo,则service、dao层的bean可能也重新加载了,但事务的AOP代理没有配置在springmvc配置文件中,从而造成新加载的bean覆盖了老的bean,造成事务失效。只要使用use-default-filters=“false”禁用掉默认的行为就可以了。
问题不难,spring使用上的问题。总结一下方便再遇到类似问题的朋友参考。
浏览 83858
论坛回复 /
(2 / 3520)
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
指定了use-default-filters=“false” 是这样的首先通过exclude-filter 进行黑名单过滤;然后通过include-filter 进行白名单过滤;否则默认排除原文中对这个部分的说明,缺少了指定了use-default-filters=“false”这个前提,导致理解很混乱,还好在回复中看到了这个答案
麻烦,开套胸下& 项目部分代码:
public class FileUploadServlet extends HttpServlet {
private FileUploadAction fileUploadA
public void init(ServletConfig config) throws ServletException {
ServletContext servletContext = config.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
fileUploadAction = (FileUploadAction) webApplicationContext.getBean("fileUploadAction");
this.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注入。
首先通过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 进行白名单过滤;否则默认排除
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)这个错误,实在不解。请参考
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)这个错误,实在不解。
jinnianshilongnian 写道sendreams 写道tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢比如使用springmvc时可能有两个容器:1、父容器 ContextLoaderListener加载的 假设此时aop代理了2、springmvc容器加载了,假设此时没有aop代理,那么 很明显 如果controller查找依赖时 查找的是2 而不是1,,这时候如果需要事务支持就失败了
sendreams 写道tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理不好意思,我还是不大明白,系统启动的时候spring就负责各类对象的创建和依赖注入,你说的“已经执行代理的对象”是什么地方发生的呢?tao兄能否说的再清楚一些呢
tao兄,我有点不明白,scan的设置跟事务不起作用有什么关系呢?假设不过滤,全部扫描进去又有什么问题呢?可能覆盖掉已经执行代理的对象,这样新扫描进来的可能不代理
& 上一页 1
jinnianshilongnian
浏览量:1971948
浏览量:2495662
浏览量:5138595
浏览量:198447
浏览量:1411085
浏览量:208199
浏览量:4694787
浏览量:557198
浏览量:609442
Spring源码一步步分析——客户端请求流转Spring MV ...
现在新推出了一个权限框架,叫jCasbin(https://g ...
抛出异常@ExceptionHandler({Unauthor ...
开涛老师你好!请问下你现在还写博客吗?我看到你好久没有更新博客 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'spring通配符的匹配很全面, 但无法找到元素的声明?? - 知乎1被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答1添加评论分享收藏感谢收起spring component-scan
的用法 - ITeye问答
spring component-scan& 的用法
采纳的答案
关于spring自动检测组件的使用方式网上太多了,而且也不是我记录的重点,我想说下一点可能你还不知道的经验
我们知道如果不想在xml文件中配置bean,我们可以给我们的类加上spring组件注解,只需再配置下spring的扫描器就可以实现bean的自动载入。
下面是引用spring framework开发手册中的一段话“
Spring 2.5引入了更多典型化注解(stereotype annotations): @Component、@Service和 @Controller。 @Component是所有受Spring管理组件的通用形式; 而@Repository、@Service和 @Controller则是@Component的细化,用来表示更具体的用例(例如,分别对应了持久化层、服务层和表现层)。也就是说, 你能用@Component来注解你的组件类, 但如果用@Repository、@Service 或@Controller来注解它们,你的类也许能更好地被工具处理,或与切面进行关联。例如,这些典型化注解可以成为理想的切入点目标。当然,在Spring Framework以后的版本中, @Repository、@Service和 @Controller也许还能携带更多语义。如此一来,如果你正在考虑服务层中是该用 @Component还是@Service, 那@Service显然是更好的选择。同样的,就像前面说的那样, @Repository已经能在持久化层中进行异常转换时被作为标记使用了。”
下面是网上目前关于组件扫描最详细的介绍
Spring applicationContext.xml的&context:component-scan&標籤用途比我想像的還要實用。而且後來才知道,有了&context:component-scan&,另一個&context:annotation-config/&標籤根本可以移除掉,因為被包含進去了。原本我survery Spring3通常只配置成&context:component-scan base-package="com.foo.bar"/&,意即在base-package下尋找有@Component和@Configuration的target Class。而現在如下的飯粒:
&context:component-scan base-package="com.foo" use-default-filters="false"&
&context:include-filter type="regex" expression="com.foo.bar.*Config"/&
&context:include-filter type="regex" expression="com.foo.config.*"/&
&/context:component-scan&
  &context:component-scan&提供兩個子標籤:&context:include-filter&和&context:exclude-filter&各代表引入和排除的過濾。而上例把use-default-filters屬性設為false,意即在base-package所有被宣告為@Component和@Configuration等target Class不予註冊為bean,由filter子標籤代勞。
  filter標籤在Spring3有五個type,如下:
Filter Type Examples Expression Description
annotation org.example.SomeAnnotation 符合SomeAnnoation的target class
assignable org.example.SomeClass 指定class或interface的全名
aspectj org.example..*Service+ AspectJ語法
regex org\.example\.Default.* Regelar Expression
custom org.example.MyTypeFilter Spring3新增自訂Type,實作org.springframework.core.type.TypeFilter
  所以上例用的regex就有個語病,com.foo.config.* 可以找到com.foo.config.WebLogger,但也可以找到com1fool2config3abcde,因為小數點在Regex是任意字元,是故要用\.把小數點跳脫為佳。(補充:但要使用\.方式,其use-default-filters不能為false,否則抓不到,感覺是Bug)
  Spring3提供豐富的Filter支援,有益配置策略,不需面臨Configuration Hell,比如Regex的com\.foo\.*\.action\.*Config,這樣就可以找到com.foo package下所有action子package的*Config的target class。
我按他的例子,配置了我自己的如下:
&context:component-scan base-package="com.xhlx.finance.budget"& &
& &context:include-filter type="regex" expression="com.lee.finance.budget.service.*"/&
& &context:include-filter type="regex" expression="com.lee.finance.budget.security.*"/&
&/context:component-scan&
但是死活扫描不到,网上又没有更好的讲解,没办法只好自己试,改成
&context:component-scan base-package="com.xhlx.finance.budget" &
& &context:include-filter type="regex" expression="com.lee.finance.budget.*"/&
&/context:component-scan&
这样连没有加注解的类也扫描并实例化,结果报错,因为有的类根本就没有默认的构造函数不能实例化,能不报错吗
改成
&context:component-scan base-package="com.xhlx.finance.budget"& &
& &context:include-filter type="regex" expression="com\.lee\.finance\.budget\.service.*"/&&
&/context:component-scan&问题依旧
&context:component-scan base-package="com.xhlx.finance.budget" &
& &context:include-filter type="regex" expression="com.lee.finance.budget.service.TestService"/&
&/context:component-scan&
嘿,这次可以了,写全限定名就可以,表达式却不行,但是如果我有成千上百个还得一个一个这样配置啊?不行,继续研究,我想有个base-package的配置,从表面意思来看这是个“基本包”,是否表示下面的过滤路径是基于这个包的呢?于是试着改成
&context:component-scan base-package="com.xhlx.finance.budget" &
& &context:include-filter type="regex" expression=".service.*"/&
&/context:component-scan&
嘿,行了。看来,别人写的东西虽然能给你很多帮助,但也不一定全对啊,我希望每个人都坚持分享实践出来的东西,而不是人云亦云。
以下是从原理出发来解读的,如果楼主,不需要了解,他的实现原理,只想关心他的用处,我推荐楼主
查看一下
Spring MVC 解读---&context:component-scan/&
一、&context:component-scan/&
&&& 想必@Component,@Repository,@Service,@Controller几个常用的Type-Level的Spring MVC注解,大家都很清楚他们的意思跟用途。标记为@Component的类,在使用注解配置的情况下,系统启动时会被自动扫描,并添加到bean工厂中去(省去了配置文件中写bean定义了),另外三个分别表示MVC三层模式中不同层中的组件,他们都是被@Component标记的,所以也会被自动扫描。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//这里。。。
public @interface Repository {
&&& String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//这里。。。
public @interface Service {
&&& String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//这里。。。
public @interface Controller {
&&& String value() default "";
}
&&& 为了达到以上效果,我们还需在xml配置文件中加入如下定义
&context:component-scan base-package="com.springrock..."/&
&&& 这样Spring就可以正确的处理我们定义好的组件了,重要的是这些都是自动的,你甚至不知道他是怎么做的,做了什么?如果不了解反射,可能真的感到吃惊了,但即便如此,我也想知道它到底做了什么?什么时候做的?
二、BeanDefinitionParser
&&& 经过仔细的源码阅读,我找到了这个接口BeanDefinitionParser,文档描述说,它是一个用来处理自定义,顶级(&beans/&的直接儿子标签)标签的接口抽象。可以实现它来将自定义的标签转化为 BeanDefinition类。下面是它的接口定义
BeanDefinition parse(Element element, ParserContext parserContext);
&&& 其中Element是Dom api 中的元素,ParserContext则是用来注册转换来的bean 工厂。
&&& 或许你开始恼火说这么多跟上面有什么关系,好吧,下面便是我真正要说的,我们来看下它有哪些实现类:
&&& 看到了吧,ComponentScanBeanDefinitionParser,正是我们想要的,他就是用来将&context:component-scan/&标签转化为bean 的解析类。那他做了什么呢?
public BeanDefinition parse(Element element, ParserContext parserContext) {
&&&&&&& String[] basePackages = StringUtils.tokenizeToStringArray(
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
&&&&&&&&&&&&&&& ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
&&&&&&& // Actually scan for bean definitions and register them.
&&&&&&& ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
&&&&&&& Set&BeanDefinitionHolder& beanDefinitions = scanner.doScan(basePackages);
&&&&&&& registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
&&&&&&&
&&& }
&& 很明显他会获得&component-scan/&的base-package属性,然后解析所需解析的包路径,然后他会创建一个ClassPathBeanDefinitionScanner对象,并委托它来执行对路径下文件的扫描,然后将获得的BeanDefinitions注册到bean工厂中。是不是很清晰?
&&& 我想你会急切的知道ClassPathBeanDefinitionScanner 做了什么?
protected Set&BeanDefinitionHolder& doScan(String... basePackages) {
&&&&&&& Set&BeanDefinitionHolder& beanDefinitions = new LinkedHashSet&BeanDefinitionHolder&();
&&&&&&& for (String basePackage : basePackages) {
&&&&&&&&&&& //这里是重点,找到候选组件
&&&&&&&&&&& Set&BeanDefinition& candidates = findCandidateComponents(basePackage);
&&&&&&&&&&& for (BeanDefinition candidate : candidates) {
&&&&&&&&&&&&&&& //.....
&&&&&&&&&&&&&&& //.....
&&&&&&&&&&&&&&& if (checkCandidate(beanName, candidate)) {
&&&&&&&&&&&&&&&&&&& BeanDefinitionHolder definitionHolder =
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& new BeanDefinitionHolder(candidate, beanName);
&&&&&&&&&&&&&&&&&&& beanDefinitions.add(definitionHolder);
&&&&&&&&&&&&&&&&&&& //注册到工厂中
&&&&&&&&&&&&&&&&&&& registerBeanDefinition(definitionHolder, this.registry);
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&& }
&&&&&&& return beanD
&&& }
&&& 重点是继承自父类ClassPathScanningCandidateComponentProvider 的findCandidateComponents方法,意思就是找到候选组件,然后注册到工厂中,那么它是怎么找到候选组件的呢?
我们再看看
public Set&BeanDefinition& findCandidateComponents(String basePackage) {
&&&&&&& Set&BeanDefinition& candidates = new LinkedHashSet&BeanDefinition&();
&&&&&&& try {
&&&&&&&&&&& String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
&&&&&&&&&&&&&&&&&&& resolveBasePackage(basePackage) + "/" + this.resourceP
&&&&&&&&&&& Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
&&&&&&&&&&& for (Resource resource : resources) {
&&&&&&&&&&&&&&& if (resource.isReadable()) {
&&&&&&&&&&&&&&&&&&& try {
&&&&&&&&&&&&&&&&&&&&&&& MetadataReader metadataReader = this.metadataReaderFactory.
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& getMetadataReader(resource);
&&&&&&&&&&&&&&&&&&&&&&& if (isCandidateComponent(metadataReader)) {
&&&&&&&&&&&&&&&&&&&&&&&&&&& ScannedGenericBeanDefinition sbd =
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& new ScannedGenericBeanDefinition(metadataReader);
&&&&&&&&&&&&&&&&&&&&&&&&&&& if (isCandidateComponent(sbd)){
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& candidates.add(sbd);
&&&&&&&&&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
&&&&&&& }
&&&&&&&
&&& }
&& 首先获取路径下的资源Resource,然后判断资源是否可读,并且获取可读资源的MetadataReader对象,然后再调用isCandidateComponent(MetadataReader)判段是否是候选组件,如果是,则生成该metadataReader的ScannedGenericBeanDefinition对象。最后判断ScannedGenericBeanDefinition是否为候选的,如果是则添加到工厂中。
三、includeFilters,excludeFilters
&&& 可以看到经历了两次筛选,才找到最终的候选Bean,我们来看第一个过滤做了什么?
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
&&&&&&& for (TypeFilter tf : this.excludeFilters) {//excludeFilters 是什么?
&&&&&&&&&&& if (tf.match(metadataReader, this.metadataReaderFactory)) {
&&&&&&&&&&&&&&&
&&&&&&&&&&& }
&&&&&&& }
&&&&&&& for (TypeFilter tf : this.includeFilters) {//includeFilters 是什么?
&&&&&&&&&&& if (tf.match(metadataReader, this.metadataReaderFactory)) {
&&&&&&&&&&&&&&& AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
&&&&&&&&&&&&&&& if (!metadata.isAnnotated(Profile.class.getName())) {
&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
&&&&&&&&&&&&&&& return this.environment.acceptsProfiles(profile.getStringArray("value"));
&&&&&&&&&&& }
&&&&&&& }
&&&&&&&
&&& }
&&& 我们看到这里有两个实例变量excludeFilters, includeFilters,然后用他们两个去匹配传递进来的MetadataReader,如果与excludeFilter匹配成功返回false, 与includeFilter匹配成功返回true。那么这两个filter分别是什么呢?我们打上断点,调试运行发现
&&& 默认情况下includeFilters是一个含有两个值得List,分别是@Component注解和@ManageBean注解,而excludeFilter是个空List,好吧,现在豁然开朗了吧,原来就是它来筛选我们的@Component标记的类。当然我们可以自定义这两个filters,只需在&context:component-scan/&标签下加两个子标签即可, 像这样:
&context:component-scan base-package="com.springrock"&
&&&&&& &context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/&
&&&&&& &context:include-filter type="annotation" expression="com.springrock.whatever.youcustomized.annotation"/&
&/context:component-scan&
四、BeanDefinitionRegistry
&&& 上面代码中我们看到还有一个isCandidateComponent方法,它主要是判断当前类是否是具体的,而非抽象类和接口,以及是否是可以独立创建的没有依赖的?鉴于与我们目前讨论的主题不相关,所以略去,感兴趣的话,可以自己查看下源码。
&&& 好了,我们既然知道了Spring是怎样通过&context:component-scan/&来扫描,过滤我们的组件了,但是他是怎样将我们定义的组件收集起来供后面的请求处理呢?
&&& 我们来看下上面doScan方法中有
//注册到工厂中
registerBeanDefinition(definitionHolder, this.registry);
&&& 这样一行代码,很明显是将beanDefinition注册到,registry中了。那这个registry是什么呢?是一个BeanDefinitionRegistry,下面是它的接口定义及继承结构:
public interface BeanDefinitionRegistry extends AliasRegistry {
&&& void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
&&&&&&&&&&& throws BeanDefinitionStoreE
&&& void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionE
&&& BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionE
&&& boolean containsBeanDefinition(String beanName);
&&& String[] getBeanDefinitionNames();
&&& int getBeanDefinitionCount();
&&& boolean isBeanNameInUse(String beanName);
}
&&& 我们可以看到接口中定义了诸多beandefinition的注册,删除,获取等方法,并且Spring为我们提供了三个内部实现,那么运行时,使用了那个实现呢?DefaultListableBeanFactory,是的就是它。它就是SpringMVC 中管理Bean的工厂了,我们来看下,它的registerBeanDefinition是怎样实现的?
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
&&&&&&&&&&& throws BeanDefinitionStoreException {
&&&&&&& synchronized (this.beanDefinitionMap) {
&&&&&&&&&&& Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
&&&&&&&&&&& if (oldBeanDefinition != null) {
&&&&&&&&&&&&&&& if (!this.allowBeanDefinitionOverriding) {
&&&&&&&&&&&&&&&&&&& throw new BeanDefinitionStoreException();
&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& else {
&&&&&&&&&&&&&&&&&&& if (this.logger.isInfoEnabled()) {
&&&&&&&&&&&&&&&&&&&&&&& this.logger.info("Overriding bean definition '" + beanName + "]");
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }
&&&&&&&&&&& }
&&&&&&&&&&& else {
&&&&&&&&&&&&&&& this.beanDefinitionNames.add(beanName);
&&&&&&&&&&&&&&& this.frozenBeanDefinitionNames =
&&&&&&&&&&& }
&&&&&&&&&&& this.beanDefinitionMap.put(beanName, beanDefinition);//添加到beanDefinitionMap中了。
&&&&&&& }
&&&&&&& resetBeanDefinition(beanName);
&&& }
&&& 从上面的代码可以看出,所有的beanDefinition都由实例变量beanDefinitionMap来保存管理,他是一个ConcurrentHashMap,beanName作为键,beanDefinition对象作为值。到这我们知道了我们的bean是怎样被注册管理的了。但是问题又来了,我们的系统是在什么时候读取&context:component-scan/&标签,并且扫描我们的bean组件的呢?
当然是从ContextLoaderListener开始了入手分析了。
五、ContextLoader
&&& 我们查看源码(篇幅问题,不贴代码了,很简答)发现ContextLoaderListener将web application context的初始化动作委托给了ContextLoader了,那ContextLoader做了什么呢?
if (this.context == null) {
&&&& this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
&&&& configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& servletContext);
}
&&& 上面的代码片段便是ContextLoader中initWebApplicationContext方法中的关键一段。首先会创建一个WebApplicationContext对象,然后configure 并且refresh这个WebApplicactionContext对象,是不是在这个configureAndRefreshWebApplicationContext方法中进行了配置文件的加载和组件的扫描呢?必须是啊。。。
wac.refresh();
&&& 方法的最后有一个调用了wac的refresh方法,这个wac呢就是前面创建的WebApplicationContext对象,也就是我们这个Web应用的上下文对象。具体是什么呢?我们看一下createWebapplicationContext方法
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
&&&&&&& Class&?& contextClass = determineContextClass(sc);//这里是关键
&&&&&&& ConfigurableWebApplicationContext wac =
&&&&&&&&&&&&&&& (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
&&&&&&&
&&& }
&& 这个方法先确定我们context的类型,调用了determineContextClass方法,
protected Class&?& determineContextClass(ServletContext servletContext) {
&&&&&&& //public static final String CONTEXT_CLASS_PARAM = "contextClass";
&&&&&&& String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
&&&&&&& if (contextClassName != null) {
&&&&&&&&&&& try {
&&&&&&&&&&&&&&& return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
&&&&&&&&&&& }
&&&&&&& }
&&&&&&& else {//defaultStrategies 是关键
&&&&&&&&&&& contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
&&&&&&&&&&& try {
&&&&&&&&&&&&&&& return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
&&&&&&&&&&& }
&&&&&&& }
&&& }
&& 这个方法先判断我们servletContext中有没有contextClass这个初始化属性(在web.xml的init-param标签中配置),通常我们不会配置这个属性。那肯定是null了,所以它接着去查看defaultStrategy中有没有相应属性,那这个defaultStrategy是什么呢?下面是ContextLoader中一个静态代码块,也就说只要ContextLoader被加载,defaultStrategy便会被赋值。
static {
&&&&&&& try {
&&&&&&&&&&& //private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
&&&&&&&&&&& ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ContextLoader.class);
&&&&&&&&&&& defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
&&&&&&& }
&&& }
&&& 很明显,系统是去ClassPath下读取一个Context.properties的属性文件,并赋值给defaultStrategy,这个属性文件如下:
org.springframework.web.context.WebApplicationContext
&&&&&&&&&&&&&&&&&&&&&&&&&&&&& =org.springframework.web.context.support.XmlWebApplicationContext
&& 啊哈,终于找到了,原来是XmlWebApplicationContext啊,这就是我们的WebApplicationContext具体实现对象。
既然找到他了,那我们看看他的refresh()方法做了什么呢?
public void refresh() throws BeansException, IllegalStateException {
&&&&&&& synchronized (this.startupShutdownMonitor) {
&&&&&&&&&&& prepareRefresh();
&&&&&&&&&&& ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
&&&&&&&&&&& prepareBeanFactory(beanFactory);
&&&&&&&&&&& try {
&&&&&&&&&&&&&&& postProcessBeanFactory(beanFactory);
&&&&&&&&&&&&&&& invokeBeanFactoryPostProcessors(beanFactory);
&&&&&&&&&&&&&&& registerBeanPostProcessors(beanFactory);
&&&&&&&&&&&&&&& initMessageSource();
&&&&&&&&&&&&&&& initApplicationEventMulticaster();
&&&&&&&&&&&&&&& onRefresh();
&&&&&&&&&&&&&&& registerListeners();
&&&&&&&&&&&&&&& finishBeanFactoryInitialization(beanFactory);
&&&&&&&&&&&&&&& finishRefresh();
&&&&&&&&&&& }
&&&&&&& }
&&& }
五、Bean Factory&&
这么多代码中,只有第二行与我们当前讨论的主题有关,这一行会尝试获取一个新鲜的BeanFactory,这个BeanFactory与我们之前说的那个BeanDefinitionRegistry有什么关系呢?继续看代码:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
&&&&&&& refreshBeanFactory();
&&&&&&& ConfigurableListableBeanFactory beanFactory = getBeanFactory();
&&&&&&& return beanF
&&& }
&& 在getBeanFactory之前,先进行了一个refreshBeanFactory的操作来刷新当前的BeanFactory,我们以此来看一下:
@Override
&&& protected final void refreshBeanFactory() throws BeansException {
&&&&&&& if (hasBeanFactory()) {
&&&&&&&&&&& destroyBeans();
&&&&&&&&&&& closeBeanFactory();
&&&&&&& }
&&&&&&& try {
&&&&&&&&&&& DefaultListableBeanFactory beanFactory = createBeanFactory();
&&&&&&&&&&& beanFactory.setSerializationId(getId());
&&&&&&&&&&& customizeBeanFactory(beanFactory);
&&&&&&&&&&& loadBeanDefinitions(beanFactory);
&&&&&&&&&&& synchronized (this.beanFactoryMonitor) {
&&&&&&&&&&&&&&& this.beanFactory = beanF
&&&&&&&&&&& }
&&&&&&& }
&&& }
&&& 代码依旧很清晰,先判断有没有BeanFactory,如果有,销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给beanFactory实例变量,有没有发现这个beanFactory是个DefaultListableBeanFactory啊?我们上边讲到的bean definition registry也是个DefaultListableBeanFactory记得吗?他们会不会是同一个呢?答案是yes。重点就在这个loadBeanDefinition(beanFactory)方法上了,很明显:加载Bean Definition到bean工厂中,是不是与我们上边讲到的对上了?
loadBeanDefinition中,Spring会读取xml配置文件,然后会读取里面的bean定义,这一切都是委托给了文章开头的BeanDefinitionParser来完成的,可以看到除了&context:component-scan/&的Parser,还有&mvc:annotation-driven/&的parser,还有&interceptors/&的parser等。是不是比较清晰了?
&&& 当然,我们的问题及好奇心远不止这些,这篇文章只是讲解了其中的一小个:系统的初始化做了什么,在什么时候加载我们定义的beans,我们定义的bean被放到了哪里? 等等,现在问题又来了,我们怎样使用我们的bean呢?或者说如果被标记为@Autowire的属性,是怎样被自动装配的呢?@RequestMapping怎样工作的呢?Spring怎样正确调用controller来处理请求呢?等等,后面的文章我们一一解答。
1.如果不想在xml文件中配置bean,我们可以给我们的类加上spring组件注解,只需再配置下spring的扫描器就可以实现bean的自动载入。
&!-- 注解注入 --&
&context:annotation-config&&/context:annotation-config&
&context:component-scan base-package="com.liantuo.hotel.common.service.impl" /&
&context:component-scan base-package="com.liantuo.hotel.common.dao.ibatis" /&
&context:component-scan base-package="com.liantuo.hotel.app.dao.ibatis" /&
&context:component-scan base-package="com.liantuo.hotel.app.service" /&
&context:component-scan base-package="com.liantuo.hotel.app.service.ibatis" /&
2.下面是引用spring framework开发手册中的一段话“
Spring 2.5引入了更多典型化注解(stereotype annotations): @Component、@Service和 @Controller。@Component是所有受Spring管理组件的通用形式;而@Repository、@Service和 @Controller则是@Component的细化,用来表示更具体的用例(例如,分别对应了持久化层、服务层和表现层)。也就是说,你能用@Component来注解你的组件类,但如果用@Repository、@Service 或@Controller来注解它们,你的类也许能更好地被工具处理,或与切面进行关联。例如,这些典型化注解可以成为理想的切入点目标。当然,在Spring Framework以后的版本中, @Repository、@Service和 @Controller也许还能携带更多语义。如此一来,如果你正在考虑服务层中是该用@Component还是@Service,那@Service显然是更好的选择。同样的,就像前面说的那样, @Repository已经能在持久化层中进行异常转换时被作为标记使用了。”
3.有了&context:component-scan&,另一个&context:annotation-config/&标签根本可以移除掉,因为已经被包含进去了。
4.&context:component-scan&提供两个子标签:&context:include-filter&和&context:exclude-filter&各代表引入和排除的过滤。
如:&context:component-scan base-package="com.xhlx.finance.budget" &
&context:include-filter type="regex" expression=".service.*"/&
&/context:component-scan&
已解决问题
未解决问题}

我要回帖

更多关于 通配符匹配 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信