Spring集成第三方组件-JSF

一、JSF介绍

jsf是京东内部自研使用的RPC框架,同dubbo,目前已开源至github—joyrpc。本文从spring的角度去看是如何集成jsf的。

二、Spring在配置文件支持自定义namespace

通过阅读源码画出如下流程图,从中可看出在解析XML时会调用BeanDefinitionParserDelegate来解析自定义的namespace,并根据其指定的NamespaceHandler实例,调用parse方法完成beanDefinition注册。


image.png
public class BeanDefinitionParserDelegate {
    /**
     * Parse a custom element (outside of the default namespace).
     * @param ele the element to parse
     * @param containingBd the containing bean definition (if any)
     * @return the resulting bean definition
     */
     @Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
}  

三、JSF通过自定义namespace注册bean

1. jsf-comsumer.xml常规配置

在Sping的bean配置文件中增加jsf的schema信息后,声明两个jsf的bean:注册中心和一个示例消费者。

<?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:jsf="http://jsf.jd.com/schema/jsf"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://jsf.jd.com/schema/jsf http://jsf.jd.com/schema/jsf/jsf.xsd">

  <!-- 注册中心 -->
  <jsf:registry id="jsfRegistry" protocol="jsfRegistry" index="i.jsf.jd.com" />

  <!-- 服务调用者配置-->
  <jsf:consumer id="demoConsumer" interface="pers.jsf.demo.IDemoConsumer" protocol="jsf" alias="demo" />
</beans>
2. 找到jsf的schema对应的handler
image.png

在jsf-1.6.9的依赖中找到jsf集成spring的两个文件:spring.schemas和spring.handlers(可类比参考joyrpc/joyrpc-spring/src/main/resources/META-INF目录,只是schema有些差异)

1)spring.schemas

声明xml中jsf相关配置的语法,可用于让IDE自动提示和校验语法。

http\://jsf.jd.com/schema/jsf/jsf.xsd=META-INF/jsf.xsd

2)spring.handlers

声明xml中jsf命名空间对应的NamespaceHandler实例。

http\://jsf.jd.com/schema/jsf=com.jd.jsf.gd.config.spring.JSFNamespaceHandler
3. JSFNamespaceHandler

JSFNamespaceHandler是spring的NamespaceHanlder的子类,用来处理命名空间下声明的各个标签。在pase方法里调用了init方法中注册的针对不同标签的BeanDefinitionParser(目的是把bean定义解析注册逻辑分离到JSFBeanDefinitionParser,单一职责)。

image.png
public class JSFNamespaceHandler extends NamespaceHandlerSupport {
    private final Map<String, BeanDefinitionParser> parsers = new HashMap<String, BeanDefinitionParser>();

    public void init() {
        this.registerBeanDefinitionParser("provider", new JSFBeanDefinitionParser(ProviderBean.class, true));
        this.registerBeanDefinitionParser("consumer", new JSFBeanDefinitionParser(ConsumerBean.class, true));
        this.registerBeanDefinitionParser("consumerGroup", new JSFBeanDefinitionParser(ConsumerGroupBean.class, true));
        this.registerBeanDefinitionParser("server", new JSFBeanDefinitionParser(ServerBean.class, true));
        this.registerBeanDefinitionParser("registry", new JSFBeanDefinitionParser(RegistryConfig.class, true));
        this.registerBeanDefinitionParser("annotation", new JSFBeanDefinitionParser(AnnotationBean.class, true));
        this.registerBeanDefinitionParser("parameter", new JSFParameterDefinitionParser(ParameterConfig.class));
        this.registerBeanDefinitionParser("filter", new JSFBeanDefinitionParser(FilterBean.class, true));
        this.registerBeanDefinitionParser("connStrategy", new JSFBeanDefinitionParser(ConnStrategyBean.class, true));
    }
    
  @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        return findParserForElement(element, parserContext).parse(element, parserContext);
    }

    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        String localName = parserContext.getDelegate().getLocalName(element);
        BeanDefinitionParser parser = this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        return parser;
    }
  
  protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
        this.parsers.put(elementName, parser);
    }
}
4. JSFBeanDefinitionParser

JSFBeanDefinitionParser是spring的BeanDefinitionParser的子类,用来根据xml中配置的参数,把某个jsf标签解析为一个spring bean。由于类代码较长,取其中关键代码,可参考joyrpc-AbstractBeanDefinitionParser.java

image.png
 public class JSFBeanDefinitionParser implements BeanDefinitionParser {
    private static final Logger logger = LoggerFactory.getLogger(JSFBeanDefinitionParser.class);
    private final Class<?> beanClass;
    private final boolean required;

    public JSFBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }
   
        public BeanDefinition parse(Element element, ParserContext parserContext) {
        return this.parse(element, parserContext, this.beanClass, this.required);
    }
   
        private BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean requireId) {
          RootBeanDefinition beanDefinition = new RootBeanDefinition();
          beanDefinition.setBeanClass(beanClass);
          beanDefinition.setLazyInit(false);
      
                    /** 
                    *   从element取属性填充到beanDefinition中,调用方法:
                    *   beanDefinition.getPropertyValues().addPropertyValue(property, value);
                    * 忽略此处逻辑
                    */ 
      
          String id = element.getAttribute("id");
                    parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
          return beanDefinition;
        }
    }

由源码可见在创建RootBeanDefinition时设置的类是JSFNamespaceHandler中传入的,此处我们取ConsumerBean进行观察。

5. ConsumerBean
image.png

从类图可看出ConsumerBean是一个FactoryBean,在其getObject方法里,调用了JSF的获取Consumer实例的API,并返回实例。

public class ConsumerBean<T> extends ConsumerConfig<T> implements InitializingBean, FactoryBean, ApplicationContextAware, DisposableBean, BeanNameAware {
    private static final long serialVersionUID = 6835324481364430812L;
    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerBean.class);
    private ApplicationContext applicationContext;
    private transient String beanName;
    private transient T object;
    private transient Class objectType;

    protected ConsumerBean() {
    }

    public T getObject() throws Exception {
        this.object = CommonUtils.isUnitTestMode() ? null : this.refer();
        return this.object;
    }
  
    ...
}

6. 小结

spring通过声明自定义namespace对应的NamespaceHandler子类,来处理xml中jsf标签的解析和注册逻辑。且最终注册的beanDefinition是一个FactoryBean,在其getObject里进行具体bean实例的生成。

四、总结

第三方组件要集成进入spring,把类交给spring容器进行声明周期管理,一般都会采取FactoryBean实现。通过FactoryBean可以获取足够的灵活性,第三方组件可以用一个公共的逻辑对组件内的类完成注入Spring的过程。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容