自定义Spring配置标签

引言:

在Sping中,一般使用<bean>这样的元素来配置一个bean,Spring在创建容器的时候会扫描这些配置,根据配置创建对象存放于容器中,然后我们再从容器中取出,或者在配置其他bean的时候作为属性注入。使用bean配置的一个限制是我们必须遵循配置文件的XML Schema定义,这在大多数情况下不会出现问题。但是在一些情况下,我们希望实现更为灵活的bean配置。Spring为此提供了 Custom tag Support,也称为Extensible XML Authoring。通过这个拓展点,我们可以灵活定制自己需要的配置格式。


例如,如果我们使用了责任链设计应用程序,那么我们可能希望用下面的方式来配置责任链:

[html]  view plain  copy
  1. <chain id="orderChain" class="foo.bar">  
  2.     <handler> handler1</handler>  
  3.     <hadnler> handler2</handler>  
  4. </chain>  

档Spring创建容器时,扫描到这样的元素的时候,会根据我们事先的定义实例化一个责任链对象,并填充属性。因此,这种特殊的<chain>标签可以作为<bean>标签以外的另一种形式。借助Spring的Custome Tag,我们完全可以实现这样的bean配置。在产品级的应用框架中,可以实现更为复杂的定制标签元素。作为一个入门级别的介绍,我们定义一个用于配置日期格式化的一个类SimpleDateFormat。当然,使用传统的<bean>完全够用,我们这里只是作为例子。


一个HelloWorld例子:

定制标签的第一步是要定义标签元素的XML结构,也就是采用XSD来元素我们要定制的元素的结构时怎样的。我们定义如下一个简单的XSD:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <xsd:schema xmlns="http://www.mycompany.com/schema/myns"  
  3.         xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
  4.         xmlns:beans="http://www.springframework.org/schema/beans"  
  5.         targetNamespace="http://www.mycompany.com/schema/myns"  
  6.         elementFormDefault="qualified"  
  7.         attributeFormDefault="unqualified">  
  8.   
  9.     <xsd:import namespace="http://www.springframework.org/schema/beans"/>  
  10.   
  11.     <xsd:element name="dateformat">  
  12.         <xsd:complexType>  
  13.             <xsd:complexContent>  
  14.                 <xsd:extension base="beans:identifiedType">  
  15.                     <xsd:attribute name="lenient" type="xsd:boolean"/>  
  16.                     <xsd:attribute name="pattern" type="xsd:string" use="required"/>  
  17.                 </xsd:extension>  
  18.             </xsd:complexContent>  
  19.         </xsd:complexType>  
  20.     </xsd:element>  
  21. </xsd:schema>  
在这个XSD定义中,有一个标签叫dateformat,这就是我们用来替换bean标签的自定义标签。注意到我们导入了Spring本身的beans命名空间,并且在beans:identifiedType基础之上定义dateformat标签。也就是我们这个标签可以像<bean>标签一样拥有id属性。同时我们增加了两个属性lenient和pattern。这有点继承的味道。


定义完XSD之后,我们要告诉Spring遇到这样的标记(命名空间+元素名称)时,如何创建对象。Spring中,完成这个任务的是NamespaceHandler。因此我们需要提供一个NamespaceHandler实现来处理自定义的<dateformat>标签元素。一个简单的实现如下:

[java]  view plain  copy
  1. package extensiblexml.customtag;  
  2.   
  3. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;  
  4.   
  5. public class MyNamespaceHandler extends NamespaceHandlerSupport {  
  6.   
  7.     public void init() {  
  8.         registerBeanDefinitionParser("dateformat",  
  9.                 new SimpleDateFormatBeanDefinitionParser());  
  10.     }  
  11.   
  12. }  

我们在初始化方法中注册了一个Bean定义的解析器,这个解析器就是用来解析定制的配置标签的。其实现如下:

[java]  view plain  copy
  1. package extensiblexml.customtag;  
  2.   
  3. import org.springframework.beans.factory.support.BeanDefinitionBuilder;  
  4. import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;  
  5. import org.springframework.util.StringUtils;  
  6. import org.w3c.dom.Element;  
  7.   
  8. import java.text.SimpleDateFormat;  
  9.   
  10. public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {   
  11.   
  12.     protected Class<SimpleDateFormat> getBeanClass(Element element) {  
  13.         return SimpleDateFormat.class;   
  14.     }  
  15.   
  16.     @SuppressWarnings("deprecation")  
  17.     protected void doParse(Element element, BeanDefinitionBuilder bean) {  
  18.         // this will never be null since the schema explicitly requires that a value be supplied  
  19.         String pattern = element.getAttribute("pattern");  
  20.         bean.addConstructorArg(pattern);  
  21.   
  22.         // this however is an optional property  
  23.         String lenient = element.getAttribute("lenient");  
  24.         if (StringUtils.hasText(lenient)) {  
  25.             bean.addPropertyValue("lenient", Boolean.valueOf(lenient));  
  26.         }  
  27.     }  
  28.   
  29. }  

这个解析器的doParse中,实现了解析的具体逻辑,借助Spring提供的支持类,我们可以很轻松地完成解析。以上三个文件放在同一个目录下,即把XSD文件跟Java代码放在同一目录。编码完毕之后,还需要做一些配置工作。我们必须告诉Spring我们准备使用自定义的标签元素,告诉Spring如何解析元素,否则Spring没那么聪明。这里需要2个配置文件,在与代码根路径同一级别下,床垫一个叫META-INF的文件。并在里面创建名为spring.handlers和spring.schemas,用于告诉Spring自定义标签的文档结构以及解析它的类。两个文件内容分别如下:

spring.handlers:

[html]  view plain  copy
  1. http\://www.mycompany.com/schema/myns=extensiblexml.customtag.MyNamespaceHandler  

等号的左边是XSD定义中的targetNamespace属性,右边是NamespaceHandler的全称限定名。


spring.schemas:

[html]  view plain  copy
  1. http\://www.mycompany.com/schema/myns/myns.xsd=extensiblexml/customtag/myns.xsd  


然后像往常一样配置bean,作为简单的测试,我们定义一个bean:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:myns="http://www.mycompany.com/schema/myns"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  5.         http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd" >  
  6.   
  7.     <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm"  
  8.         lenient="true" />  
  9. </beans>  

在Eclipse中,整个项目结构如下图:



最后我们写个测试类测试一下能否工作:

[java]  view plain  copy
  1. package extensiblexml.customtag;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5.   
  6. import org.springframework.context.ApplicationContext;  
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  8.   
  9. public class Test {  
  10.   
  11.     public static void main(String[] args) {  
  12.         ApplicationContext context = new ClassPathXmlApplicationContext(  
  13.                 "beans.xml");  
  14.         SimpleDateFormat format = (SimpleDateFormat) context  
  15.                 .getBean("defaultDateFormat");  
  16.         System.out.println(format.format(new Date()));  
  17.   
  18.     }  
  19.   
  20. }  

一切正常,输出如下:



更实用的例子

第一个例子主要是为了举例,在实际中用处不大,我们接着来看一个更复杂的自定义标签。我们自定义一个<fileList>标签,当Spring扫描到这个标签的时候,创建一个指定目录下的File类的集合。另外,可以使用<fileFilter>对该目录的文件进行过滤。如下:

[java]  view plain  copy
  1. <core-commons:fileList id="xmlList" directory="src/extensiblexml/example">  
  2.     <core-commons:fileFilter>  
  3.     <bean class="org.apache.commons.io.filefilter.RegexFileFilter">  
  4.         <constructor-arg value=".*.java" />  
  5.     </bean>  
  6.     </core-commons:fileFilter>  
  7. </core-commons:fileList>  
上面的bean定义中,我们从“src/extensible/example”目录中筛选出java源码文件。

使用下面的测试迭代输出文件名:

[java]  view plain  copy
  1. @SuppressWarnings("unchecked")  
  2. List<File> fileList = (List<File>) context.getBean("xmlList");  
  3. for (File file : fileList) {  
  4.     System.out.println(file.getName());  
  5. }  
输出结果如下:


根据第一个例子中的步骤,各部分配置及代码如下:

core-commons-1.0.xsd:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <xsd:schema xmlns="http://www.example.com/schema/core-commons-1.0"  
  3.     targetNamespace="http://www.example.com/schema/core-commons-1.0"  
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.     xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
  6.     xmlns:beans="http://www.springframework.org/schema/beans"  
  7.     elementFormDefault="qualified"  
  8.     attributeFormDefault="unqualified"  
  9.     version="1.0">  
  10.   
  11.     <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"/>  
  12.   
  13.     <xsd:element name="fileList">  
  14.         <xsd:complexType>  
  15.             <xsd:complexContent>  
  16.                 <xsd:extension base="beans:identifiedType">  
  17.                     <xsd:sequence>  
  18.                         <xsd:element ref="fileFilter" minOccurs="0" maxOccurs="1"/>  
  19.                         <xsd:element ref="fileList" minOccurs="0" maxOccurs="unbounded"/>  
  20.                     </xsd:sequence>  
  21.                     <xsd:attribute name="directory" type="xsd:string"/>  
  22.                     <xsd:attribute name="scope" type="xsd:string"/>  
  23.                 </xsd:extension>  
  24.             </xsd:complexContent>  
  25.         </xsd:complexType>  
  26.     </xsd:element>  
  27.   
  28.     <xsd:element name="fileFilter">  
  29.         <xsd:complexType>  
  30.             <xsd:complexContent>  
  31.                 <xsd:extension base="beans:identifiedType">  
  32.                     <xsd:group ref="limitedType"/>  
  33.                     <xsd:attribute name="scope" type="xsd:string"/>  
  34.                 </xsd:extension>  
  35.             </xsd:complexContent>  
  36.         </xsd:complexType>  
  37.     </xsd:element>  
  38.   
  39.     <xsd:group name="limitedType">  
  40.         <xsd:sequence>  
  41.             <xsd:choice minOccurs="1" maxOccurs="unbounded">  
  42.                 <xsd:element ref="beans:bean"/>  
  43.                 <xsd:element ref="beans:ref"/>  
  44.                 <xsd:element ref="beans:idref"/>  
  45.                 <xsd:element ref="beans:value"/>  
  46.                 <xsd:any minOccurs="0"/>  
  47.             </xsd:choice>  
  48.         </xsd:sequence>  
  49.     </xsd:group>  
  50. </xsd:schema>  

CoreNamespaceHandler.java:

[java]  view plain  copy
  1. package extensiblexml.example;  
  2.   
  3. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;  
  4.   
  5. public class CoreNamespaceHandler  
  6.     extends NamespaceHandlerSupport  
  7. {  
  8.   
  9.     @Override  
  10.     public void init() {  
  11.         this.registerBeanDefinitionParser("fileList"new FileListDefinitionParser());  
  12.         this.registerBeanDefinitionParser("fileFilter"new FileFilterDefinitionParser());  
  13.     }  
  14. }  

FileListDefinitionParser.java:

[java]  view plain  copy
  1. public class FileListDefinitionParser  
  2.     extends AbstractSingleBeanDefinitionParser  
  3. {  
  4.   
  5.     /** 
  6.      * The bean that is created for this tag element 
  7.      * 
  8.      * @param element The tag element 
  9.      * @return A FileListFactoryBean 
  10.      */  
  11.     @Override  
  12.     protected Class<?> getBeanClass(Element element) {  
  13.         return FileListFactoryBean.class;  
  14.     }  
  15.   
  16.     /** 
  17.      * Called when the fileList tag is to be parsed 
  18.      * 
  19.      * @param element The tag element 
  20.      * @param ctx The context in which the parsing is occuring 
  21.      * @param builder The bean definitions build to use 
  22.      */  
  23.     @Override  
  24.     protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) {  
  25.         // Set the directory property  
  26.         builder.addPropertyValue("directory", element.getAttribute("directory"));  
  27.   
  28.         // Set the scope  
  29.         builder.setScope(element.getAttribute("scope"));  
  30.   
  31.         // We want any parsing to occur as a child of this tag so we need to make  
  32.         // a new one that has this as it's owner/parent  
  33.         ParserContext nestedCtx = new ParserContext(ctx.getReaderContext(), ctx.getDelegate(), builder.getBeanDefinition());  
  34.   
  35.         // Support for filters  
  36.         Element exclusionElem = DomUtils.getChildElementByTagName(element, "fileFilter");  
  37.         if (exclusionElem != null) {  
  38.             // Just make a new Parser for each one and let the parser do the work  
  39.             FileFilterDefinitionParser ff = new FileFilterDefinitionParser();  
  40.             builder.addPropertyValue("filters", ff.parse(exclusionElem, nestedCtx));  
  41.         }  
  42.   
  43.         // Support for nested fileList  
  44.         List<Element> fileLists = DomUtils.getChildElementsByTagName(element, "fileList");  
  45.         // Any objects that created will be placed in a ManagedList  
  46.         // so Spring does the bulk of the resolution work for us  
  47.         ManagedList<Object> nestedFiles = new ManagedList<Object>();  
  48.         if (fileLists.size() > 0) {  
  49.             // Just make a new Parser for each one and let them do the work  
  50.             FileListDefinitionParser fldp = new FileListDefinitionParser();  
  51.             for (Element fileListElem : fileLists) {  
  52.                 nestedFiles.add(fldp.parse(fileListElem, nestedCtx));  
  53.             }  
  54.         }  
  55.   
  56.         // Support for other tags that return File (value will be converted to file)  
  57.         try {  
  58.             // Go through any other tags we may find.  This does not mean we support  
  59.             // any tag, we support only what parseLimitedList will process  
  60.             NodeList nl = element.getChildNodes();  
  61.             for (int i=0; i<nl.getLength(); i++) {  
  62.                 // Parse each child tag we find in the correct scope but we  
  63.                 // won't support custom tags at this point as it coudl destablize things  
  64.                 DefinitionParserUtil.parseLimitedList(nestedFiles, nl.item(i), ctx,  
  65.                     builder.getBeanDefinition(), element.getAttribute("scope"), false);  
  66.             }  
  67.         }  
  68.         catch (Exception e) {  
  69.             throw new RuntimeException(e);  
  70.         }  
  71.   
  72.         // Set the nestedFiles in the properties so it is set on the FactoryBean  
  73.         builder.addPropertyValue("nestedFiles", nestedFiles);  
  74.   
  75.     }  
  76.   
  77.     public static class FileListFactoryBean  
  78.         implements FactoryBean<Collection<File>>  
  79.     {  
  80.   
  81.         String directory;  
  82.         private Collection<FileFilter> filters;  
  83.         private Collection<File> nestedFiles;  
  84.   
  85.         @Override  
  86.         public Collection<File> getObject() throws Exception {  
  87.             // These can be an array list because the directory will have unique's and the nested is already only unique's  
  88.             Collection<File> files = new ArrayList<File>();  
  89.             Collection<File> results = new ArrayList<File>(0);  
  90.   
  91.             if (directory != null) {  
  92.                 // get all the files in the directory  
  93.                 File dir = new File(directory);  
  94.                 File[] dirFiles = dir.listFiles();  
  95.                 if (dirFiles != null) {  
  96.                     files = Arrays.asList(dirFiles);  
  97.                 }  
  98.             }  
  99.   
  100.             // If there are any files that were created from the nested tags,  
  101.             // add those to the list of files  
  102.             if (nestedFiles != null) {  
  103.                 files.addAll(nestedFiles);  
  104.             }  
  105.   
  106.             // If there are filters we need to go through each filter  
  107.             // and see if the files in the list pass the filters.  
  108.             // If the files does not pass any one of the filters then it  
  109.             // will not be included in the list  
  110.             if (filters != null) {  
  111.                 boolean add;  
  112.                 for (File f : files) {  
  113.                     add = true;  
  114.                     for (FileFilter ff : filters) {  
  115.                         if (!ff.accept(f)) {  
  116.                             add = false;  
  117.                             break;  
  118.                         }  
  119.                     }  
  120.                     if (add) results.add(f);  
  121.                 }  
  122.                 return results;  
  123.             }  
  124.   
  125.             return files;  
  126.         }  
  127.   
  128.         @Override  
  129.         public Class<?> getObjectType() {  
  130.             return Collection.class;  
  131.         }  
  132.   
  133.         @Override  
  134.         public boolean isSingleton() {  
  135.             return false;  
  136.         }  
  137.   
  138.         public void setDirectory(String dir) {  
  139.             this.directory = dir;  
  140.         }  
  141.   
  142.         public void setFilters(Collection<FileFilter> filters) {  
  143.             this.filters = filters;  
  144.         }  
  145.   
  146.         /** 
  147.          * What we actually get from the processing of the nested tags 
  148.          * is a collection of files within a collection so we flatten it and 
  149.          * only keep the uniques 
  150.          */  
  151.         public void setNestedFiles(Collection<Collection<File>> nestedFiles) {  
  152.             this.nestedFiles = new HashSet<File>(); // keep the list unique  
  153.             for (Collection<File> nested : nestedFiles) {  
  154.                 this.nestedFiles.addAll(nested);  
  155.             }  
  156.         }  
  157.   
  158.     }  
  159. }  

FileFilterDefinitionParser.java

[java]  view plain  copy
  1. public class FileFilterDefinitionParser  
  2.     extends AbstractSingleBeanDefinitionParser  
  3. {  
  4.   
  5.     /** 
  6.      * The bean that is created for this tag element 
  7.      * 
  8.      * @param element The tag element 
  9.      * @return A FileFilterFactoryBean 
  10.      */  
  11.     @Override  
  12.     protected Class<?> getBeanClass(Element element) {  
  13.         return FileFilterFactoryBean.class;  
  14.     }  
  15.   
  16.     /** 
  17.      * Called when the fileFilter tag is to be parsed 
  18.      * 
  19.      * @param element The tag element 
  20.      * @param ctx The context in which the parsing is occuring 
  21.      * @param builder The bean definitions build to use 
  22.      */  
  23.     @Override  
  24.     protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) {  
  25.   
  26.         // Set the scope  
  27.         builder.setScope(element.getAttribute("scope"));  
  28.   
  29.         try {  
  30.             // All of the filters will eventually end up in this list  
  31.             // We use a 'ManagedList' and not a regular list because anything  
  32.             // placed in a ManagedList object will support all of Springs  
  33.             // functionalities and scopes for us, we dont' have to code anything  
  34.             // in terms of reference lookups, EL, etc  
  35.             ManagedList<Object> filters = new ManagedList<Object>();  
  36.   
  37.             // For each child node of the fileFilter tag, parse it and place it  
  38.             // in the filtes list  
  39.             NodeList nl = element.getChildNodes();  
  40.             for (int i=0; i<nl.getLength(); i++) {  
  41.                 DefinitionParserUtil.parseLimitedList(filters, nl.item(i), ctx, builder.getBeanDefinition(), element.getAttribute("scope"));  
  42.             }  
  43.   
  44.             // Add the filtes to the list of properties (this is applied  
  45.             // to the factory beans setFilters below)  
  46.             builder.addPropertyValue("filters", filters);  
  47.         }  
  48.         catch (Exception e) {  
  49.             throw new RuntimeException(e);  
  50.         }  
  51.     }  
  52.   
  53.     public static class FileFilterFactoryBean  
  54.         implements FactoryBean<Collection<FileFilter>>  
  55.     {  
  56.   
  57.         private final List<FileFilter> filters = new ArrayList<FileFilter>();  
  58.   
  59.         @Override  
  60.         public Collection<FileFilter> getObject() throws Exception {  
  61.             return filters;  
  62.         }  
  63.   
  64.         @Override  
  65.         public Class<?> getObjectType() {  
  66.             return Collection.class;  
  67.         }  
  68.   
  69.         @Override  
  70.         public boolean isSingleton() {  
  71.             return false;  
  72.         }  
  73.   
  74.         /** 
  75.          * Go through the list of filters and convert the String ones 
  76.          * (the ones that were set with <value> and make them NameFileFilters 
  77.          */  
  78.         public void setFilters(Collection<Object> filterList) {  
  79.             for (Object o : filterList) {  
  80.                 if (o instanceof String) {  
  81.                     filters.add(new NameFileFilter(o.toString()));  
  82.                 }  
  83.                 else if (o instanceof FileFilter) {  
  84.                     filters.add((FileFilter)o);  
  85.                 }  
  86.             }  
  87.         }  
  88.   
  89.     }  
  90. }  

DefinitionParserUtil.java:

[java]  view plain  copy
  1. package extensiblexml.example;  
  2.   
  3. import org.springframework.beans.factory.config.BeanDefinition;  
  4. import org.springframework.beans.factory.config.BeanDefinitionHolder;  
  5. import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;  
  6. import org.springframework.beans.factory.support.DefaultListableBeanFactory;  
  7. import org.springframework.beans.factory.support.ManagedList;  
  8. import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;  
  9. import org.springframework.beans.factory.xml.ParserContext;  
  10. import org.springframework.expression.Expression;  
  11. import org.springframework.expression.ExpressionParser;  
  12. import org.springframework.expression.spel.standard.SpelExpressionParser;  
  13. import org.w3c.dom.Element;  
  14. import org.w3c.dom.Node;  
  15.   
  16. public class DefinitionParserUtil {  
  17.   
  18.     /** 
  19.      * Parses the children of the passed in ParentNode for the following tags: 
  20.      * <br/> 
  21.      * value 
  22.      * ref 
  23.      * idref 
  24.      * bean 
  25.      * property 
  26.      * *custom* 
  27.      * <p/> 
  28.      * 
  29.      * The value tag works with Spring EL even in a Spring Batch scope="step" 
  30.      * 
  31.      * @param objects The list of resultings objects from the parsing (passed in for recursion purposes) 
  32.      * @param parentNode The node who's children should be parsed 
  33.      * @param ctx The ParserContext to use 
  34.      * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean 
  35.      *      (i.e. the Bean that is the parentNode) 
  36.      * @param scope The scope to execute in.  Checked if 'step' to provide Spring EL 
  37.      *      support in a Spring Batch env 
  38.      * @throws Exception 
  39.      */  
  40.     public static void parseLimitedList(ManagedList<Object> objects, Node node,  
  41.         ParserContext ctx, BeanDefinition parentBean, String scope)  
  42.         throws Exception  
  43.     {  
  44.         parseLimitedList(objects, node, ctx, parentBean, scope, true);  
  45.     }  
  46.   
  47.     /** 
  48.      * Parses the children of the passed in ParentNode for the following tags: 
  49.      * <br/> 
  50.      * value 
  51.      * ref 
  52.      * idref 
  53.      * bean 
  54.      * property 
  55.      * *custom* 
  56.      * <p/> 
  57.      * 
  58.      * The value tag works with Spring EL even in a Spring Batch scope="step" 
  59.      * 
  60.      * @param objects The list of resultings objects from the parsing (passed in for recursion purposes) 
  61.      * @param parentNode The node who's children should be parsed 
  62.      * @param ctx The ParserContext to use 
  63.      * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean 
  64.      *      (i.e. the Bean that is the parentNode) 
  65.      * @param scope The scope to execute in.  Checked if 'step' to provide Spring EL 
  66.      *      support in a Spring Batch env 
  67.      * @param supportCustomTags Should we support custom tags within our tags? 
  68.      * @throws Exception 
  69.      */  
  70.     @SuppressWarnings("deprecation")  
  71.     public static void parseLimitedList(ManagedList<Object> objects, Node node,  
  72.         ParserContext ctx, BeanDefinition parentBean, String scope, boolean supportCustomTags)  
  73.         throws Exception  
  74.     {  
  75.         // Only worry about element nodes  
  76.         if (node.getNodeType() == Node.ELEMENT_NODE) {  
  77.             Element elem = (Element)node;  
  78.             String tagName = node.getLocalName();  
  79.   
  80.             if (tagName.equals("value")) {  
  81.                 String val = node.getTextContent();  
  82.                 // to get around an issue with Spring Batch not parsing Spring EL  
  83.                 // we will do it for them  
  84.                 if (scope.equals("step")  
  85.                     && (val.startsWith("#{") && val.endsWith("}"))  
  86.                     && (!val.startsWith("#{jobParameters"))  
  87.                     )  
  88.                 {  
  89.                     // Set up a new EL parser  
  90.                     ExpressionParser parser = new SpelExpressionParser();  
  91.                     // Parse the value  
  92.                     Expression exp = parser.parseExpression(val.substring(2, val.length()-1));  
  93.                     // Place the results in the list of created objects  
  94.                     objects.add(exp.getValue());  
  95.                 }  
  96.                 else {  
  97.                     // Otherwise, just treat it as a normal value tag  
  98.                     objects.add(val);  
  99.                 }  
  100.             }  
  101.             // Either of these is a just a lookup of an existing bean  
  102.             else if (tagName.equals("ref") || tagName.equals("idref")) {  
  103.                 objects.add(ctx.getRegistry().getBeanDefinition(node.getTextContent()));  
  104.             }  
  105.             // We need to create the bean  
  106.             else if (tagName.equals("bean")) {  
  107.                 // There is no quick little util I could find to create a bean  
  108.                 // on the fly programmatically in Spring and still support all  
  109.                 // Spring functionality so basically I mimic what Spring actually  
  110.                 // does but on a smaller scale.  Everything Spring allows is  
  111.                 // still supported  
  112.   
  113.                 // Create a factory to make the bean  
  114.                 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();  
  115.                 // Set up a parser for the bean  
  116.                 BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext());  
  117.                 // Parse the bean get its information, now in a DefintionHolder  
  118.                 BeanDefinitionHolder bh = pd.parseBeanDefinitionElement(elem, parentBean);  
  119.                 // Register the bean will all the other beans Spring is aware of  
  120.                 BeanDefinitionReaderUtils.registerBeanDefinition(bh, beanFactory);  
  121.                 // Get the bean from the factory.  This will allows Spring  
  122.                 // to do all its work (EL processing, scope, etc) and give us  
  123.                 // the actual bean itself  
  124.                 Object bean = beanFactory.getBean(bh.getBeanName());  
  125.                 objects.add(bean);  
  126.             }  
  127.             /* 
  128.              * This is handled a bit differently in that it actually sets the property 
  129.              * on the parent bean for us based on the property 
  130.              */  
  131.             else if (tagName.equals("property")) {  
  132.                 BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext());  
  133.                 // This method actually set eh property on the parentBean for us so  
  134.                 // we don't have to add anything to the objects object  
  135.                 pd.parsePropertyElement(elem, parentBean);  
  136.             }  
  137.             else if (supportCustomTags) {  
  138.                 // handle custom tag  
  139.                 BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext());  
  140.                 BeanDefinition bd = pd.parseCustomElement(elem, parentBean);  
  141.                 objects.add(bd);  
  142.             }  
  143.         }  
  144.     }  
  145. }  

spring.schemas

[html]  view plain  copy
  1. http\://www.mycompany.com/schema/myns/myns.xsd=extensiblexml/customtag/myns.xsd  
  2. http\://www.example.com/schema/core-commons-1.0.xsd=extensiblexml/example/core-commons-1.0.xsd  

spring.handlers

[html]  view plain  copy
  1. http\://www.mycompany.com/schema/myns=extensiblexml.customtag.MyNamespaceHandler  
  2. http\://www.example.com/schema/core-commons-1.0=extensiblexml.example.CoreNamespaceHandler  


小结:

要自定义Spring的配置标签,需要一下几个步骤:

**使用XSD定义XML配置中标签元素的结构(myns.XSD)

**提供该XSD命名空间的处理类,它可以处理多个标签定义(MyNamespaceHandler.java)

**为每个标签元素的定义提供解析类。(SimpleDateFormatBeanDefinitionParser.java)

**两个特殊文件通知Spring使用自定义标签元素(spring.handlers 和spring.schemas)


参考资料:

Spring官方的Extensible XML Authoring文档:

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/extensible-xml.html

一个不错的教程:

http://sloanseaman.com/wordpress/2012/03/26/spring-custom-tags-extensible-xml-part-1/

http://sloanseaman.com/wordpress/2012/04/08/spring-custom-tags-extensible-xml-part-2/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值