0

0

Java怎么自定义Spring配置标签

WBOY

WBOY

发布时间:2023-04-24 17:16:17

|

1590人浏览过

|

来源于亿速云

转载

引言:

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

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


     handler1
     handler2

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

一个HelloWorld例子:

立即学习Java免费学习笔记(深入)”;

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

我们定义如下一个简单的XSD:



 
    
 
    
        
            
                
                    
                    
                
            
        
    

在这个XSD定义中,有一个标签叫dateformat,这就是我们用来替换bean标签的自定义标签。注意到我们导入了Spring本身的beans命名空间,并且在beans:identifiedType基础之上定义dateformat标签。也就是我们这个标签可以像标签一样拥有id属性。同时我们增加了两个属性lenient和pattern。这有点继承的味道。

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

一个简单的实现如下:

package extensiblexml.customtag;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class MyNamespaceHandler extends NamespaceHandlerSupport {
	public void init() {
		registerBeanDefinitionParser("dateformat",
				new SimpleDateFormatBeanDefinitionParser());
	}
 
}

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

其实现如下:

package extensiblexml.customtag;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import java.text.SimpleDateFormat;
public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 
    protected Class getBeanClass(Element element) {
        return SimpleDateFormat.class; 
    }
    @SuppressWarnings("deprecation")
	protected void doParse(Element element, BeanDefinitionBuilder bean) {
        // this will never be null since the schema explicitly requires that a value be supplied
        String pattern = element.getAttribute("pattern");
        bean.addConstructorArg(pattern);
 
        // this however is an optional property
        String lenient = element.getAttribute("lenient");
        if (StringUtils.hasText(lenient)) {
            bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
        }
    }
 
}

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

spring.handlers:

http\://www.mycompany.com/schema/myns=extensiblexml.customtag.MyNamespaceHandler

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

spring.schemas:

http\://www.mycompany.com/schema/myns/myns.xsd=extensiblexml/customtag/myns.xsd

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



 
	

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

Java怎么自定义Spring配置标签

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

package extensiblexml.customtag;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"beans.xml");
		SimpleDateFormat format = (SimpleDateFormat) context
				.getBean("defaultDateFormat");
		System.out.println(format.format(new Date()));
 
	}
 
}

一切正常,输出如下:

Java怎么自定义Spring配置标签

更实用的例子

YXPHP企业网站管理系统4.0
YXPHP企业网站管理系统4.0

支持静态模板,支持动态模板标签,支持图片.SWF.FLV系列广告标签.支持百万级海量数据,绑定内置URL伪装策略(URL后缀名随你怎么写),绑定内置系统升级策略(暂不开放升级),绑定内置模板付费升级策略(暂不开放更新)。支持标签容错处理,绑定内置攻击防御策略,绑定内置服务器优化策略(系统内存释放的干干净净)。支持离线运行,支持次目录,兼容U主机。支持会员功能,支持文章版块权限阅读,支持会员自主注册

下载

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

如下:


    
	
	    
	
    

上面的bean定义中,我们从“src/extensible/example”目录中筛选出java源码文件。

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

@SuppressWarnings("unchecked")
List fileList = (List) context.getBean("xmlList");
for (File file : fileList) {
	System.out.println(file.getName());
}

输出结果如下:

Java怎么自定义Spring配置标签

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

core-commons-1.0.xsd:



 
	
 
    
        
            
                
                    
                        
                        
                    
                    
                    
                
            
        
    
 
    
        
            
                
                    
                    
                
            
        
    
 
    
        
            
                
                
                
                
                
            
        
    

CoreNamespaceHandler.java:

package extensiblexml.example;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class CoreNamespaceHandler
    extends NamespaceHandlerSupport
{
    @Override
    public void init() {
        this.registerBeanDefinitionParser("fileList", new FileListDefinitionParser());
        this.registerBeanDefinitionParser("fileFilter", new FileFilterDefinitionParser());
    }
}

FileListDefinitionParser.java:

public class FileListDefinitionParser
	extends AbstractSingleBeanDefinitionParser
{
	/**
	 * The bean that is created for this tag element
	 *
	 * @param element The tag element
	 * @return A FileListFactoryBean
	 */
	@Override
	protected Class getBeanClass(Element element) {
		return FileListFactoryBean.class;
	}
 
	/**
	 * Called when the fileList tag is to be parsed
	 *
	 * @param element The tag element
	 * @param ctx The context in which the parsing is occuring
	 * @param builder The bean definitions build to use
	 */
	@Override
	protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) {
		// Set the directory property
		builder.addPropertyValue("directory", element.getAttribute("directory"));
 
		// Set the scope
		builder.setScope(element.getAttribute("scope"));
 
		// We want any parsing to occur as a child of this tag so we need to make
		// a new one that has this as it's owner/parent
		ParserContext nestedCtx = new ParserContext(ctx.getReaderContext(), ctx.getDelegate(), builder.getBeanDefinition());
 
		// Support for filters
		Element exclusionElem = DomUtils.getChildElementByTagName(element, "fileFilter");
		if (exclusionElem != null) {
			// Just make a new Parser for each one and let the parser do the work
			FileFilterDefinitionParser ff = new FileFilterDefinitionParser();
			builder.addPropertyValue("filters", ff.parse(exclusionElem, nestedCtx));
		}
 
		// Support for nested fileList
		List fileLists = DomUtils.getChildElementsByTagName(element, "fileList");
		// Any objects that created will be placed in a ManagedList
		// so Spring does the bulk of the resolution work for us
		ManagedList nestedFiles = new ManagedList();
		if (fileLists.size() > 0) {
			// Just make a new Parser for each one and let them do the work
			FileListDefinitionParser fldp = new FileListDefinitionParser();
			for (Element fileListElem : fileLists) {
				nestedFiles.add(fldp.parse(fileListElem, nestedCtx));
			}
		}
 
		// Support for other tags that return File (value will be converted to file)
		try {
			// Go through any other tags we may find.  This does not mean we support
			// any tag, we support only what parseLimitedList will process
			NodeList nl = element.getChildNodes();
			for (int i=0; i>
	{
 
		String directory;
		private Collection filters;
		private Collection nestedFiles;
 
		@Override
		public Collection getObject() throws Exception {
			// These can be an array list because the directory will have unique's and the nested is already only unique's
			Collection files = new ArrayList();
			Collection results = new ArrayList(0);
 
			if (directory != null) {
				// get all the files in the directory
				File dir = new File(directory);
				File[] dirFiles = dir.listFiles();
				if (dirFiles != null) {
					files = Arrays.asList(dirFiles);
				}
			}
 
			// If there are any files that were created from the nested tags,
			// add those to the list of files
			if (nestedFiles != null) {
				files.addAll(nestedFiles);
			}
 
			// If there are filters we need to go through each filter
			// and see if the files in the list pass the filters.
			// If the files does not pass any one of the filters then it
			// will not be included in the list
			if (filters != null) {
				boolean add;
				for (File f : files) {
					add = true;
					for (FileFilter ff : filters) {
						if (!ff.accept(f)) {
							add = false;
							break;
						}
					}
					if (add) results.add(f);
				}
				return results;
			}
 
			return files;
		}
 
		@Override
		public Class getObjectType() {
			return Collection.class;
		}
 
		@Override
		public boolean isSingleton() {
			return false;
		}
 
		public void setDirectory(String dir) {
			this.directory = dir;
		}
		public void setFilters(Collection filters) {
			this.filters = filters;
		}
 
		/**
		 * What we actually get from the processing of the nested tags
		 * is a collection of files within a collection so we flatten it and
		 * only keep the uniques
		 */
		public void setNestedFiles(Collection> nestedFiles) {
			this.nestedFiles = new HashSet(); // keep the list unique
			for (Collection nested : nestedFiles) {
				this.nestedFiles.addAll(nested);
			}
		}
 
	}
}

FileFilterDefinitionParser.java

public class FileFilterDefinitionParser
	extends AbstractSingleBeanDefinitionParser
{
 
	/**
	 * The bean that is created for this tag element
	 *
	 * @param element The tag element
	 * @return A FileFilterFactoryBean
	 */
	@Override
	protected Class getBeanClass(Element element) {
		return FileFilterFactoryBean.class;
	}
 
	/**
	 * Called when the fileFilter tag is to be parsed
	 *
	 * @param element The tag element
	 * @param ctx The context in which the parsing is occuring
	 * @param builder The bean definitions build to use
	 */
	@Override
	protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) {
 
		// Set the scope
		builder.setScope(element.getAttribute("scope"));
 
		try {
			// All of the filters will eventually end up in this list
			// We use a 'ManagedList' and not a regular list because anything
			// placed in a ManagedList object will support all of Springs
			// functionalities and scopes for us, we dont' have to code anything
			// in terms of reference lookups, EL, etc
			ManagedList filters = new ManagedList();
 
			// For each child node of the fileFilter tag, parse it and place it
			// in the filtes list
			NodeList nl = element.getChildNodes();
			for (int i=0; i>
	{
 
		private final List filters = new ArrayList();
 
		@Override
		public Collection getObject() throws Exception {
			return filters;
		}
 
		@Override
		public Class getObjectType() {
			return Collection.class;
		}
 
		@Override
		public boolean isSingleton() {
			return false;
		}
 
		/**
		 * Go through the list of filters and convert the String ones
		 * (the ones that were set with  and make them NameFileFilters
		 */
		public void setFilters(Collection filterList) {
			for (Object o : filterList) {
				if (o instanceof String) {
					filters.add(new NameFileFilter(o.toString()));
				}
				else if (o instanceof FileFilter) {
					filters.add((FileFilter)o);
				}
			}
		}
 
	}
}

DefinitionParserUtil.java:

package extensiblexml.example;
 
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
 
public class DefinitionParserUtil {
 
	/**
	 * Parses the children of the passed in ParentNode for the following tags:
	 * 
* value * ref * idref * bean * property * *custom* *

* * The value tag works with Spring EL even in a Spring Batch scope="step" * * @param objects The list of resultings objects from the parsing (passed in for recursion purposes) * @param parentNode The node who's children should be parsed * @param ctx The ParserContext to use * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean * (i.e. the Bean that is the parentNode) * @param scope The scope to execute in. Checked if 'step' to provide Spring EL * support in a Spring Batch env * @throws Exception */ public static void parseLimitedList(ManagedList objects, Node node, ParserContext ctx, BeanDefinition parentBean, String scope) throws Exception { parseLimitedList(objects, node, ctx, parentBean, scope, true); } /** * Parses the children of the passed in ParentNode for the following tags: *
* value * ref * idref * bean * property * *custom* *

* * The value tag works with Spring EL even in a Spring Batch scope="step" * * @param objects The list of resultings objects from the parsing (passed in for recursion purposes) * @param parentNode The node who's children should be parsed * @param ctx The ParserContext to use * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean * (i.e. the Bean that is the parentNode) * @param scope The scope to execute in. Checked if 'step' to provide Spring EL * support in a Spring Batch env * @param supportCustomTags Should we support custom tags within our tags? * @throws Exception */ @SuppressWarnings("deprecation") public static void parseLimitedList(ManagedList objects, Node node, ParserContext ctx, BeanDefinition parentBean, String scope, boolean supportCustomTags) throws Exception { // Only worry about element nodes if (node.getNodeType() == Node.ELEMENT_NODE) { Element elem = (Element)node; String tagName = node.getLocalName(); if (tagName.equals("value")) { String val = node.getTextContent(); // to get around an issue with Spring Batch not parsing Spring EL // we will do it for them if (scope.equals("step") && (val.startsWith("#{") && val.endsWith("}")) && (!val.startsWith("#{jobParameters")) ) { // Set up a new EL parser ExpressionParser parser = new SpelExpressionParser(); // Parse the value Expression exp = parser.parseExpression(val.substring(2, val.length()-1)); // Place the results in the list of created objects objects.add(exp.getValue()); } else { // Otherwise, just treat it as a normal value tag objects.add(val); } } // Either of these is a just a lookup of an existing bean else if (tagName.equals("ref") || tagName.equals("idref")) { objects.add(ctx.getRegistry().getBeanDefinition(node.getTextContent())); } // We need to create the bean else if (tagName.equals("bean")) { // There is no quick little util I could find to create a bean // on the fly programmatically in Spring and still support all // Spring functionality so basically I mimic what Spring actually // does but on a smaller scale. Everything Spring allows is // still supported // Create a factory to make the bean DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // Set up a parser for the bean BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext()); // Parse the bean get its information, now in a DefintionHolder BeanDefinitionHolder bh = pd.parseBeanDefinitionElement(elem, parentBean); // Register the bean will all the other beans Spring is aware of BeanDefinitionReaderUtils.registerBeanDefinition(bh, beanFactory); // Get the bean from the factory. This will allows Spring // to do all its work (EL processing, scope, etc) and give us // the actual bean itself Object bean = beanFactory.getBean(bh.getBeanName()); objects.add(bean); } /* * This is handled a bit differently in that it actually sets the property * on the parent bean for us based on the property */ else if (tagName.equals("property")) { BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext()); // This method actually set eh property on the parentBean for us so // we don't have to add anything to the objects object pd.parsePropertyElement(elem, parentBean); } else if (supportCustomTags) { // handle custom tag BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext()); BeanDefinition bd = pd.parseCustomElement(elem, parentBean); objects.add(bd); } } } }

spring.schemas

http\://www.mycompany.com/schema/myns/myns.xsd=extensiblexml/customtag/myns.xsd
http\://www.example.com/schema/core-commons-1.0.xsd=extensiblexml/example/core-commons-1.0.xsd

spring.handlers

http\://www.mycompany.com/schema/myns=extensiblexml.customtag.MyNamespaceHandler
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)

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

844

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

742

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

740

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

400

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

431

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

C++ 高级模板编程与元编程
C++ 高级模板编程与元编程

本专题深入讲解 C++ 中的高级模板编程与元编程技术,涵盖模板特化、SFINAE、模板递归、类型萃取、编译时常量与计算、C++17 的折叠表达式与变长模板参数等。通过多个实际示例,帮助开发者掌握 如何利用 C++ 模板机制编写高效、可扩展的通用代码,并提升代码的灵活性与性能。

6

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Spring中文手册
Spring中文手册

共0课时 | 0人学习

马士兵spring视频教程
马士兵spring视频教程

共25课时 | 9.1万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 2.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号