项目中有一个HelloService的接口,接口中有一个sayHello方法,可是实现并无在本项目中,而是在其余项目,在项目启动时先注入一个代理对象。接下来看如何扩展spring的schema。不少的操做和解释都写在了代码的注释里面,因此在外部就不作过多的解释了。java
项目文件的目录结构以下git
├─java │ └─wtf │ ├─main │ │ Test.java (测试类) │ │ │ ├─namespace │ │ ├─handler │ │ │ MyConsumer.java (代理consumer节点的Bean) │ │ │ MyNamespaceHandler.java (myrpc.xsd schema的处理类) │ │ │ MyRpcBeanDefinitionParse.java (处理xml节点的统一处理类) │ │ │ │ │ └─proxy │ │ RpcServiceBeanProxy.java (动态代理对象) │ │ │ └─service │ HelloService.java (代理的接口) │ └─resources │ applicationContext.xml (spring的入口文件) │ myconsumer.xml (使用了mrpx.xsd schema 的xmlbean文件) │ └─META-INF myrpc.xsd spring.handlers (spring 要求的扩展点名称格式,不容许修改) spring.schemas (spring 要求的扩展点格式,不容许修改)
myrpc.xsdweb
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.myrpccompany.com/schema/myrpc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.myrpccompany.com/schema/myrpc" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:element name="consumer"> <xsd:complexType> <xsd:attribute name="id" type="xsd:ID"/> <xsd:attribute name="interface" type="xsd:string" use="required"/> </xsd:complexType> </xsd:element> </xsd:schema>
定义一个xml的schema,空间为http://www.myrpccompany.com/schema/myrpcspring
定义一个名称为consumer的节点,consumer节点中有两个属性,一个是id,一个是interface,id属性讲做为实例化Bean的id,interface属性是指的要实现哪一个接口。bash
consumer.xmlapp
<?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:myrpc="http://www.myrpccompany.com/schema/myrpc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.myrpccompany.com/schema/myrpc http://www.myrpccompany.com/schema/myrpc/myrpc.xsd"> <myrpc:consumer id="helloService" interface="wtf.service.HelloService"/> </beans>
引用第一步中定义的schema,而后命名bean的id为helloService,实现的接口是wtf.service.HelloService这个接口框架
xml文件都比较熟悉,没什么好讲的。dom
在spring中,若是想扩展其schema,须要在class根目录新建一个META-INF文件夹,文件夹中须要有两个文件ide
spring.handlers函数
http\://www.myrpccompany.com/schema/myrpc=wtf.namespace.handler.MyNamespaceHandler
spring.schemas
http\://www.myrpccompany.com/schema/myrpc/myrpc.xsd=META-INF/myrpc.xsd
处理myrpc.xml 中myrpcscheam的类
package wtf.namespace.handler; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * Created by wangtengfei1 on 2017/8/2. */ public class MyNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { /** *spring解析到consumer这个节点时,将会自动交给MyRpcBeanDefinitionParse这个解析器进行处理, * MyRpcBeanDefinitionParse须要继承BeanDefinitionParser,而且实现它的parse方法 * MyRpcBeanDefinitionParse构造函数接收了一个MyConsumer的class * 这么定义是由于在myrpc.xsd中不必定只有一个consumer节点,也有有些其余的节点, * 咱们想把myrpc.xsd中定义的全部节点,均交给MyRpcBeanDefinitionParse进行处理, * 只是对各个节点的具体实例化,须要传入相应的ClassBean进行接收, * MyConsumer就是为了consumer节点在实例化以前存储的一些信息 */ registerBeanDefinitionParser("consumer", new MyRpcBeanDefinitionParse(MyConsumer.class)); } }
具体的处理方法
package wtf.namespace.handler; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.w3c.dom.Element; /** * Created by wangtengfei1 on 2017/8/3. */ public class MyRpcBeanDefinitionParse implements BeanDefinitionParser { private final Class<?> beanClass; MyRpcBeanDefinitionParse(Class<?> beanClass){ this.beanClass = beanClass; } public BeanDefinition parse(Element element, ParserContext parserContext) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(); rootBeanDefinition.setBeanClass(beanClass); String id = element.getAttribute("id"); parserContext.getRegistry().registerBeanDefinition(id,rootBeanDefinition); //处理Consumer中的属性和xml中定义的attribute不一致的问题 if(beanClass.equals(MyConsumer.class)){ String interfaceId = element.getAttribute("interface"); rootBeanDefinition.getPropertyValues().addPropertyValue("interfaceId",interfaceId); } return rootBeanDefinition; } }
MyConsumer类的信息 ,相关信息在代码中都有注释,就再也不作过多解释
package wtf.namespace.handler; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import wtf.namespace.proxy.RpcServiceBeanProxy; /** * Created by wangtengfei1 on 2017/8/3. */ public class MyConsumer implements InitializingBean,FactoryBean, ApplicationContextAware,BeanNameAware,DisposableBean { protected String id; //用来接收xml 文件的id属性 做为bean的id protected String interfaceId; //用来接收xml中定义的interface属性,用来做为代理的类型 protected Class objectType; //代理类的类型,也就是xml中定义的接口类型 //在使用@Autowire或者@Resource等,须要注入时真正调用的方法 public Object getObject() throws Exception { RpcServiceBeanProxy proxy = new RpcServiceBeanProxy(objectType); return proxy.getProxyOject(); } //获取代理对象的类型,spring会自动调用 public Class<?> getObjectType() { try { Class<?> aClass = Class.forName(interfaceId); //判断interface节点中定义的是否是一个接口,若是不是接口就返回null if(aClass.isInterface()){ objectType = aClass; return aClass; } } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } public boolean isSingleton() { return false; } public void afterPropertiesSet() throws Exception { } public void setBeanName(String s) { } public void destroy() throws Exception { } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getInterfaceId() { return interfaceId; } public void setInterfaceId(String interfaceId) { this.interfaceId = interfaceId; } }
对于本类中使用的RpcServiceBeanProxy 类,是我在另一篇博客中使用的 javassist 实现接口动态代理 用来返回一个代理对象。并实现sayHello方法,输出一句 “hi from dynamic proxy class”.
package wtf.main; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.web.context.support.XmlWebApplicationContext; import wtf.service.HelloService; /** * Created by wangtengfei1 on 2017/8/2. */ public class Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); HelloService helloService = (HelloService) applicationContext.getBean("helloService"); helloService.sayHello(); } }
其中 applicationContext.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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <import resource="myconsumer.xml"/> </beans>
运行test测试,输出的结果为
以上示例简单的演示了如何扩展spring的schema,而后借助于spring,让它帮忙加载咱们须要的东西。以便对spring进行扩展。主要的设想是集成spring实现一个简单的rpc框架。