百木园-与人分享,
就是让自己快乐。

Spring 源码(4)在Spring配置文件中自定义标签如何实现?

Spring 配置文件自定义标签的前置条件

在上一篇文章https://www.cnblogs.com/redwinter/p/16165274.html Spring BeanFactory的创建过程中了解了BeanDefinition的加载和BeanFactory的创建,并且提到了Spring留了一个扩展点就是用户可以自定义标签进行解析BeanDefinition

基于Spring源码在处理定制的标签时是通过定制的命名空间处理器和xsd文件进行解析的,在springclasspath下的META-INF/spring.schemasMETA-INF/spring.handlers,并且需要将标签的解析器注册到BeanDefinition的解析器中,这样说起来比较抽象,接下来我们自己定义一个标签就明了了。

定义标签属性类

创建一个需要解析的标签的属性,比如在Spring配置文件中经常看到的<context:component-scan base-package=\"com.redwinter.test\"/> ,这里的component-scan就是标签属性。

/**
 * @author <a href=\"https://www.cnblogs.com/redwinter/\">redwinter</a>
 * @since 1.0
 **/
public class Redwinter {

	private String username;
	private String email;
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

定义一个Redwinter类,里面三个属性,当然你可以自己定义你需要的属性,我这里就随便写啦。

定义标签属性解析器类

定义好标签的属性之后就需要定义一个解析器对这些属性进行解析,定义解析器需要继承AbstractSingleBeanDefinitionParser,这个类是实现了BeanDefinitionParser的类,他下面有很多实现类,一般来说我们的Bean都是单例的,那就继承AbstractSingleBeanDefinitionParser即可。

/**
 * @author <a href=\"https://www.cnblogs.com/redwinter/\">redwinter</a>
 * @since 1.0
 **/
public class RedwinterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

	@Override
	protected Class<?> getBeanClass(Element element) {
		return Redwinter.class;
	}

	@Override
	protected void doParse(Element element, BeanDefinitionBuilder builder) {
		/**
		 * 自定义解析xml的自定义字段,并做相应的其他处理
		 */
		String username = element.getAttribute(\"username\");
		String email = element.getAttribute(\"email\");
		String password = element.getAttribute(\"password\");
		if (StringUtils.hasText(username)){
			builder.addPropertyValue(\"username\",username);
		}
		if (StringUtils.hasText(email)){
			builder.addPropertyValue(\"email\",email);
		}
		if (StringUtils.hasText(password)){
			builder.addPropertyValue(\"password\",password);
		}
	}
}

这个解析器主要是重写了父类的两个方法,一个是getBeanClass用于返回对应的标签属性类,一个是解析属性doParser,这里我只是从element中获取出来然后进行了下判断在加入到属性值中,当然这里你可以自定义自己的逻辑处理。

定义命名空间处理器类

定义命名空间处理器需要继承NamespaceHandlerSupport,然后重写他的init方法,将解析器注册进去,这个解析器就是上面定义的用来解析标签属性的解析器。

/**
 * @author <a href=\"https://www.cnblogs.com/redwinter/\">redwinter</a>
 * @since 1.0
 **/
public class RedwinterNameSpaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		// 这里的属性必须和xsd中指定的属性一致,否则报错
		//org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [dl]
		registerBeanDefinitionParser(\"dl\",new RedwinterBeanDefinitionParser());
	}
}

这里需要注意的是,进行注册时需要指定一个elementName,这个值必须和xml中定义的名称一致,否者的话就会报如下错:

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [dl]

我这里定义的元素名称叫dl

定义xsd文件

xsd文件就是spring进行xml解析时解析的标签,当然你可以定义dtd文件,不过现在一般都用xsd文件,我这里命名为redwinter.xsd,完整文件如下:

<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>

<xsd:schema xmlns=\"http://www.redwinter.com/schema/redwinter\"
			xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"
			targetNamespace=\"http://www.redwinter.com/schema/redwinter\"
			elementFormDefault=\"qualified\"
			attributeFormDefault=\"unqualified\">
	<xsd:element name=\"dl\">
		<xsd:complexType>
			<xsd:attribute name=\"id\" type=\"xsd:string\"/>
			<xsd:attribute name=\"username\" type=\"xsd:string\" use=\"required\"/>
			<xsd:attribute name=\"email\" type=\"xsd:string\" use=\"required\"/>
			<xsd:attribute name=\"password\" type=\"xsd:string\" use=\"required\"/>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>

这里有几个点需要注意: schema标签下有个targetNamespace,这里指定了命名空间叫http://www.redwinter.com/schema/redwinter ,那么在进行spring配置文件的时候引入的namespace就是这个,然后有个name=\"dl\",这里的这个dl就是处理器中定义的元素名称,而且必须一致,不然解析不到,下面定义的属性就是标签属性类中定义的刷新,这个id是表示唯一的Bean名称。

编写spring.schemas和spring.handlers文件

这里直接列出完整文件内容:

  • spring.schemas文件
http\\://www.redwinter.com/schema/redwinter.xsd=META-INF/redwinter.xsd

这里需要注意的是,这里配置的key也是需要在spring配置文件中引入的,value就是上一步定义的xsd文件所在路径

  • spring.handlers文件
http\\://www.redwinter.com/schema/redwinter=com.redwinter.test.RedwinterNameSpaceHandler

这里配置的key就是上一步定义的xsd文件中定义的targetNamespacevalue就是定义的命名空间处理器。

到这里自定义标签和解析就完成了,最后就需要在spring配置文件中引入并配置。

验证自定义属性标签

我这里定义个角spring-test.xml的文件进行配置

<?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:mvc=\"http://www.springframework.org/schema/mvc\"
	   xmlns:context=\"http://www.springframework.org/schema/context\"
	   xmlns:redwinter=\"http://www.redwinter.com/schema/redwinter\"
	   xsi:schemaLocation=\"http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
		http://www.redwinter.com/schema/redwinter  http://www.redwinter.com/schema/redwinter.xsd
		\">
	<!--自定义标签-->
	<redwinter:dl id =\"redwinter\" email=\"abc@qq.com\" password=\"123456\" username=\"冬玲记忆\"/>
	<redwinter:dl id =\"redwinter123456\"  email=\"123456-abc@qq.com\" password=\"123456\" username=\"冬玲记忆\"/>

</beans>

验证是否配置正确:

public class BeanCreate {

	@Test
	public void classPathXml() {
//		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(\"classpath:spring-test.xml\");
		ClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext(\"classpath:spring-test.xml\");
      
		Redwinter redwinter = (Redwinter) context.getBean(\"redwinter\");
		System.out.println(redwinter.getEmail());

		Redwinter redwinter123456 = (Redwinter) context.getBean(\"redwinter123456\");
		System.out.println(redwinter123456.getEmail());
	}
}

输出:

abc@qq.com
123456-abc@qq.com

那说明自定义标签生效了,并且成功解析出来。

接下来就是继续介绍Spring 容器的实现AbstractApplicationContex#refresh的第三个方法,这个方法其实就是BeanFactory使用的前戏准备,而第一个方法是BeanFactory刷新的前戏准备。


来源:https://www.cnblogs.com/redwinter/p/16165878.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » Spring 源码(4)在Spring配置文件中自定义标签如何实现?

相关推荐

  • 暂无文章