Spring Bean的lookup-mehtod和replace-method原理解析

Spring Bean的lookup-mehtod和replace-method原理解析

解析阶段

1、在解析xml文件的时候,会把配置了lookup-method、replace-method的属性的值放入到GenericBeanDefinition的overrides中。如下代码:

BeanDefinitionParserDelegate#parseLookupOverrideSubElements方法:

    /**
     * Parse lookup-override sub-elements of the given bean element.
     */
    public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
                Element ele = (Element) node;
                String methodName = ele.getAttribute(NAME_ATTRIBUTE);
                String beanRef = ele.getAttribute(BEAN_ELEMENT);
                LookupOverride override = new LookupOverride(methodName, beanRef);
                override.setSource(extractSource(ele));
                overrides.addOverride(override);
            }
        }
    }

BeanDefinitionParserDelegate#parseReplacedMethodSubElements方法:

    /**
     * Parse replaced-method sub-elements of the given bean element.
     */
    public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
                Element replacedMethodEle = (Element) node;
                String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
                String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
                ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
                // Look for arg-type match elements.
                List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
                for (Element argTypeEle : argTypeEles) {
                    String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
                    match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
                    if (StringUtils.hasText(match)) {
                        replaceOverride.addTypeIdentifier(match);
                    }
                }
                replaceOverride.setSource(extractSource(replacedMethodEle));
                overrides.addOverride(replaceOverride);
            }
        }
    }

调用阶段

1、在getBean的生命周期中的实例化这一步骤中,判断了当前bd是否配置了override属性。

原理上是使用cglib代理对方法进行拦截,从而实现功能的增强

    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        // 如果配置了overrides。即配置lookup-method 和 replace-method 方法的原理
        if (!bd.hasMethodOverrides()) {
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.  
            // 采用cglib代理对lookup-method和replace-method方法做增强      
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

2、cglib代理简单分析。如下是cglib代理的通用模式。

设置对“谁”做增强:setSuperclass方法

设置回调的过滤条件:setCallbackFilter方法

设置回调函数:setCallbacks方法设置回调的逻辑

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
enhancer.setCallbackTypes(CALLBACK_TYPES);
// 设置回调的逻辑
enhancer.setCallbacks(new Callback[] {NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)})

3、具体分析2种回调函数怎么起作用的

  • LookupOverrideMethodInterceptor拦截器

可以看到,lookup-method的原理是吧配置了lookup-method的属性值,当做beanName重新从Spring容器中获取bean,然后返回新bean

public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    // Cast is safe, as CallbackFilter filters are used selectively.
    // 可以安全的强制转换,大概是上文提到的设置了cglib的“回调过滤器”
    LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
    Assert.state(lo != null, "LookupOverride not found");
    Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
    if (StringUtils.hasText(lo.getBeanName())) {
        // 【lookup-method原理】:获取到lookup-mehtod指向bean的名称,并返回
        Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                this.owner.getBean(lo.getBeanName()));
        return (bean.equals(null) ? null : bean);
    }
    else {
        return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
                this.owner.getBean(method.getReturnType()));
    }
}
  • ReplaceOverrideMethodInterceptor拦截器

可以看到,replace-method的原理是吧配置了replace-method的属性值,当做beanName重新从Spring容器中获取bean,然后调用新bean的reimplement方法

public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
    ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
    Assert.state(ro != null, "ReplaceOverride not found");
    // TODO could cache if a singleton for minor performance optimization
    MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
    // 然后调用新bean的reimplement方法
    return mr.reimplement(obj, method, args);
}

总结

知道他两的回调逻辑就先行了吧,暂未发现特别好用的点。

你可能感兴趣的:(java,spring,java,后端)