扩展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地址

相关文章
相关标签/搜索