最近看一个项目,在spirng配置文件中,引用了非正常标签,上面配置几个参数,启动后就可以在别的bean中注入了
于是就引发了我的好奇心,决定一探究竟
配置文件写的很精简,没有过多的描述,详细的可以参照spirng的配置文件,复制出来改一改
核心点是这个 targetNamespace 它的值表示了这个xsd的地址
这个文件的位置默认放到META-INF下,也可以自己随意放置,在spring.schema中指定清楚就可以了
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://www.example.org/schema/user">
<element name="user">
<complexType>
<attribute name="id" type="string" use="required"/>
<attribute name="name" type="string"/>
<attribute name="description" type="string"/>
complexType>
element>
schema>
这里用了lombok插件来简化代码量,这个类就是要用自定义标签创建的类
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class User implements Serializable {
private String id;
private String name;
private String description;
}
spring启动后,遇到不认识的标签,默认回去META-INF下去找spring.handlers和spring.schemas这两个文件
spring.handlers
application.xml中会表述出标签的地址,然后用到到handler文件中找到处理的类
http\://www.example.org/schema/user=com.bat.handler.UserNamespaceHandler
spring.schemas
application.xml中会表述xsd文件的地址,拿地址到schemas中找到xsd实际的位置
http\://www.example.org/schema/user.xsd=META-INF/user-1.0.xsd
通过继承 NamespaceHandlerSupport ,在初始化init()方法中,可以指定对标签使用何种解析器,对于自定义很多标签来说,本类只需要创建一个,在init方法中添加多个parse即可
import com.bat.parser.UserBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());// 这里可以创建多个
}
}
通过继承 AbstractSingleBeanDefinitionParser / AbstractSimpleBeanDefinitionParser / BeanDefinitionParser 等(都可以,只是具体实现的方法不一样)
import com.bat.domain.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String id = element.getAttribute("id");
String name = element.getAttribute("name");
String desc = element.getAttribute("description");
if(StringUtils.isEmpty(id)){
throw new RuntimeException("id is required");
}
builder.addPropertyValue("id",id);
builder.addPropertyValue("name",name);
builder.addPropertyValue("description",desc);
}
}
为什么在标签前加上tt(test)呢?
xmlns表示每一个标签的命名空间,每个标签都有自己的属性,默认情况下标签会去xmlns中查找。
多个xmlns是通过加上 冒号名称 [ :xxx ] 来对多个标签进行区分的,tt:user 会到上面找到 xmlns:tt 的地址
然后到xsi:schemaLocation中找到处理自己的handler和xsd的。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tt="http://www.example.org/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.example.org/schema/user http://www.example.org/schema/user.xsd">
<tt:user id="user" name="caoke" description="consumer"/>
beans>
容器启动,成功的得到了bean
import com.bat.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationStart {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
context.getBean(User.class);
User user = (User) context.getBean("user");
System.out.println(user.getId()+":"+user.getName()+":"+user.getDescription());
}
}