自定义配置文件到spring 中,有时候想做一些数据结构的配置化信息,根据业务做一个扩展。
首先:
在项目的META-INF目录下新建两个文件spring.handlers,和spring.shcemas
Spring.handlers在类org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver中已经写死了要取mapping的handlerMappingsLocation的路径
public static finalString DEFAULT_HANDLER_MAPPINGS_LOCATION ="META-INF/spring.handlers";
Spring.Schemas 在org.springframework.beans.factory.xml.PluggableSchemaResolver这个类中
同样写死了位置
public static finalString DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
初始化的时候第一次调用的时候会调用
PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation,this.classLoader)
把所有的文件名包含的取出来放入map中。
spring.handlers内容:
http\://www.ruishenh.com/custom/mytest=com.ruishenh.spring.config.MyNamespaceHandler
spring.schemas内容:
http\://www.ruishenh.com/custom/mytest/myTest.xsd=customTag/myTest.xsd
customTag/myTest.xsd文件:
<?xml version="1.0"encoding="UTF-8"?> <xsd:schema xmlns="http://www.ruishenh.com/custom/myTest" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.ruishenh.com/custom/mytest" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:element name="executor"> <xsd:complexType> <xsd:attribute name="name" type="xsd:string"> </xsd:attribute> <xsd:attribute name="delay" type="xsd:int"> </xsd:attribute> <xsd:attribute name="interval" type="xsd:int"use="required"> </xsd:attribute> <xsd:attribute name="address" type="xsd:string"> </xsd:attribute> <xsd:attribute name="entity" type="xsd:string"> </xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="entity"> <xsd:complexType> <xsd:attribute name="name" type="xsd:string"> <xsd:annotation> <xsd:documentation><![CDATA[ bean Name ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="splitBy" type="xsd:string"> <xsd:annotation> <xsd:documentation xml:lang="zh"><![CDATA[ sqoop分割字段 ]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="sql" type="xsd:string"> <xsd:annotation> <xsd:documentation xml:lang="zh"><![CDATA[数据库操作sql]]></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name="dbTypeID"> <xsd:annotation> <xsd:documentation xml:lang="zh"> <![CDATA[ 指定数据库类型 如果类型不存在就判断dbTypeName属性,1是mysql,2是oracle,3是sqlserver ]]> </xsd:documentation> </xsd:annotation> <xsd:simpleType> <xsd:restriction base="xsd:int"> <xsd:enumeration value="1" /> <xsd:enumeration value="2" /> <xsd:enumeration value="3" /> </xsd:restriction> </xsd:simpleType> </xsd:attribute> <xsd:attribute name="dbTypeName"> <xsd:annotation> <xsd:documentation xml:lang="zh"><![CDATA[ 指定数据库指定名称 如果 dbTypeID 不存在就取当前值 ]]></xsd:documentation> </xsd:annotation> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="mysql" /> <xsd:enumeration value="oracle" /> <xsd:enumeration value="sqlserver" /> </xsd:restriction> </xsd:simpleType> </xsd:attribute> </xsd:complexType> </xsd:element> </xsd:schema>
spring/myTest.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:mi="http://www.ruishenh.com/custom/mytest" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.ruishenh.com/custom/mytesthttp://www.ruishenh.com/custom/mytest/myTest.xsd"> <mi:entity dbTypeID="1" dbTypeName="mysql"splitBy="id" sql="1" name="mye" /> <mi:executor interval="5000" address="127.0.0.1"delay="2000" name="myexecutor" entity="mye" /> </beans>
com.ruishenh.spring.config.MyNamespaceHandler 命名空间处理类:
package com.ruishenh.spring.config; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class MyNamespaceHandler extends NamespaceHandlerSupport{ @Override public void init() { registerBeanDefinitionParser("executor",newMyBeanDefinitionParser(MyExecutor.class)); registerBeanDefinitionParser("entity",newMyBeanDefinitionParser(MyEntity.class)); } }
这个类主要是在DefaultNamespaceHandlerResolver这个类中getHandlerMappings()取到了所有的META-INF/spring.handlers的文件内容存入map,然后根据当前的命名空间找到对应的NamespaceHandler,然后反射出对象调用init()
Class<?> handlerClass =ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)){ throw new FatalBeanException("Class ["+ className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } NamespaceHandlernamespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init();
com.ruishenh.spring.config.MyBeanDefinitionParser解析类:
packagecom.ruishenh.spring.config; importorg.springframework.beans.factory.config.BeanDefinition; importorg.springframework.beans.factory.config.RuntimeBeanReference; importorg.springframework.beans.factory.support.RootBeanDefinition; importorg.springframework.beans.factory.xml.BeanDefinitionParser; importorg.springframework.beans.factory.xml.ParserContext; importorg.w3c.dom.Element; importorg.w3c.dom.NamedNodeMap; importorg.w3c.dom.Node; public classMyBeanDefinitionParser implements BeanDefinitionParser { private Class<?> clssze; publicMyBeanDefinitionParser(Class<?> cls) { this.clssze = cls; } @Override public BeanDefinition parse(Elementelement, ParserContext parserContext) { RootBeanDefinitionbeanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(clssze); String id = null; id =element.getAttribute("name"); if (id == null) { if (clssze ==MyExecutor.class) { id = "test_myExecutor"; } else if (clssze ==MyEntity.class) { id ="test_myentity"; } else { throw newIllegalStateException("MyBeanDefinitionParser.parse,未知的业务逻辑处理:class:" +element.getAttribute("class")); } } int counter = 2; while(parserContext.getRegistry().containsBeanDefinition(id)) { id = id +(counter++); } if (id != null &&id.length() > 0) { if(parserContext.getRegistry().containsBeanDefinition(id)) { throw newIllegalStateException("Duplicate spring bean id " + id); } parserContext.getRegistry().registerBeanDefinition(id,beanDefinition); } NamedNodeMap nnm =element.getAttributes(); for (int i = 0; i <nnm.getLength(); i++) { Node node =nnm.item(i); String key =node.getLocalName(); String value =node.getNodeValue(); if(key.equals("entity")) { if(parserContext.getRegistry().containsBeanDefinition(value)) { beanDefinition.getPropertyValues().add(key,parserContext.getRegistry().getBeanDefinition(value)); } else { beanDefinition.getPropertyValues().add(key,new RuntimeBeanReference(value)); } } else { beanDefinition.getPropertyValues().add(key,value); } } return beanDefinition; } }
这个类会在
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Elementroot, BeanDefinitionParserDelegate delegate)这个方法中入口执行
然后到org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse(Elementelement, ParserContext parserContext);
最后找到对应的BeanDefinitionParser执行parse方法。至于放入BeanDefinitionParser的入口就在自定义的NamespaceHandler中init()方法中。
com/ruishenh/spring/config/MyEntity.java实体类:
package com.ruishenh.spring.config; import com.ruishenh.model.BaseModel; public classMyEntity extendsBaseModel{ private int dbTypeID; private String dbTypeName; private String splitBy; private String sql; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSplitBy() { return splitBy; } public void setSplitBy(StringsplitBy) { this.splitBy = splitBy; } public String getSql() { return sql; } public void setSql(String sql) { this.sql = sql; } public int getDbTypeID() { return dbTypeID; } public void setDbTypeID(int dbTypeID) { this.dbTypeID = dbTypeID; } public String getDbTypeName(){ return dbTypeName; } public void setDbTypeName(StringdbTypeName) { this.dbTypeName = dbTypeName; } }
com/ruishenh/spring/config/MyExecutor.java实体类:
package com.ruishenh.spring.config; import com.ruishenh.model.BaseModel; public classMyExecutor extends BaseModel{ private String name; private int delay; private int interval; private String address; private MyEntity entity; public MyEntity getEntity() { return entity; } public void setEntity(MyEntity entity) { this.entity = entity; } public int getDelay() { return delay; } public void setDelay(int delay) { this.delay = delay; } public int getInterval() { return interval; } public void setInterval(int interval) { this.interval = interval; } public String getAddress() { return address; } public void setAddress(Stringaddress) { this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试类:
package com.ruishenh.spring.test; import org.springframework.context.support.FileSystemXmlApplicationContext; import com.ruishenh.spring.config.MyEntity; import com.ruishenh.spring.config.MyExecutor; public classTest { public static void main(String[] args) { Stringconf = "classpath:spring/myTest.xml"; FileSystemXmlApplicationContextac = newFileSystemXmlApplicationContext(conf); MyExecutorme = ac.getBean(MyExecutor.class); System.out.println(me.toString()); MyEntity mye = ac.getBean(MyEntity.class); System.out.println(mye.toString()); } }
运行结果:
MyExecutor[name=myexecutor,delay=2000,interval=5000,address=127.0.0.1,entity=MyEntity[dbTypeID=1,dbTypeName=mysql,splitBy=id,sql=1,name=mye]]
MyEntity[dbTypeID=1,dbTypeName=mysql,splitBy=id,sql=1,name=mye]