轻量级Rpc框架设计--motan源码解析二:自定义spring标签与解析

一, 如何自定义标签

可参考以前本人针对spring自定义标签写过的一篇博客, Spring自定义标签, 其实原理就是利用spring在初始化的过程中会加载META-INF/spring.handlers, META-INF/spring.schemes这两项文件,  再通过继承NamespaceHandlerSupport类, 并且自定义一个实现类实现BeanDefinitionParser接口就完成了自定的spring标签.

二, motan解析自定义标签

上一篇我们使用xml配置文件的方式进行了服务的暴露与引用, 如下:

motan针对服务暴露, server端xml文件的配置

<bean id="motanDemoServiceImpl" class="com.weibo.motan.demo.server.MotanDemoServiceImpl"/>
<motan:registry regProtocol="zookeeper" name="registry" address="127.0.0.1:2181"/>
<motan:protocol id="demoMotan" default="true" name="motan"
                    requestTimeout="220" maxServerConnection="80000" maxContentLength="1048576"
                    maxWorkerThread="800" minWorkerThread="20"/>
                    
<motan:basicService requestTimeout="220" export="demoMotan:8002"
                        group="motan-demo-rpc" accessLog="false" shareChannel="true" module="motan-demo-rpc"
                        application="myMotanDemo" registry="registry" id="serviceBasicConfig"/>
<motan:service interface="com.weibo.motan.demo.service.MotanDemoService"
                   ref="motanDemoServiceImpl" export="demoMotan:8001" basicService="serviceBasicConfig"
                   requestTimeout="200">
</motan:service>
<motan:service interface="com.weibo.motan.demo.service.MotanDemoService"
                   ref="motanDemoServiceImpl" export="demoMotan:8002" basicService="serviceBasicConfig"
                   requestTimeout="200">
</motan:service>

motan针对服务引用, client端xml文件配置

<motan:registry regProtocol="zookeeper" name="registry" address="127.0.0.1:2181" requestTimeout="5000"/>
<motan:protocol default="true" name="motan" haStrategy="failover"
		loadbalance="roundrobin" maxClientConnection="10" minClientConnection="2"/>
<motan:basicReferer requestTimeout="200" accessLog="false"
		retries="2" group="motan-demo-rpc" module="motan-demo-rpc"
		application="myMotanDemo" protocol="motan" registry="registry"
		id="motantestClientBasicConfig" throwException="false" check="true" />
<motan:referer id="motanDemoReferer"
		interface="com.weibo.motan.demo.service.MotanDemoService"
		connectTimeout="300" requestTimeout="300" basicReferer="motantestClientBasicConfig"/>

motan框架对以上配置的解析源码: 通过继承BeanDefinitionPaser接口, 并实现pase()方法.

public class MotanBeanDefinitionParser implements BeanDefinitionParser {

    private final Class<?> beanClass;

    private final boolean required;

    public MotanBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        try {
            return parse(element, parserContext, beanClass, required);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

paser解析方法:该方法就是将上面server端和client端的xml配置文件中配置项进行逐行解析, 并将解析的内容放入spring容器.

private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required)
            throws ClassNotFoundException {
        RootBeanDefinition bd = new RootBeanDefinition();
        bd.setBeanClass(beanClass);
        // 不允许lazy init
        bd.setLazyInit(false);

        // 如果没有id则按照规则生成一个id,注册id到context中
        String id = element.getAttribute("id");
        
        // 如果xml配置文件中该标签没有id属性, 则使用name属性
        if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("name");
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                generatedBeanName = element.getAttribute("class");
            }
            
            // 如果解析的标签既没有配置id或者name, 那就使用该属性包装类的全限定名称com.weibo.api.xxx.xxz
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                generatedBeanName = beanClass.getName();
            }
            id = generatedBeanName;
            int counter = 2;
            while (parserContext.getRegistry().containsBeanDefinition(id)) {
                id = generatedBeanName + (counter++);
            }
        }
        if (id != null && id.length() > 0) {
            if (parserContext.getRegistry().containsBeanDefinition(id)) {
                throw new IllegalStateException("Duplicate spring bean id " + id);
            }
            parserContext.getRegistry().registerBeanDefinition(id, bd);
        }
        bd.getPropertyValues().addPropertyValue("id", id);
        if (ProtocolConfig.class.equals(beanClass)) {
            MotanNamespaceHandler.protocolDefineNames.add(id);

        } else if (RegistryConfig.class.equals(beanClass)) {
            MotanNamespaceHandler.registryDefineNames.add(id);

        } else if (BasicServiceInterfaceConfig.class.equals(beanClass)) {
            MotanNamespaceHandler.basicServiceConfigDefineNames.add(id);

        } else if (BasicRefererInterfaceConfig.class.equals(beanClass)) {
            MotanNamespaceHandler.basicRefererConfigDefineNames.add(id);

        } else if (ServiceConfigBean.class.equals(beanClass)) {
            String className = element.getAttribute("class");
            if (className != null && className.length() > 0) {
                RootBeanDefinition classDefinition = new RootBeanDefinition();
                classDefinition.setBeanClass(Class.forName(className, true, Thread.currentThread().getContextClassLoader()));
                classDefinition.setLazyInit(false);
                parseProperties(element.getChildNodes(), classDefinition);
                bd.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
            }
        }

        Set<String> props = new HashSet<String>();
        ManagedMap parameters = null;
        // 把配置文件中的可以set的属性放到bd中
        for (Method setter : beanClass.getMethods()) {
            String name = setter.getName();
            // 必须是setXXX
            if (name.length() <= 3 || !name.startsWith("set") || !Modifier.isPublic(setter.getModifiers())
                    || setter.getParameterTypes().length != 1) {
                continue;
            }
            String property = (name.substring(3, 4).toLowerCase() + name.substring(4)).replaceAll("_", "-");
            props.add(property);
            if ("id".equals(property)) {
                bd.getPropertyValues().addPropertyValue("id", id);
                continue;
            }
            String value = element.getAttribute(property);
            if (StringUtils.isBlank(value) && "protocol".equals(property)) {
                // srevice中的protocol信息是隐含在export中,所以需要从export中获取protocol来配置
                String exportValue = element.getAttribute(URLParamType.export.getName());
                if (!StringUtils.isBlank(exportValue)) {
                    value = ConfigUtil.extractProtocols(exportValue);
                }
            }
            if ("methods".equals(property)) {
                parseMethods(id, element.getChildNodes(), bd, parserContext);
            }
            if (StringUtils.isBlank(value)) {
                continue;
            }
            value = value.trim();
            if (value.length() == 0) {
                continue;
            }
            Object reference;
            if ("ref".equals(property)) {
                if (parserContext.getRegistry().containsBeanDefinition(value)) {
                    BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                    if (!refBean.isSingleton()) {
                        throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value
                                + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
                    }
                }
                reference = new RuntimeBeanReference(value);
            } else if ("protocol".equals(property)) {
                if (!value.contains(",")) {
                    reference = new RuntimeBeanReference(value);
                } else {
                    parseMultiRef("protocols", value, bd, parserContext);
                    reference = null;
                }
            } else if ("registry".equals(property)) {
                parseMultiRef("registries", value, bd, parserContext);
                reference = null;
            } else if ("basicService".equals(property)) {
                reference = new RuntimeBeanReference(value);

            } else if ("basicReferer".equals(property)) {
                reference = new RuntimeBeanReference(value);

            } else if ("extConfig".equals(property)) {
                reference = new RuntimeBeanReference(value);
            } else {
            	// <motan:service>标签的interface属性, 也是在这时候被add到spring容器
                reference = new TypedStringValue(value);
            }

            if (reference != null) {
                bd.getPropertyValues().addPropertyValue(property, reference);
            }
        }
        if (ProtocolConfig.class.equals(beanClass)) {
            // 把剩余的属性放到protocol的parameters里面
            NamedNodeMap attributes = element.getAttributes();
            int len = attributes.getLength();
            for (int i = 0; i < len; i++) {
                Node node = attributes.item(i);
                String name = node.getLocalName();
                if (!props.contains(name)) {
                    if (parameters == null) {
                        parameters = new ManagedMap();
                    }
                    String value = node.getNodeValue();
                    parameters.put(name, new TypedStringValue(value, String.class));
                }
            }
            bd.getPropertyValues().addPropertyValue("parameters", parameters);
        }
        return bd;
    }

三, 总结

motan中自定义spring标签, 其原理就是利用了spring在初始化过程中会去加载META-INF/spring.handlers, META-INF/spring.schemes这两个文件, 其中spring.schemes文件中定义了xsd文件的具体路径, 如:http\://api.weibo.com/schema/motan.xsd=META-INF/motan.xsd, 而spring.handlers中则是定义了具体的解析类, 如:http\://api.weibo.com/schema/motan=com.weibo.api.motan.config.springsupport.MotanNamespaceHandler,该MotanNamespaceHandler类则继承自NamespaceHandlerSupport抽象类, 实现其init()方法, 完成具体的解析过程.

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值