【spring以及第三方jar的案例】在spring中的aop相关配置的标签,线程池相关配置的标签,都是基于该种方式实现的。包括dubbo的配置标签都是基于该方式实现的。
【一】原理
===>spring在解析xml标签,一旦不是以<bean>开头的元素,就会走org.springframework.beans.factory.xml.BeanDefinitionParserDelegate的parseCustomElement(Element ele)方法解析自定义的标签
===>在该方法里会根据xml中的命名空间去查询该标签对应的NamespaceHandler接口的实现类去解析该配置标签。该接口解析该配置标签,并形成BeanDefinition注册到IOC容器中。
===>该扩展需要做的内容:
(1)建立spring.handlers文件,这是在解析xml配置文件的时候,spring会通过xml文件头的命名空间,去找该配置文件中的NamespaceHandler的实现类。
(2)建立spring.schemas文件,这是在xml文件中配置自定义标签的标签合法验证,也是合法检验。如果随意填写配置标签,spring将无法解析。
(3)在所扩展的项目的resources目录下,建立META-INF目录,并将两个文件放置在目录下。
(4)将spring.schemas中的xsd文件配置在随意的类路径下。关于xsd文件,可以了解:http://www.w3school.com.cn/schema/index.asp
(5)建立NamespaceHandler接口的实现类,建立BeanDefinitionParser的实现类。用于解析自已定义标签的内容。
【二】实现例子:定义一个自定义标签,实现一个类ZKClient的bean通过NamespaceHandler注册IOC容器。本例子已经通过测试。不写测试方法,只写实现过程。
(1)spring.handlers文件内容
http\://localhost.com/sxf=com.mobile.thinks.manages.namespaceHandler.SxfNameSpaceHandler
(2)spring.schemas文件内容
http\://localhost.com/sxf.xsd=com/mobile/thinks/manages/namespaceHandler/sxf.xsd
(3)xsd文件内容
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://localhost.com/sxf" xmlns="http://www.w3school.com.cn" elementFormDefault="qualified"> <xsd:element name="zk"> <xsd:complexType> <xsd:attribute name="host" use="required" > <xsd:simpleType> <xsd:restriction base="xsd:string"/> </xsd:simpleType> </xsd:attribute> <xsd:attribute name="port" use="required" > <xsd:simpleType> <xsd:restriction base="xsd:integer"/> </xsd:simpleType> </xsd:attribute> <xsd:attribute name="user" use="required" > <xsd:simpleType> <xsd:restriction base="xsd:string"/> </xsd:simpleType> </xsd:attribute> <xsd:attribute name="pwd" use="required" > <xsd:simpleType> <xsd:restriction base="xsd:string"/> </xsd:simpleType> </xsd:attribute> </xsd:complexType> </xsd:element> </xsd:schema>
(4)spring的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:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:sxf="http://localhost.com/sxf" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://localhost.com/sxf http://localhost.com/sxf.xsd "> <sxf:zk host="127.0.0.1" port="2181" user="shangxiaofei" pwd="smxcyx"/> <!-- <context:property-placeholder location="classpath:resources.properties"/> --> <!-- 扫描注解Bean --> <context:component-scan base-package="com.mobile.thinks.**"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> <context:include-filter type="annotation" expression="org.springframework.beans.factory.annotation.Autowired"/> </context:component-scan> </beans>
(5)NamespaceHandler接口实现类的内容
package com.mobile.thinks.manages.namespaceHandler; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * 自定义标签的注解解释器 * @author sxf * */ public class SxfNameSpaceHandler extends NamespaceHandlerSupport { /** * 初始化zk元素的具体解析器 */ @Override public void init() { registerBeanDefinitionParser("zk", new ZkBeanDefinitionParser()); } }
(6)BeanDefinitionParser接口实现类的内容
package com.mobile.thinks.manages.namespaceHandler; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; public class ZkBeanDefinitionParser implements BeanDefinitionParser{ private static final String HOST ="host"; private static final String PORT="port"; private static final String USER="user"; private static final String PWD="pwd"; /** * 解析标签,形成特定的beanDefinition加入到ioc容器中 */ @Override public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ZkClient.class); String host=element.getAttribute("host"); String port=element.getAttribute("port"); String user=element.getAttribute("user"); String pwd=element.getAttribute("pwd"); builder.addPropertyValue("host",host); builder.addPropertyValue("port", Integer.valueOf(port)); builder.addPropertyValue("user", user); builder.addPropertyValue("pwd", pwd); parserContext.getRegistry().registerBeanDefinition("zkClient", builder.getBeanDefinition()); return builder.getBeanDefinition(); } }
(7)Zkclient类的内容,将来在项目中用一下方式直接使用该类
@Autowired
private ZkClient zkClient;
package com.mobile.thinks.manages.namespaceHandler; /** * 该类用自定义的NameSpaceHandler类解析配置文件向IOC容器中注册 * @author sxf * */ public class ZkClient { private String host; private int port; private String user; private String pwd; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
【三】项目结构图
【&&】需要注意的点,当maven打包的时候,默认不会将sxf.xsd文件打包进jar包,将来项目用到的时候就不会从本地读取到该sxf.xsd文件,则需要在pom.xml文件配置如下,才可以将文件打包到相应的位置。也可以将其他格式的文件,打包到jar包相应的位置,只需要修改相应文件的后缀。
<build> <!-- 打成jar包的名字 --> <finalName>test</finalName> <!-- 这样也可以把所有的readme文件,打包到相应位置。其他的比如XX.xml文件,也是同样配置 --> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.txt</include> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xsd</include> </includes> </resource> </resources> </build>
该项目的jar完整的pom.xml文件
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.mobile</groupId> <artifactId>thinks</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.mobile</groupId> <artifactId>thinks-manages</artifactId> <version>1.0.0</version> <name>thinks-manages</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <build> <!-- <finalName>test</finalName> 打包成默认名字 --> <!-- 这样也可以把所有的readme文件,打包到相应位置。其他的比如XX.xml文件,也是同样配置 --> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.txt</include> <include>**/*.xml</include> <include>**/*.handlers</include> <include>**/*.schemas</include> <include>**/*.drl</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xsd</include> </includes> </resource> </resources> </build> <dependencies> <dependency> <groupId>com.mobile</groupId> <artifactId>thinks-service</artifactId> <version>${thinks.service.version}</version> </dependency> <dependency> <groupId>com.mobile</groupId> <artifactId>thinks-core</artifactId> <version>${thinks.core.version}</version> </dependency> <dependency> <groupId>com.mobile</groupId> <artifactId>thinks-commons</artifactId> <version>${thinks.commons.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- drools规则引擎 --> <dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artifactId> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-compiler</artifactId> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>knowledge-api</artifactId> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-decisiontables</artifactId> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-jsr94</artifactId> <version>${drools.version}</version><!--$NO-MVN-MAN-VER$--> </dependency> <!-- drools升级6.5.0.final版本依赖 --> <dependency> <groupId>org.eclipse.jdt.core.compiler</groupId> <artifactId>ecj</artifactId> </dependency> </dependencies> </project>