項目中有一個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框架。