美文网首页
如何在filter等dubbo自管理组件中注入spring的be

如何在filter等dubbo自管理组件中注入spring的be

作者: dracula337435 | 来源:发表于2019-03-15 12:50 被阅读0次

1.抛出问题

dubbo的设计思路是微内核+插件,Filter等插件被dubbo创建,而不是被spring创建。
Filterspring两不相认,如果想在Filter中使用被spring管理的对象,注入spring的bean,怎么办?

2.先给出结论

Filter中新建一个setter方法。此方法名称形如setAbc,有且仅有一个参数。
spring上下文中定义一个名为“abc”的bean,类型要对。
如此即可实现,在Fliter被实例化后,此setter被调用,传入名为“abc”的bean(一定要类型正确,名字对上更好,详细算法见3.2节结尾处)。
(4.章介绍另一种机制,spring利用instrumentationload-time-weaver令非spring管理的对象也能使用spring基础设施,开阔思路,不建议用)

3.源码分析

这部分尽可能从现象到原因探索。

  1. 插件实例化后可被调setter,参数从objectFactory中来
  2. objectFactoryExtensionFactory接口类型的,SpringExtensionFactory是此接口的一个实现类
  3. SpringExtensionFactory在使用dubbo-spring时被设置好

3.1.插件实例化后可被调setter,参数从objectFactory中来

Filter中的setter可被dubbo调起,在springApplicationContext中按名字查找并塞进一个bean。
Filter等插件被按SPI机制管理,com.alibaba.dubbo.common.extension.ExtensionLoader<T>public T getExtension(String name)函数被用于加载特定的插件,其中有一层缓存,真正的创建过程在private T createExtension(String name)函数中。createExtension(...)函数源码如下

private T createExtension(String name) {
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                type + ")  could not be instantiated: " + t.getMessage(), t);
    }
}

可见其中的主要步骤:

  1. 调用ClassnewInstance()方法,实例化插件
  2. 调用ExtensionLoader<T>injectExtension(T instance),从objectFactory中拿一个bean,调用插件的setter方法
  3. 处理一下wrapper

再看injectExtension(...)函数源码:

private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            for (Method method : instance.getClass().getMethods()) {
                if (method.getName().startsWith("set")
                        && method.getParameterTypes().length == 1
                        && Modifier.isPublic(method.getModifiers())) {
                    Class<?> pt = method.getParameterTypes()[0];
                    try {
                        String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                        Object object = objectFactory.getExtension(pt, property);
                        if (object != null) {
                            method.invoke(instance, object);
                        }
                    } catch (Exception e) {
                        logger.error("fail to inject via method " + method.getName()
                                + " of interface " + type.getName() + ": " + e.getMessage(), e);
                    }
                }
            }
        }
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
    }
    return instance;
}

可见其中的主要步骤:

  1. 遍历方法
  2. 如果是setter(即,方法名以“set”开头,有1个参数,是public
  3. 得到属性名,从objectFactory中得到bean
  4. 调用setter

3.2.objectFactory是ExtensionFactory接口类型的,SpringExtensionFactory是此接口的一个实现类

属性private final ExtensionFactory objectFactory在私有构造函数中被初始化:

private ExtensionLoader(Class<?> type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

接口ExtensionFactory中只有一个方法,见源码:

@SPI
public interface ExtensionFactory {

    /**
     * Get extension.
     *
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

按照dubboSPI机制的惯例,见META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory内容:

adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory

关注“spring”名称对应的SpringExtensionFactory,其源码片段:

public <T> T getExtension(Class<T> type, String name) {
    for (ApplicationContext context : contexts) {
        if (context.containsBean(name)) {
            Object bean = context.getBean(name);
            if (type.isInstance(bean)) {
                return (T) bean;
            }
        }
    }

    logger.warn("No spring extension(bean) named:" + name + ", try to find an extension(bean) of type " + type.getName());

    for (ApplicationContext context : contexts) {
        try {
            return context.getBean(type);
        } catch (NoUniqueBeanDefinitionException multiBeanExe) {
            throw multiBeanExe;
        } catch (NoSuchBeanDefinitionException noBeanExe) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
            }
        }
    }

    logger.warn("No spring extension(bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");

    return null;
}

可见其中查找逻辑:

  1. 遍历ApplicationContext
  2. 先按名字找bean,要求类型必须正确
  3. 如果上一步找不到,就不考虑此名字了。改为仅按类型找,要求比类型bean必须是唯一的

3.3.SpringExtensionFactory在使用dubbo-spring时被设置好

查找SpringExtensionFactory在哪里被调用,仅ReferenceBeanServiceBean,这两个类是使用dubbo-spring一定绕不开的。
涉及SpringExtensionFactory的逻辑相似,ReferenceBeanServiceBean均实现了ApplicationContextAware,回调方法中有

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
    SpringExtensionFactory.addApplicationContext(applicationContext);
}

ServiceBean的此方法中多了增加监听器的功能)
可见其中调用了SpringExtensionFactory的静态方法public static void addApplicationContext(ApplicationContext context)

4.另一种做法

核心思路是利用instrumentationload-time-weaver
启动时加入-javaagent参数,在自定义Filter上加@Configurable注解。
不建议的理由主要是需要修改启动脚本。

相关文章

网友评论

      本文标题:如何在filter等dubbo自管理组件中注入spring的be

      本文链接:https://www.haomeiwen.com/subject/goqhmqtx.html