擴展spring schema

項目中有一個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 要求的擴展點格式,不容許修改)

 

1、定義xsd文件

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

2、根據xsd文件,編寫xml文件

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

3、接入spring進行擴展

    在spring中,若是想擴展其schema,須要在class根目錄新建一個META-INF文件夾,文件夾中須要有兩個文件ide

  1.     spring.handlers 須要處理定義的xsd schema的類,須要繼承NamespaceHandlerSupport類,這樣當元素讀取到定義的這種schema時,講會交給在此文件中指定的類進行處理。
  2.     spring.schemas 有哪些自定的schema

    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

4、處理Schema中接收到的元素

處理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」.

5、測試

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框架。

附git地址

相關文章
相關標籤/搜索