自定义命名空间解析 当我们想要扩展Spring 的配置时,可以定义我们自己的标签,然后解析成 bean定义,注册到Spring 容器中。这时需要用到 NamespaceHandler。Spring 框架中除了beans顶级标签外,其他的顶级标签都是自定义命名空间中的标签。如,p、 c、util命名空间。
NamespaceHandler 命名空间解析器Spring 框架中mvc、context、tx 等功能都是通过扩展这个接口来实现的。这个接口负责将标签解析成bean 定义对象。该接口提供了parse 和decorate 方法。parse方法用来将顶级标签解析成BeanDefinition对象。decorate 方法负责对parse出来的BeanDefinition进行进一步处理,需要解析的可以是元素属性和标签。 可以返回原来的BeanDefinition,或者返回一个新的BeanDefinition。
为了解析的方便,Spring 提供了一个抽象类NamespaceHandlerSupport,封装了一些基础功能,并提供了两个新的接口(BeanDefinitionParser和BeanDefinitionDecorator)方便扩展。并提供了registerBeanDefinitionDecorator、registerBeanDefinitionParser、registerBeanDefinitionDecoratorForAttribute三个方法分别注册BeanDefinitionParser和BeanDefinitionDecorator实现类,内部用map来维护标签名或者属性名到BeanDefinitionParser和BeanDefinitionDecorator的映射关系,解析时用名称(无命名空间前缀)来查找。
Spring提供的NamespaceHandler Spring 提供了一些 NamespaceHandler来实现自身功能。下面详细了解一下。
SimplePropertyNamespaceHandler 简单属性解析器解析的命名空间为 http://www.springframework.org/schema/p。它将特定属性直接映射到bean属性。需要注意的重要一点是,NamespaceHandler无法预知所有可能的属性名。该实现类直接实现了NamespaceHandler。
下面是使用NamespaceHandler的一个例子: 
1 <bean  id  = "rob"  class  = "..TestBean"  p:name ="Rob"  p:spouse-ref ="sally" /> 
这里的p:name直接对应于类TestBean上的name属性。p:spouse-ref属性对应于spouse属性,将value 所对应的bean注入到该属性中。
parse方法直接记录日志信息,并返回 null。因为它只支持对属性进行解析。
1 2 3 4 5 public  BeanDefinition parse (Element element, ParserContext parserContext)  		parserContext.getReaderContext().error( 				"Class ["  + getClass().getName() + "] does not support custom elements." , element); 		return  null ; 	} 
decorate将元素属性表示的属性设置到对应的对象属性中。每个属性都会调用一次这个方法。拿上文提到的例子来解释一下下面代码的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public  BeanDefinitionHolder decorate (Node node, BeanDefinitionHolder definition, ParserContext parserContext)     if  (node instanceof  Attr) {               Attr attr = (Attr) node;               String propertyName = parserContext.getDelegate().getLocalName(attr);               String propertyValue = attr.getValue();               MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();               if  (pvs.contains(propertyName)) {          parserContext.getReaderContext().error("Property '"  + propertyName + "' is already defined using "  +                "both <property> and inline syntax. Only one approach may be used per property." , attr);       }               if  (propertyName.endsWith(REF_SUFFIX)) {          propertyName = propertyName.substring(0 , propertyName.length() - REF_SUFFIX.length());                     pvs.add(Conventions.attributeNameToPropertyName(propertyName), new  RuntimeBeanReference(propertyValue));       }       else  {                     pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);       }    }    return  definition; } 
小结:用这个自定义属性来替换<property>使用起来更方便。 
SimpleConstructorNamespaceHandler 简单构造函数解析器将自定义属性映射到构造函数参数。解析的命名空间地址为http://www.springframework.org/schema/c。需要注意该解析器无法预知所有的参数。
下面看一个简单例子:
1 <bean  id ="author"  class ="..TestBean"  c:name ="Enescu"  c:work-ref ="compositions" /> 
将name映射到TestBean的构造函数name参数,值为Enescu,而work参数引用了compositions bean。
下面看一下源代码:
parse 方法不支持解析标签
1 2 3 4 5 public  BeanDefinition parse (Element element, ParserContext parserContext)     parserContext.getReaderContext().error(          "Class ["  + getClass().getName() + "] does not support custom elements." , element);    return  null ; } 
decorate 方法, 每个参数都会调用一次这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 public  BeanDefinitionHolder decorate (Node node, BeanDefinitionHolder definition, ParserContext parserContext)     if  (node instanceof  Attr) {               Attr attr = (Attr) node;               String argName = StringUtils.trimWhitespace(parserContext.getDelegate().getLocalName(attr));               String argValue = StringUtils.trimWhitespace(attr.getValue());       ConstructorArgumentValues cvs = definition.getBeanDefinition().getConstructorArgumentValues();       boolean  ref = false ;              if  (argName.endsWith(REF_SUFFIX)) {                     ref = true ;          argName = argName.substring(0 , argName.length() - REF_SUFFIX.length());       }               ValueHolder valueHolder = new  ValueHolder(ref ? new  RuntimeBeanReference(argValue) : argValue);       valueHolder.setSource(parserContext.getReaderContext().extractSource(attr));              if  (argName.startsWith(DELIMITER_PREFIX)) {                    String arg = argName.substring(1 ).trim();                    if  (!StringUtils.hasText(arg)) {                           cvs.addGenericArgumentValue(valueHolder);          }                    else  {                           int  index = -1 ;             try  {                index = Integer.parseInt(arg);                              }             catch  (NumberFormatException ex) {                parserContext.getReaderContext().error(                      "Constructor argument '"  + argName + "' specifies an invalid integer" , attr);             }             if  (index < 0 ) {                                 parserContext.getReaderContext().error(                      "Constructor argument '"  + argName + "' specifies a negative index" , attr);             }             if  (cvs.hasIndexedArgumentValue(index)) {                                 parserContext.getReaderContext().error(                      "Constructor argument '"  + argName + "' with index " + index+" already defined using <constructor-arg>."  +                      " Only one approach may be used per argument." , attr);             } 			             cvs.addIndexedArgumentValue(index, valueHolder);          }       }              else  {                     String name = Conventions.attributeNameToPropertyName(argName);          if  (containsArgWithName(name, cvs)) {                           parserContext.getReaderContext().error(                   "Constructor argument '"  + argName + "' already defined using <constructor-arg>."  +                   " Only one approach may be used per argument." , attr);          }          valueHolder.setName(Conventions.attributeNameToPropertyName(argName));          cvs.addGenericArgumentValue(valueHolder);       }    }    return  definition; } 
小结:配置构造函数的方式 
使用<constructor-arg>标签,可以使用名称来定义,也可以使用下标。
在<bean>标签中使用c:自定义属性, 属性名有两种方式:
下标形式_0  
构造函数参数名形式c:name="value" 
 
如果参数值需要引用bean,那么需要在属性名后加上_ref表示引用。
 
两种方式对比:
方式 
名称 
下标 
 
 
<constructor-arg>标签name=“name” value=“value” (ref=“beanid”) 
index=“0” value=“value”(ref=“beanid”) 
 
c:属性c: name=“value”(c:name_ref=“beanid”) 
c:_0=“value” (c:_0_ref=“beanid”) 
 
 
NamespaceHandlerSupport 命名空间解析器支持类实现自定义NamespaceHandler的支持类。各个节点的解析和装饰分别通过BeanDefinitionParser和BeanDefinitionDecorator策略接口完成。提供registerBeanDefinitionParser和registerBeanDefinitionDecorator方法,用于注册BeanDefinitionParser或BeanDefinitionDecorator来处理特定元素。
BeanDefinitionParserDefaultBeanDefinitionDocumentReader用于处理自定义顶级标签的接口。实现类可以根据需要自由地将自定义标签中的元数据转换成任意多的bean定义。
AbstractBeanDefinitionParser 类BeanDefinitionParser实现类,该类提供了许多方便的方法和模板方法,子类必须重写这些方法才能提供实际的解析逻辑。
当你想将任意复杂的XML解析为一个或多个bean定义时,请使用这个BeanDefinitionParser实现。如果只是想将一些XML解析为一个bean定义,那么可以考虑该类更简单的扩展类,即 AbstractSingleBeanDefinitionParser 和 AbstractSimpleBeanDefinitionParser。
parse 方法parse 方法是该解析器的入口,定义了整个解析的流程以及子类可以扩展的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public  final  BeanDefinition parse (Element element, ParserContext parserContext)          AbstractBeanDefinition definition = parseInternal(element, parserContext);    if  (definition != null  && !parserContext.isNested()) {               try  {                     String id = resolveId(element, definition, parserContext);          if  (!StringUtils.hasText(id)) {             parserContext.getReaderContext().error(                   "Id is required for element '"  + parserContext.getDelegate().getLocalName(element)                         + "' when used as a top-level tag" , element);          }          String[] aliases = null ;          if  (shouldParseNameAsAliases()) {                           String name = element.getAttribute(NAME_ATTRIBUTE);             if  (StringUtils.hasLength(name)) {                aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));             }          }          BeanDefinitionHolder holder = new  BeanDefinitionHolder(definition, id, aliases);                     registerBeanDefinition(holder, parserContext.getRegistry());          if  (shouldFireEvents()) {                           BeanComponentDefinition componentDefinition = new  BeanComponentDefinition(holder);             postProcessComponentDefinition(componentDefinition);                           parserContext.registerComponent(componentDefinition);          }       }       catch  (BeanDefinitionStoreException ex) {          String msg = ex.getMessage();          parserContext.getReaderContext().error((msg != null  ? msg : ex.toString()), element);          return  null ;       }    }    return  definition; } 
小结 解析流程:
调用parseInternal 方法将 xml 解析成BeanDefinition,该方法由子类去实现
调用resolveId方法得到bean 定义id,可以由子类重写
别名解析
注册bean 定义
发布事件,实现ReaderEventListener 进行监听
 
AbstractSingleBeanDefinitionParser 类只需要解析和定义一个bean定义的BeanDefinitionParser实现基类,继承自AbstractBeanDefinitionParser类,实现parseInternal方法,近一步细化解析逻辑。
只需要从任意复杂的XML元素创建单个bean定义时,可以扩展这个解析器类。当你想从一个相对简单的自定义XML元素创建一个bean定义时,可以考虑扩展AbstractSimpleBeanDefinitionParser类。
生成的bean定义将自动注册到org.springframework.beans.factory.support.BeanDefinitionRegistry。只需要将自定义XML元素解析为一个bean定义就可以了。
parseInternal方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = getParentName(element); if  (parentName != null ) {        builder.getRawBeanDefinition().setParentName(parentName); } Class<?> beanClass = getBeanClass(element); if  (beanClass != null ) {   builder.getRawBeanDefinition().setBeanClass(beanClass); } else  {        String beanClassName = getBeanClassName(element);    if  (beanClassName != null ) {       builder.getRawBeanDefinition().setBeanClassName(beanClassName);    } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); BeanDefinition containingBd = parserContext.getContainingBeanDefinition(); if  (containingBd != null ) {            builder.setScope(containingBd.getScope()); } if  (parserContext.isDefaultLazyInit()) {       builder.setLazyInit(true ); } doParse(element, parserContext, builder); return  builder.getBeanDefinition();
小结 解析流程:
创建builder
设置父级定义名称
设置要转换成bean类,通过getBeanClass方法或者getBeanClassName
根据父级bean定义设置scope
设置lazyInit属性
调用doParse方法将xml对应的相关属性设置到builder中
获取BeanDefinition
对于只需要将xml映射为bean这样简单的解析情形,可以继承这个类实现doParse方法,将xml的属性设置到BeanDefinitionBuilder中即可。
 
ContextNamespaceHandler上下文命名空间解析器继承自NamespaceHandlerSupport类,在init方法中注册了context命名空间下的标签解析类。用来解析命名空间地址http://www.springframework.org/schema/context。
context命名空间下的自定义标签init方法为每个标签都注册了一个解析类
标签 
解析类 
 
 
property-placeholderorg.springframework.context.config.PropertyPlaceholderBeanDefinitionParser 
property-overrideorg.springframework.context.config.PropertyOverrideBeanDefinitionParser 
annotation-configorg.springframework.context.annotation.AnnotationConfigBeanDefinitionParser 
component-scanorg.springframework.context.annotation.ComponentScanBeanDefinitionParser 
load-time-weaverorg.springframework.context.config.LoadTimeWeaverBeanDefinitionParser 
spring-configuredorg.springframework.context.config.SpringConfiguredBeanDefinitionParser 
mbean-exportorg.springframework.context.config.MBeanExportBeanDefinitionParser 
mbean-serverorg.springframework.context.config.MBeanServerBeanDefinitionParser 
property-placeholder标签解析org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser 类用来解析该标签。继承自AbstractPropertyLoadingBeanDefinitionParser, 该类又继承自AbstractSingleBeanDefinitionParser,因此只返回单个BeanDefinition。
AbstractPropertyLoadingBeanDefinitionParser的doParse方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 protected  void  doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder)     String location = element.getAttribute("location" );    if  (StringUtils.hasLength(location)) {       location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location);       String[] locations = StringUtils.commaDelimitedListToStringArray(location);       builder.addPropertyValue("locations" , locations);    }    String propertiesRef = element.getAttribute("properties-ref" );    if  (StringUtils.hasLength(propertiesRef)) {       builder.addPropertyReference("properties" , propertiesRef);    }    String fileEncoding = element.getAttribute("file-encoding" );    if  (StringUtils.hasLength(fileEncoding)) {       builder.addPropertyValue("fileEncoding" , fileEncoding);    }    String order = element.getAttribute("order" );    if  (StringUtils.hasLength(order)) {       builder.addPropertyValue("order" , Integer.valueOf(order));    }    builder.addPropertyValue("ignoreResourceNotFound" ,          Boolean.valueOf(element.getAttribute("ignore-resource-not-found" )));    builder.addPropertyValue("localOverride" ,          Boolean.valueOf(element.getAttribute("local-override" )));    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); } 
可以看出只是简单的将xml标签的属性取出并设置到builder中。
再看看PropertyPlaceholderBeanDefinitionParser的doParse方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protected  void  doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder)     super .doParse(element, parserContext, builder);    builder.addPropertyValue("ignoreUnresolvablePlaceholders" ,          Boolean.valueOf(element.getAttribute("ignore-unresolvable" )));    String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE);    if  (StringUtils.hasLength(systemPropertiesModeName) &&          !systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {               builder.addPropertyValue("systemPropertiesModeName" , "SYSTEM_PROPERTIES_MODE_"  + systemPropertiesModeName);    }    if  (element.hasAttribute("value-separator" )) {       builder.addPropertyValue("valueSeparator" , element.getAttribute("value-separator" ));    }    if  (element.hasAttribute("trim-values" )) {       builder.addPropertyValue("trimValues" , element.getAttribute("trim-values" ));    }    if  (element.hasAttribute("null-value" )) {       builder.addPropertyValue("nullValue" , element.getAttribute("null-value" ));    } } 
返回的bean :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected  Class<?> getBeanClass(Element element) {                   if  (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {               return  PropertySourcesPlaceholderConfigurer.class;    }                 return  PropertyPlaceholderConfigurer.class; } 
小结 从property-placeholder标签中解析出的属性:
xml属性 
bean 属性 
 
 
location 
locations 
 
properties-ref 
properties 
 
file-encoding 
fileEncoding 
 
order 
order 
 
ignore-resource-not-found 
ignoreResourceNotFound 
 
local-override 
localOverride 
 
ignore-unresolvable 
ignoreUnresolvablePlaceholders 
 
system-properties-mode 
systemPropertiesModeName 
 
value-separator 
valueSeparator 
 
trim-values 
trimValues 
 
null-value 
nullValue 
 
property-override标签解析org.springframework.context.config.PropertyOverrideBeanDefinitionParser类用来解析该标签,与property-placeholder标签类似,该类也继承自AbstractPropertyLoadingBeanDefinitionParser,与property-placeholder标签有一些共有的属性。
doParse方法:
1 2 3 4 5 6 7 8 @Override protected  void  doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder)     super .doParse(element, parserContext, builder);    builder.addPropertyValue("ignoreInvalidKeys" ,          Boolean.valueOf(element.getAttribute("ignore-unresolvable" ))); } 
getBeanClass方法:
1 2 3 protected  Class<?> getBeanClass(Element element) {   return  PropertyOverrideConfigurer.class; } 
小结 从property-override标签中解析出的属性:
xml属性 
bean 属性 
 
 
location 
locations 
 
properties-ref 
properties 
 
file-encoding 
fileEncoding 
 
order 
order 
 
ignore-resource-not-found 
ignoreResourceNotFound 
 
local-override 
localOverride 
 
ignore-unresolvable 
ignoreInvalidKeys 
 
annotation-config标签解析org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser用来解析该标签。该类直接实现了BeanDefinitionParser接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public  BeanDefinition parse (Element element, ParserContext parserContext)     Object source = parserContext.extractSource(element);                       Set<BeanDefinitionHolder> processorDefinitions =          AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);             CompositeComponentDefinition compDefinition = new  CompositeComponentDefinition(element.getTagName(), source);              parserContext.pushContainingComponent(compDefinition);             for  (BeanDefinitionHolder processorDefinition : processorDefinitions) {       parserContext.registerComponent(new  BeanComponentDefinition(processorDefinition));    }             parserContext.popAndRegisterContainingComponent();         return  null ; } 
从上面的代码中可以看到,该解析器注册了几个BeanPostProcessor, 用来处理注解配置,这里先不展开,后面再具体看一下这几个类的作用。其实整个解析器关键的代码只有这一句:
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
 
后面的代码用来保证整个解析链的完整性,需要发布解析完成的事件。
component-scan标签解析org.springframework.context.annotation.ComponentScanBeanDefinitionParser 类用来解析该标签。该类直接实现了BeanDefinitionParser接口。
parse方法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  BeanDefinition parse (Element element, ParserContext parserContext)          String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);         basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);         String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);                  ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);         Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);         registerComponents(parserContext.getReaderContext(), beanDefinitions, element);    return  null ; } 
小结 该解析器主要是配置一个ClassPathBeanDefinitionScanner,然后调用其doScan方法,默认扫描@Component注解的类,并且可以配置过滤器调整扫描范围。解析出来的扫描路径以classpath*:开头,因此可以扫描到jar里面的注解。
load-time-weaver标签解析org.springframework.context.config.LoadTimeWeaverBeanDefinitionParser标签用来解析该标签。继承自org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser,返回一个bean定义。
getBeanClassName方法:
1 2 3 4 5 6 7 protected  String getBeanClassName (Element element)     if  (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {       return  element.getAttribute(WEAVER_CLASS_ATTRIBUTE);    }         return  DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME; } 
doParse方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 protected  void  doParse (Element element, ParserContext parserContext, BeanDefinitionBuilder builder)     builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);    if  (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {               if  (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {                     RootBeanDefinition def = new  RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);          parserContext.registerBeanComponent(                new  BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME));       }       if  (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {                                new  SpringConfiguredBeanDefinitionParser().parse(element, parserContext);       }    } 
该标签由org.springframework.context.config.SpringConfiguredBeanDefinitionParser来解析。
parse方法:
1 2 3 4 5 6 7 8 9 10 11 12 public  BeanDefinition parse (Element element, ParserContext parserContext)  if (!parserContext.getRegistry().containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) {          RootBeanDefinition def = new  RootBeanDefinition();              def.setBeanClassName(BEAN_CONFIGURER_ASPECT_CLASS_NAME);       def.setFactoryMethodName("aspectOf" );       def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);       def.setSource(parserContext.extractSource(element));       parserContext.registerBeanComponent(new  BeanComponentDefinition(def, BEAN_CONFIGURER_ASPECT_BEAN_NAME));    }    return  null ; } 
AopNamespaceHandler Aop 命名空间解析器用于aop命名空间的NamespaceHandler。继承自NamespaceHandlerSupport,注册以下BeanDefinitionParser:
标签 
解析类 
 
 
configConfigBeanDefinitionParser 
aspectj-autoproxyAspectJAutoProxyBeanDefinitionParser 
注册BeanDefinitionDecorator:
属性 
解析类 
 
 
scoped-proxyScopedProxyBeanDefinitionDecorator 
为<aop:config>标签提供一个BeanDefinitionParser。config标签可以包含嵌套的pointcut、advisor和aspect标签。
pointcut标签允许使用简单的语法创建命名的AspectJExpressionPointcut bean:
1 <aop:pointcut  id ="getNameCalls"  expression ="execution(* *..ITestBean.getName(..))" /> 
使用advisor标签,可以配置org.springframework.aop.Advisor并自动将其应用于org.springframework.beans.factory.BeanFactory中所有相关bean。advisor标签支持内联和引用pointcut:
1 2 3 4 5 6 7 <aop:advisor  id ="getAgeAdvisor"               pointcut ="execution(* *..ITestBean.getAge(..))"               advice-ref ="getAgeCounter" /> <aop:advisor  id ="getNameAdvisor"               pointcut-ref ="getNameCalls"               advice-ref ="getNameCounter" /> 
<aop:config>标签解析org.springframework.aop.config.ConfigBeanDefinitionParser类用来解析该标签。实现了BeanDefinitionParser 接口
parse方法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public  BeanDefinition parse (Element element, ParserContext parserContext)     CompositeComponentDefinition compositeDef =          new  CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));         parserContext.pushContainingComponent(compositeDef);         configureAutoProxyCreator(parserContext, element);         List<Element> childElts = DomUtils.getChildElements(element);    for  (Element elt: childElts) {       String localName = parserContext.getDelegate().getLocalName(elt);       if  (POINTCUT.equals(localName)) {          parsePointcut(elt, parserContext);       }       else  if  (ADVISOR.equals(localName)) {          parseAdvisor(elt, parserContext);       }       else  if  (ASPECT.equals(localName)) {          parseAspect(elt, parserContext);       }    }         parserContext.popAndRegisterContainingComponent();    return  null ; } 
parsePointcut 方法解析切入点1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private  AbstractBeanDefinition parsePointcut (Element pointcutElement, ParserContext parserContext)          String id = pointcutElement.getAttribute(ID);         String expression = pointcutElement.getAttribute(EXPRESSION);    AbstractBeanDefinition pointcutDefinition = null ;    try  {               this .parseState.push(new  PointcutEntry(id));               pointcutDefinition = createPointcutDefinition(expression);       pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));       String pointcutBeanName = id;               if  (StringUtils.hasText(pointcutBeanName)) {          parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);       }       else  {          pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);       }               parserContext.registerComponent(             new  PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));    }    finally  {               this .parseState.pop();    }    return  pointcutDefinition; } 
parseAdvisor 解析advisor标签1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 private  void  parseAdvisor (Element advisorElement, ParserContext parserContext)          AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);    String id = advisorElement.getAttribute(ID);    try  {       this .parseState.push(new  AdvisorEntry(id));       String advisorBeanName = id;               if  (StringUtils.hasText(advisorBeanName)) {          parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);       }       else  {          advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);       }               Object pointcut = parsePointcutProperty(advisorElement, parserContext);               if  (pointcut instanceof  BeanDefinition) {          advisorDef.getPropertyValues().add(POINTCUT, pointcut);          parserContext.registerComponent(                new  AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));       }       else  if  (pointcut instanceof  String) {          advisorDef.getPropertyValues().add(POINTCUT, new  RuntimeBeanReference((String) pointcut));          parserContext.registerComponent(                new  AdvisorComponentDefinition(advisorBeanName, advisorDef));       }    }    finally  {       this .parseState.pop();    } } 
parseAspect 解析切面1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 private  void  parseAspect (Element aspectElement, ParserContext parserContext)     String aspectId = aspectElement.getAttribute(ID);         String aspectName = aspectElement.getAttribute(REF);    try  {       this .parseState.push(new  AspectEntry(aspectId, aspectName));       List<BeanDefinition> beanDefinitions = new  ArrayList<>();       List<BeanReference> beanReferences = new  ArrayList<>();               List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);       for  (int  i = METHOD_INDEX; i < declareParents.size(); i++) {          Element declareParentsElement = declareParents.get(i);          beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));       }                             NodeList nodeList = aspectElement.getChildNodes();       boolean  adviceFoundAlready = false ;       for  (int  i = 0 ; i < nodeList.getLength(); i++) {          Node node = nodeList.item(i);          if  (isAdviceNode(node, parserContext)) {                           if  (!adviceFoundAlready) {                adviceFoundAlready = true ;                if  (!StringUtils.hasText(aspectName)) {                   parserContext.getReaderContext().error(                         "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices." ,                         aspectElement, this .parseState.snapshot());                   return ;                }                beanReferences.add(new  RuntimeBeanReference(aspectName));             }                           AbstractBeanDefinition advisorDefinition = parseAdvice(                   aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);             beanDefinitions.add(advisorDefinition);          }       }       AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(             aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);       parserContext.pushContainingComponent(aspectComponentDefinition);       List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);               for  (Element pointcutElement : pointcuts) {          parsePointcut(pointcutElement, parserContext);       }       parserContext.popAndRegisterContainingComponent();    }    finally  {       this .parseState.pop();    } } 
小结 注册的BeanDefinition如下:
标签 
BeanDefinition 
 
 
config 
AspectJAwareAdvisorAutoProxyCreator 
pointcut 
AspectJExpressionPointcut 
 
advisor 
DefaultBeanFactoryPointcutAdvisor 
 
before,after,after-returning,after-throwing,around 
AspectJPointcutAdvisor 
 
declare-parents 
DeclareParentsAdvisor 
 
<aop:aspectj-autoproxy>标签解析AspectJAutoProxyBeanDefinitionParser类用来解析该标签,实现了BeanDefinitionParser接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public  BeanDefinition parse (Element element, ParserContext parserContext)          AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);    extendBeanDefinition(element, parserContext);    return  null ; } private  void  extendBeanDefinition (Element element, ParserContext parserContext)     BeanDefinition beanDef =          parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);    if  (element.hasChildNodes()) {               addIncludePatterns(element, parserContext, beanDef);    } } private  void  addIncludePatterns (Element element, ParserContext parserContext, BeanDefinition beanDef)     ManagedList<TypedStringValue> includePatterns = new  ManagedList<>();    NodeList childNodes = element.getChildNodes();    for  (int  i = 0 ; i < childNodes.getLength(); i++) {       Node node = childNodes.item(i);       if  (node instanceof  Element) {          Element includeElement = (Element) node;          TypedStringValue valueHolder = new  TypedStringValue(includeElement.getAttribute("name" ));          valueHolder.setSource(parserContext.extractSource(includeElement));          includePatterns.add(valueHolder);       }    }    if  (!includePatterns.isEmpty()) {       includePatterns.setSource(parserContext.extractSource(element));       beanDef.getPropertyValues().add("includePatterns" , includePatterns);    } } 
小结 注册的BeanDefinition如下:
标签 
BeanDefinition 
 
 
aspectj-autoproxy 
AnnotationAwareAspectJAutoProxyCreator 
 
MvcNamespaceHandler解析MVC命名空间该类init方法中注册了下面的解析器:
annotation-driven 
AnnotationDrivenBeanDefinitionParser 
 
 
default-servlet-handler 
DefaultServletHandlerBeanDefinitionParser 
 
interceptors 
InterceptorsBeanDefinitionParser 
 
resources 
ResourcesBeanDefinitionParser 
 
view-controller 
ViewControllerBeanDefinitionParser 
 
redirect-view-controller 
ViewControllerBeanDefinitionParser 
 
status-controller 
ViewControllerBeanDefinitionParser 
 
view-resolvers 
ViewResolversBeanDefinitionParser 
 
tiles-configurer 
TilesConfigurerBeanDefinitionParser 
 
freemarker-configurer 
FreeMarkerConfigurerBeanDefinitionParser 
 
groovy-configurer 
GroovyMarkupConfigurerBeanDefinitionParser 
 
script-template-configurer 
ScriptTemplateConfigurerBeanDefinitionParser 
 
cors 
CorsBeanDefinitionParser 
 
后面再专门写一篇讲MVC的。
自定义命名空间示例 自定义命名空间可以分为步:
定义NamespaceHandler 
定义命名空间地址,创建META-INF/spring.handlers文件,注册NamespaceHandler 
如果NamespaceHandler继承自NamespaceHandlerSupport,则定义BeanDefinitionParser。用来解析具体的标签 
 
自定义NamespaceHandler 为了方便,通常是继承NamespaceHandlerSupport。在init方法中注册标签解析器。
1 2 3 4 5 6 public  class  MyNamespaceHandler  extends  NamespaceHandlerSupport      @Override      public  void  init ()           registerBeanDefinitionParser("start-up" ,new  MyNamespaceStartUpBeanDefinitionParser());     } } 
注册NamespaceHandler 在META-INF目录下创建spring.handlers文件,文件内容为:
1 http\://www.springframework.org/schema/my =cn.sexycode.spring.study.chapter3.MyNamespaceHandler 
Spring 在会读到自定义命名空间时,从这个文件中解析对应的handler。= 前面为命名空间地址
实现BeanDefinitionParser 由于handler继承自NamespaceHandlerSupport,那么我们可以实现这个接口用来解析标签。Spring 为我们提供了一个抽象类,用来只返回一个bean 定义的时候使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  MyNamespaceStartUpBeanDefinitionParser  extends  AbstractSingleBeanDefinitionParser      @Override      protected  Class<?> getBeanClass(Element element) {                  return  MyBeanPostProcessor.class;     }     @Override      protected  boolean  shouldGenerateId ()                    return  true ;     } } 
使用示例 上述几步之后,我们就可以在例子中使用了。xml文件:
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="UTF-8"?> <beans  xmlns ="http://www.springframework.org/schema/beans"         xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"         xmlns:my ="http://www.springframework.org/schema/my"         xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >     <my:start-up />  </beans > 
因为我们没有定义dtd,导致无法通过校验。我们使用时可以设置为不校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  class  MyNamespaceHandlerDemo      public  static  void  main (String[] args)                DefaultListableBeanFactory factory = new  DefaultListableBeanFactory();                  XmlBeanDefinitionReader reader = new  XmlBeanDefinitionReader(factory);                  reader.setValidating(false );                  reader.loadBeanDefinitions("MyNamespaceHandlerDemo.xml" );         System.out.println(factory.getBean(MyBeanPostProcessor.class));     } } 
cn.sexycode.spring.study.chapter3.MyBeanPostProcessor@78b1cc93
 
可以看到已经正确注册到容器中。