概念
在spring的xml配置中,我们除了可以使用默认的beans标签外,还可以自己定义标签。
其实spring自己内部的很多模块也是使用了自定义标签来实现功能的。
还有一些别的比较知名的案例,比如:dubbo
用法
首先,随便定义一个想通过标签来配置的bean:
public class CustomBean {
private String name;
//get set
}
自定义一个解析器:
public class CustomBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return CustomBean.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
if(StringUtils.isNotBlank(name)){
builder.addPropertyValue("name",name);
}
}
}
自定义一个handler
public class CustomHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("custom",new CustomBeanDefinitionParser());
}
}
定义一个xsd验证文件
<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.my.org/schema/custom"
xml:tns="http://www.my.org/schema/custom"
elementFormDefault="qualified">
<element name="custom">
<complexType>
<attribute name="id" type="string"/>
<attribute name="name" type="string"/>
</complexType>
</element>
</schema>
spring.handlers文件
http\://www.my.org/schema/custom=com.pk.study.spring.customer.CustomHandler
spring.schemas文件
http\://www.my.org/schema/custom.xsd=META-INF/custom.xsd
spring的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:custom="http://www.my.org/schema/custom"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.my.org/schema/custom http://www.my.org/schema/custom.xsd">
<custom:custom id="custom" name="custom name"/>
</beans>
测试类:
public class CustomTest {
@Test
public void test(){
//创建一个实现了BeanDefinitionRegistry的BeanFactory实现
//DefaultListableBeanFactory也是绝大多数场景下,BeanFactory的具体实现
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//XmlBeanDefinitionReader创建,从名字可以看出来 这个类是用来从xml文件中读取配置的
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//具体解析xml文件中的配置,并注册到BeanDefinitionRegistry中去
reader.loadBeanDefinitions(new ClassPathResource("custom.xml"));
CustomBean userInfo = beanFactory.getBean(CustomBean.class);
System.out.println(userInfo.getName());
}
}
会输出custom name
实现
自定义标签的解析入口在BeanDefinitionParserDelegate.parseCustomElement()方法:
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//获取标签的namespace
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//获取namespace对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//调用自定义的handler去解析标签
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
我们自定义的handler并没有重写parse方法,而是重写了doParse方法,这也是借助了spring的模板方法模式,由父类AbstractBeanDefinitionParser定义了方法执行的框架,而我们自己的子类仅仅实现了parse方法中的一小部分。进入AbstractBeanDefinitionParser.parse()方法:
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//主要逻辑
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
//必须要有id属性
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;
}
进入AbstractBeanDefinitionParser.parseInternal()
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
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) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
//调用我们自定义的handler的方法
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
结语
自定义标签的解析。可以自己定义插件集成到spring中。
(水平有限,最近在看spring源码,分享学习过程,希望对各位有点微小的帮助。如有错误,请指正~)