欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

dubbo源码分析01:SPI机制

程序员文章站 2023-01-01 10:10:01
一、什么是SPI SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件。这样可以在运行时,动态为该接口替换实现类。 JDK提供了默认的SPI实现,但是Dubbo并未使用JDK提供的SPI,而是自己 ......

一、什么是spi

  spi全称为service provider interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件。这样可以在运行时,动态为该接口替换实现类。
  jdk提供了默认的spi实现,但是dubbo并未使用jdk提供的spi,而是自己封装了一套。我们先来通过dubbo官网给的两个例子简单了解下jdk和dubbo的spi是如何使用的。

1.1.jdk spi示例

  首先定义一个接口以及它的两个实现类
 1 public interface robot {
 2     void sayhello();
 3 }
 4 
 5 public class optimusprime implements robot {
 6     
 7     @override
 8     public void sayhello() {
 9         system.out.println("hello, i am optimus prime.");
10     }
11 }
12 
13 public class bumblebee implements robot {
14 
15     @override
16     public void sayhello() {
17         system.out.println("hello, i am bumblebee.");
18     }
19 }

  接下来,在项目(以一个典型的maven项目为例)的“resources/meta-inf/services”路径下创建一个文件,名称为robot的全限定名“org.apache.spi.robot”。文件内容如下:

org.apache.spi.optimusprime
org.apache.spi.bumblebee

  编写测试代码,运行之后可以看到两个实现类被加载并调用了sayhello方法(调用结果演示略)。

public class javaspitest {

    @test
    public void sayhello() throws exception {
        serviceloader<robot> serviceloader = serviceloader.load(robot.class);
        system.out.println("java spi");
        serviceloader.foreach(robot::sayhello);
    }
}

 

1.2.dubbo spi示例

  仍然使用jdk spi示例中的接口和其实现类代码,不过需要在接口robot上添加注解@spi
//使用spi注解标注的接口
@spi
public interface robot {
    void sayhello();
}

  接下来,在项目(以一个典型的maven项目为例)的“resources/meta-inf/dubbo”路径下创建一个文件,名称为robot的全限定名“org.apache.spi.robot”。文件内容如下:

optimusprime=org.apache.spi.optimusprime
bumblebee=org.apache.spi.bumblebee

 

  通过测试类进行测试,则会执行相应实现类的sayhello方法:
//测试类
public class dubbospitest {

    @test
    public void sayhello() throws exception {
        //传入一个标注有@spi的接口class,通过getextensionloader获取该spi接口的extensionloader实例
        extensionloader<robot> extensionloader = extensionloader.getextensionloader(robot.class);
        //传入要获取的实现类的name(meta-inf/dubbo/org.apache.spi.robot文件下的name),获取实现类实例
        robot optimusprime = extensionloader.getextension("optimusprime");
        optimusprime.sayhello();
        robot bumblebee = extensionloader.getextension("bumblebee");
        bumblebee.sayhello();
    }
}

  通过与jdk spi示例的比较,发现dubbo spi与jdk spi最大的不同就是dubbo spi通过键值对的方式进行配置。这样最大的好处是可以按需加载指定的实现类(通过name指定)。下面就让我们以本例中getextensionloader与getextension两个方法作为引子,分析dubbo spi的实现源码。

 

二、getextensionloader

  该方法根据spi接口类型创建一个extensionloader实例,即每种类型的spi接口都有一个相应的extensionloader。代码如下所示:
/**************************************** 相关字段 ****************************************/

//extensionloader对应的spi接口类型
private final class<?> type;

//extensionloader对应的extensionfactory实例
private final extensionfactory objectfactory;

//extensionloader全局缓存,key为spi接口类型,value为相应的extensionloader实例
private static final concurrentmap<class<?>, extensionloader<?>> extension_loaders = new concurrenthashmap<>();

/**************************************** 相关方法 ****************************************/

/**
 * 静态方法,根据spi接口类型获取相应的extensionloader
 */
public static <t> extensionloader<t> getextensionloader(class<t> type) {
    //判断type是否为空、是否是接口类型、是否具有@spi注解
    if (type == null) {
        throw new illegalargumentexception("extension type == null");
    }
    if (!type.isinterface()) {
        throw new illegalargumentexception("extension type (" + type + ") is not an interface!");
    }
    if (!withextensionannotation(type)) {
        throw new illegalargumentexception("extension type (" + type + ") is not an extension...");
    }
    
    //从extensionloader的缓存中根据spi接口类型获取对应的extensionloader实例
    extensionloader<t> loader = (extensionloader<t>) extension_loaders.get(type);
    //若缓存没有该实例,则new一个,并且存放入缓存,key为type
    if (loader == null) {
        extension_loaders.putifabsent(type, new extensionloader<t>(type));
        loader = (extensionloader<t>) extension_loaders.get(type);
    }
    return loader;
}

/**
 * 私有构造器,对调用者而言,只能通过getextensionloader方法获取extensionloader实例
 */
private extensionloader(class<?> type) {
    //保存该extensionloader的spi接口类型信息
    this.type = type; 
    //若spi接口类型为extensionfactory,则不设置字段extensionfactory,
    //否则需要设置一个extensionfactory。具体的获取逻辑我们后面会讲到
    objectfactory = (type == extensionfactory.class ? null : extensionloader.getextensionloader(extensionfactory.class).getadaptiveextension());
}

 

三、getextension

  该方法用于获取extensionloader对应的spi接口的实现类实例,name用于指定需要获取的实现类类型。在后面,我们将spi接口实现类统一称为扩展类。
 
/**************************************** 实例缓存相关字段 ****************************************/

//全部spi接口的扩展类实例缓存,key为扩展类class(每一个spi接口的每一个实现类的class都不同)
//value为对应的扩展类实例。注意该缓存要与另外一个类似的缓存cachedinstances区分开。
private static final concurrentmap<class<?>, object> extension_instances = new concurrenthashmap<>();

//每个extensionloader对应的spi接口的扩展类实例缓存,
//key为扩展类的name,value为holder对象,其持有/维护扩展类的实例。
private final concurrentmap<string, holder<object>> cachedinstances = new concurrenthashmap<>();

/**************************************** class缓存相关字段 ****************************************/

//每个extensionloader对应的spi接口的扩展类class缓存,key为扩展类的name,value为扩展类class
private final holder<map<string, class<?>>> cachedclasses = new holder<>();

//spi接口的扩展类中具有@adaptive注解的扩展类class缓存
private volatile class<?> cachedadaptiveclass = null;

//spi接口的扩展类中被判定为具有包装功能的扩展类class缓存
private set<class<?>> cachedwrapperclasses;

//spi接口的扩展类中具有@activate注解的缓存,key是扩展类names[0],value为@activate的class
private final map<string, object> cachedactivates = new concurrenthashmap<>();

/**************************************** name缓存相关字段 ****************************************/

//spi接口的扩展类的name缓存,key为扩展类的class,value为扩展类的names[0]
private final concurrentmap<class<?>, string> cachednames = new concurrenthashmap<>();

//spi接口的默认扩展类的name
private string cacheddefaultname;

/**************************************** 其他相关字段 ****************************************/

//extensionloader对应的spi接口class
private final class<?> type;

/**************************************** 相关方法 ****************************************/

/**
 * 获取extensionloader对应的spi接口的扩展类实例
 */
public t getextension(string name) {
    //检查扩展类的name
    if (stringutils.isempty(name)) {
        throw new illegalargumentexception("extension name == null");
    }
    //如果传入的name值为true,则获取默认的spi接口扩展类实例
    if ("true".equals(name)) {
        return getdefaultextension();
    }
    //getorcreateholder方法比较简单,它从缓存“cachedinstances”中获取该name对应的holder实例,
    //holder是一个“持有类”,其可能持有扩展类实例
    holder<object> holder = getorcreateholder(name);
    object instance = holder.get();
    //如果未获取到实例,在监视器锁中进行第二次获取与创建
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                //创建name对应的扩展类实例,并缓存到cachedinstances中
                instance = createextension(name);
                holder.set(instance);
            }
        }
    }
    return (t) instance;
}

/**
 * 获取默认的扩展类实例
 */
public t getdefaultextension() {
    //调用getextensionclasses方法获取spi接口配置的扩展类信息,返回结果为一个map<string, class>,
    //其中key为扩展类name,value为该name对应的扩展类class。
    getextensionclasses(); 
    
    //如果spi接口默认扩展类name为空或者为true,则默认的扩展类实例为null
    if (stringutils.isblank(cacheddefaultname) || "true".equals(cacheddefaultname)) {
        return null;
    }
    //否则调用getextension根据默认的扩展类name去获取实例
    return getextension(cacheddefaultname);
}

/**
 * 创建一个扩展类的实例
 */
private t createextension(string name) {
    //调用getextensionclasses方法,从返回结果中获取name对应的扩展类class,如果class为null,则抛出异常
    class<?> clazz = getextensionclasses().get(name);  
    if (clazz == null) {
        throw findexception(name);
    }
    try {
        //从缓存“extension_instances”中根据扩展类class获取实例
        t instance = (t) extension_instances.get(clazz);
        if (instance == null) {
            //缓存未命中则通过反射创建一个实例,并存入缓存
            extension_instances.putifabsent(clazz, clazz.newinstance());
            instance = (t) extension_instances.get(clazz);
        }
        //向这个扩展类实例注入其所需要的属性(以setxxx为准),该方法比较复杂,我们后面会进行分析
        injectextension(instance);
        //获取spi接口扩展类中具有包装功能的扩展类class缓存,然后对instance进行层层包装(装饰器模式),
        //对每次包装出来的新实例进行属性注入,全部包装完成后让instance指向最终的包装结果实例 
        set<class<?>> wrapperclasses = cachedwrapperclasses;
        if (collectionutils.isnotempty(wrapperclasses)) {
            for (class<?> wrapperclass : wrapperclasses) {
                instance = injectextension((t) wrapperclass.getconstructor(type).newinstance(instance));
            }
        }
        return instance;
    } catch (throwable t) {
        throw new illegalstateexception("extension instance ... couldn't be instantiated: ");
    }
}

/**
 * 获取spi接口配置的扩展类信息
 */
private map<string, class<?>> getextensionclasses() {
    //从缓存“cachedclasses”中获取已加载的扩展类
    map<string, class<?>> classes = cachedclasses.get();
    if (classes == null) {
        synchronized (cachedclasses) {
            classes = cachedclasses.get();
            if (classes == null) {
                //缓存为空,调用loadextensionclasses方法加载spi接口配置的扩展类信息,并缓存结果
                classes = loadextensionclasses();
                cachedclasses.set(classes);
            }
        }
    }
    return classes;
}

/**
 * 加载spi接口配置的扩展类信息
 */
private map<string, class<?>> loadextensionclasses() {
    //获取spi接口默认扩展类的name并进行缓存
    cachedefaultextensionname();
    
    //读取并解析spi接口的配置文件,去几个固定的目录下读取
    //(1):meta-inf/services/spi接口全限定类名(或spi接口全限定类名替换org.apache为com.alibaba)
    //(2):meta-inf/dubbo/spi接口全限定类名(或spi接口全限定类名替换org.apache为com.alibaba)
    //(3):meta-inf/dubbo/internal/spi接口全限定类名(或spi接口全限定类名替换org.apache为com.alibaba)
    map<string, class<?>> extensionclasses = new hashmap<>();
    loaddirectory(extensionclasses, dubbo_internal_directory, type.getname());
    loaddirectory(extensionclasses, dubbo_internal_directory, type.getname().replace("org.apache", 
                                                                                     "com.alibaba"));
    loaddirectory(extensionclasses, dubbo_directory, type.getname());
    loaddirectory(extensionclasses, dubbo_directory, type.getname().replace("org.apache", 
                                                                            "com.alibaba"));
    loaddirectory(extensionclasses, services_directory, type.getname());
    loaddirectory(extensionclasses, services_directory, type.getname().replace("org.apache", 
                                                                               "com.alibaba"));
    return extensionclasses;
}

/**
 * 获取spi接口默认扩展类的name并进行缓存
 */
private void cachedefaultextensionname() {
    //获取extensionloader对应的spi接口上的spi注解
    final spi defaultannotation = type.getannotation(spi.class);
    if (defaultannotation != null) {
        //获取spi注解的value值并进行校验
        string value = defaultannotation.value();
        if ((value = value.trim()).length() > 0) {
            string[] names = name_separator.split(value);
            if (names.length > 1) {
                throw new illegalstateexception("more than 1 default extension name ...");
            }
            if (names.length == 1) {
                //将其作为spi接口默认扩展类的name并进行缓存
                cacheddefaultname = names[0];
            }
        }
    }
}

/**
 * 加载spi接口的配置文件
 */
private void loaddirectory(map<string, class<?>> extensionclasses, string dir, string type) {
    string filename = dir + type;
    try {
        enumeration<java.net.url> urls;
        //获取并使用类加载器去加载文件(同名文件进行内容合并)
        classloader classloader = findclassloader();
        if (classloader != null) {
            urls = classloader.getresources(filename);
        } else {
            urls = classloader.getsystemresources(filename);
        }
        if (urls != null) {
            //遍历获取到的url,并调用loadresource去加载资源
            while (urls.hasmoreelements()) {
                java.net.url resourceurl = urls.nextelement();
                loadresource(extensionclasses, classloader, resourceurl);
            }
        }
    } catch (throwable t) {
        logger.error("exception occurred when loading extension class (interface: ...");
    }
}

/**
 * 在loaddirectory的基础上,对每个文件中的内容进行加载与解析
 */
private void loadresource(map<string, class<?>> extensionclasses, classloader classloader, java.net.url resourceurl) {
    try {
        try (bufferedreader reader = new bufferedreader(new inputstreamreader(resourceurl.openstream(), standardcharsets.utf_8))) {
            string line;
            while ((line = reader.readline()) != null) {
                //按行读取,每一行先去掉#号后面的内容(#号后面的内容为注释)
                final int ci = line.indexof('#');
                if (ci >= 0) {
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        string name = null;
                        //按“=”号截取name和扩展类的全限定类名,可以看出name是可以没有的
                        int i = line.indexof('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        //如果line,即扩展类全限定类名不为空,通过class.forname获取其class,
                        //然后调用loadclass继续加载该class
                        if (line.length() > 0) {
                            loadclass(extensionclasses, resourceurl, class.forname(line, true, classloader), name);
                        }
                    } catch (throwable t) {
                        illegalstateexception e = new illegalstateexception("failed to load ...");
                        exceptions.put(line, e);
                    }
                }
            }
        }
    } catch (throwable t) {
        logger.error("exception occurred when loading extension class (interface: ...");
    }
}

/**
 * 在loadresource的基础上,对文件中的每行获取到的class进行分析,并进行缓存
 */
private void loadclass(map<string, class<?>> extensionclasses, java.net.url resourceurl, class<?> clazz, string name) throws nosuchmethodexception {
    //判断配置的扩展类是否为指定spi接口的实现类
    if (!type.isassignablefrom(clazz)) {
        throw new illegalstateexception("error occurred when loading ... is not subtype of interface.");
    }
    if (clazz.isannotationpresent(adaptive.class)) {
        //若扩展类有@adaptive注解,将这个class存入缓存“cachedadaptiveclass”
        //注意:一个spi接口配置文件中,只能配置一个有@adaptive注解的扩展类
        cacheadaptiveclass(clazz);
    } else if (iswrapperclass(clazz)) {
        //若扩展类具有clazz.getconstructor(type)这样的构造器,则认为其是一个具有包装功能的扩展类,
        //并将其存入缓存“cachedwrapperclasses”,一个spi接口可以配置多个用于包装的扩展类
        cachewrapperclass(clazz);
    } else {
        //进入到这个分支,表示该class只是一个普通的spi接口扩展类。
        //判断该扩展类是否具有无参构造器
        clazz.getconstructor();
        //如果该扩展类在spi接口配置文件中未定义name,则判断扩展类是否具有@extension注解,
        //如果有,则以@extension的value值作为name;否则以扩展类的simplename作为name,
        //并且,如果扩展类的simplename以spi接口的simplename作为后缀结尾,则name需要去掉该后缀
        if (stringutils.isempty(name)) {
            name = findannotationname(clazz);
            if (name.length() == 0) {
                throw new illegalstateexception("no such extension name for the class ...");
            }
        }    
        //对name按照","进行分割
        string[] names = name_separator.split(name);
        if (arrayutils.isnotempty(names)) {
            //names不为空
            //若扩展类具有@activate注解,则使用names数组的第一个元素作为key,@activate的class为value
            //将映射关系存入缓存“cachedactivates”
            cacheactivateclass(clazz, names[0]);
            for (string n : names) {
                //将该扩展类的name和class存入缓存“cachednames”,name保持为names[0]
                cachename(clazz, n);
                //将该扩展类的name和class存入方法参数extensionclasses
                saveinextensionclass(extensionclasses, clazz, name);
            }
        }
    }
}

  通过以上的源码分析,我们了解到getextension方法获取一个spi接口的扩展类实例的流程分为解析配置文件、加载并缓存扩展类、创建并加工扩展类实例几个步骤。

  在得到一个最终可用的扩展类实例前,该实例会进行属性注入与层层包装,这些行为在官网上被成为扩展点特性,这里我们把它称之为“扩展类特性”。官方给出了4个特性,分别为“扩展点自动包装”、“扩展点自动装配”、“扩展点自适应”以及“扩展点自动激活”。在下面的其余章节中,我们将重点来研究这几个扩展点特性。

 

四、扩展点自适应

  在分析与getextension方法密切相关的扩展点自动装配与扩展点自动包装之前,我们先来了解一下扩展点自适应这个特性。扩展点自适应是一个非常重要的特性,因为dubbo某些spi接口的扩展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载,这种动态决定调用哪个扩展类实例的方式使得dubbo非常的灵活。此外,先了解这个特性,也有助于我们更好的分析自动装配与自动包装的源码。  

4.1.什么是自适应扩展类

  自适应扩展类同样是某个spi接口的扩展类,该扩展类具备这样的一些特征:它没有实际的业务逻辑,而是能够根据传入的参数,动态的获取对应的扩展类实例。可以看一下官网给的例子:
/**
 * 一个模拟的spi接口
 */
@spi
public interface wheelmaker {
    wheel makewheel(url url);
}

/**
 * spi接口的自适应扩展类
 */
public class adaptivewheelmaker implements wheelmaker {
    //自适应扩展类该方法的逻辑为通过url中的参数,动态的获取真正需要执行的spi接口扩展类
    public wheel makewheel(url url) {
        if (url == null) {
            throw new illegalargumentexception("url == null");
        }      
        // 1.从url中获取wheelmaker名称
        string wheelmakername = url.getparameter("wheel.maker");
        if (wheelmakername == null) {
            throw new illegalargumentexception("wheelmakername == null");
        }
        // 2.通过spi加载具体的wheelmaker
        wheelmaker wheelmaker = extensionloader.getextensionloader(wheelmaker.class).getextension(wheelmakername);        
        // 3.调用目标方法
        return wheelmaker.makewheel(url url);
    }
}

/**
 * 一个模拟的spi接口
 */
@spi
public interface carmaker {
    car makecar(url url);
}

/**
 * 汽车制造者spi接口的扩展类
 */
public class racecarmaker implements carmaker {
    
    wheelmaker wheelmaker;
 
    //在injectextension方法中会通过setter注入adaptivewheelmaker
    //目前我们只知道自动装配行为的结果,原因会在“扩展点自动装配”小节分析
    public setwheelmaker(wheelmaker wheelmaker) {
        this.wheelmaker = wheelmaker;
    }
 
    //实现的方法
    public car makecar(url url) {
        //实际调用adaptivewheelmaker的makewheel方法,获得当前运行环境参数url下需要使用的wheel扩展类
        wheel wheel = wheelmaker.makewheel(url);
        return new racecar(wheel, ...);
    }
}

  假设运行时传入这样一个url参数“dubbo://192.168.0.101:20880/xxxservice?wheel.maker=michelinwheelmaker”,那么racecar最终的wheel为扩展类“michelinwheelmaker”制造出来的轮胎。

4.2.获取自适应扩展类实例

  让我们回到extensionloader类,在该类中提供了getadaptiveextension方法用于获取一个spi接口的自适应扩展类的实例,这个方法的源码分析如下:
/**************************************** 相关字段 ****************************************/

//缓存的自适应扩展类实例
private final holder<object> cachedadaptiveinstance = new holder<>();

//spi接口的扩展类中具有@adaptive注解的扩展类class缓存
private volatile class<?> cachedadaptiveclass = null;

//创建自适应扩展类实例的异常信息
private volatile throwable createadaptiveinstanceerror;

/**************************************** 相关方法 ****************************************/

/**
 * 获取自适应扩展类的实例
 */
public t getadaptiveextension() {
    //从extensionloader的缓存字段中获取数据,cachedadaptiveinstance为一个holder<object>对象
    object instance = cachedadaptiveinstance.get();
    if (instance == null) {
        //需要判断一下创建自适应扩展对象的throwable缓存是否存在,如果存在,直接抛出
        if (createadaptiveinstanceerror == null) {
            //并发控制
            synchronized (cachedadaptiveinstance) {
                instance = cachedadaptiveinstance.get();
                if (instance == null) {
                    try {
                        //创建自适应扩展类实例
                        instance = createadaptiveextension();
                        //设置缓存
                        cachedadaptiveinstance.set(instance);
                    } catch (throwable t) {
                        createadaptiveinstanceerror = t;
                        throw new illegalstateexception("......");
                    }
                }
            }
        } else {
            throw new illegalstateexception("......");
        }
    }

    return (t) instance;
}

/**
 * 创建自适应扩展类实例
 */
private t createadaptiveextension() {
    try {
        //(1)调用getadaptiveextensionclass方法获取自适应扩展类的class;
        //(2)通过newinstance实例化一个自适应扩展类的对象;
        //(3)调用injectextension方法向自适应拓展类的实例中注入依赖,参考“扩展点自动装配”小节;
        return injectextension((t) getadaptiveextensionclass().newinstance());
    } catch (exception e) {
        throw new illegalstateexception("......");
    }
}

/**
 * 获取自适应扩展类的class
 */
private class<?> getadaptiveextensionclass() {
    //获取该extensionloader对应spi接口配置的所有扩展类(参考“getextensionclsses”小节) 
    getextensionclasses();
    //检查具有@adaptive注解的扩展类缓存,若缓存不为空,则直接返回缓存
    if (cachedadaptiveclass != null) {
        return cachedadaptiveclass;
    }
    //如果spi接口配置的所有扩展类都没有被@adaptive标注,则创建自适应扩展类
    return cachedadaptiveclass = createadaptiveextensionclass();
}

/**
 * 创建自适应扩展类
 */
private class<?> createadaptiveextensionclass() {
    //通过adaptiveclasscodegenerator的generate方法创建自适应扩展代码
    //该方法会检测spi接口中是否有被@adapative注解的方法,对于要生成自适应扩展类的spi接口
    //必须至少包含一个被@adaptive注解的方法,否则会抛出异常
    string code = new adaptiveclasscodegenerator(type, cacheddefaultname).generate();
    classloader classloader = findclassloader();
    //获取编译器实现类,一样是通过adaptiveextension进行获取,获取之后进行编译
    org.apache.dubbo.common.compiler.compiler compiler = extensionloader.getextensionloader(org.apache.dubbo.common.compiler.compiler.class).getadaptiveextension();
    return compiler.compile(code, classloader);
}

   通过getadaptiveextension方法的流程可以发现,要想获得一个spi接口的自适应扩展类实例,有2种方式:

  • 在spi接口的配置文件中配置具有@adaptive注解的扩展类,在执行解析spi接口配置文件方法getextensionclasses时,它会调用loadclass方法,该方法判断扩展类是否具有@adaptive注解,如果有,则将该类class缓存到extensionloader的字段“cachedadaptiveclass”中,然后直接实例化该class的实例并进行自动装配;
  • 如果未配置@adaptive修饰的扩展类,则dubbo会使用字节码技术创建一个自适应扩展类,前提是spi接口上至少有一个被@adaptive注解的方法;
  在上述两种创建自适应扩展类实例的方法中,都与@adaptive这个注解息息相关,该注解的代码如下:
@documented
@retention(retentionpolicy.runtime)
@target({elementtype.type, elementtype.method})
public @interface adaptive {
    string[] value() default {};
}

   可以看到,@adaptive注解既可以使用在类上,又可以使用在方法上。其包含一个字符串数组的属性,在通过字节码技术创建自适应扩展类时,该属性参与到生成逻辑中,具体的创建逻辑我们马上通过下一节来了解。

4.3.关于创建自适应扩展类

  dubbo通过字节码技术创建一个自适应扩展类,第一步使用adaptiveclasscodegenerator类创建一个自适应扩展类的代码字符串,第二步通过extensionloader获取字节码编译器compiler并编译加载第一步中的代码字符串,拿到自适应扩展类的class。下面我们对每一个步骤进行详细的源码分析。

4.3.1.代码拼接

  回顾一下extensionloader.createadaptiveextensionclass方法中调用逻辑
string code = new adaptiveclasscodegenerator(type, cacheddefaultname).generate();

  首先传入spi接口的class与默认的扩展类名称(即@spi注解的value值)创建一个adaptiveclasscodegenerator实例,之后调用generate方法生成代码,adaptiveclasscodegenerator相对应的源码如下:

/**************************************** 相关字段 ****************************************/

//spi接口类型
private final class<?> type;
    
//spi接口默认的扩展类名称,即@spi注解的value属性值
private string defaultextname;
 
/**************************************** 相关方法 ****************************************/

/**
 * 可以看到,构造器并未做太多的事情,只是简单的将传入的参数为成员变量赋值
 */
public adaptiveclasscodegenerator(class<?> type, string defaultextname) {
    this.type = type;
    this.defaultextname = defaultextname;
}

/**
 * 创建自适应扩展类的代码串
 */
public string generate() {
    //遍历spi接口是否具有@adaptive注解修饰的方法,如果没有则抛出异常
    if (!hasadaptivemethod()) {
        throw new illegalstateexception("......");
    }
    //按照一个java类的代码组成顺序,拼接代码字符串
    stringbuilder code = new stringbuilder();
    //拼接包信息字符串: "package" + spi接口所在的包
    code.append(generatepackageinfo()); 
    //拼接import字符串: "import" + extensionloader的全限定名
    code.append(generateimports());
    //拼接类开头字符串: "public class" + spi接口简单名 + "$adaptive implements" + spi接口全限定名 + "{"
    //注意:使用全限定名的原因为import代码串中只导入extensionloader的类,下同
    code.append(generateclassdeclaration());
    //拼接每一个方法字符串,逻辑比较复杂,在generatemethod方法源码中详细说明
    method[] methods = type.getmethods();
    for (method method : methods) {
        code.append(generatemethod(method));
    }
    //拼接类结束字符串: "}"
    code.append("}");
    return code.tostring();
}

/**
 * 检测该spi接口是否具有@adaptive修饰的方法
 */
private boolean hasadaptivemethod() {
    return arrays.stream(type.getmethods()).anymatch(m -> m.isannotationpresent(adaptive.class));
}

/**
 * 生成自适应扩展类的方法代码串
 */
private string generatemethod(method method) {
    //获取方法返回值类型全限定名
    string methodreturntype = method.getreturntype().getcanonicalname(); 
    //获取方法名
    string methodname = method.getname(); 
    //获取方法内容,有无@adaptive修饰的方法其方法内容不同,详细见下generatemethodcontent源码
    string methodcontent = generatemethodcontent(method); 
    //获取方法参数列表,格式为"参数类型全限定名 arg0, 参数类型全限定名 arg1, ..."
    string methodargs = generatemethodarguments(method);
    //获取方法异常抛出,格式为"throws 异常1全限定名, 异常2全限定名, ..."
    string methodthrows = generatemethodthrows(method);
    //获取一个方法代码串
    return string.format(code_method_declaration, methodreturntype, methodname, methodargs, methodthrows, methodcontent);
}

/**
 * 生成自适应扩展类的方法体代码串
 */
private string generatemethodcontent(method method) {
    adaptive adaptiveannotation = method.getannotation(adaptive.class);
    stringbuilder code = new stringbuilder(512);
    //判断当前要生成的方法是否有@adaptive注解
    if (adaptiveannotation == null) {
        //对于没有@adaptive注解的方法,生成"throw new unsupportedoperationexception(...)"代码
        return generateunsupported(method);
    } else {
        //检查方法参数列表中是否有类型为"com.apache.dubbo.common.url"的参数
        //简单提一下,com.apache.dubbo.common.url是dubbo框架中各组件的数据总线
        int urltypeindex = geturltypeindex(method);           
        if (urltypeindex != -1) {
            //如果方法参数列表中有url类型参数,则为该参数生成判断是否为null以及赋值的代码:
            //(generateurlnullcheck方法比较简单,不进行详细的源码分析,这里只给出逻辑)
            //if (arg%d == null) throw new illegalargumentexception("url == null");
            //com.apache.dubbo.common.url url = arg%d;
            //d的值为urltypeindex
            code.append(generateurlnullcheck(urltypeindex));
        } else {
            //如果方法参数列表中没有url类型参数,则需要遍历参数列表中每一个参数的类型信息,
            //判断哪一个参数具有"public url getxxx()"签名形式的方法,如果有则停止遍历且生成如下代码:
            //(generateurlassignmentindirectly方法比较简单,不进行详细的源码分析,这里只给出逻辑)
            //if (arg%d == null) 备注:此处的d为具备"public url getxxx()"方法的参数下标,从0开始
            //    throw new illegalargumentexception("参数全限定名 + argument == null");
            //if (arg%d.getter方法名() == null) 
            //    throw new illegalargumentexception(参数全限定名 + argument geturl() == null);
            //com.apache.dubbo.common.url url = arg%d.getter方法名();
            code.append(generateurlassignmentindirectly(method));
        }
        
        //获取该方法的@adaptive注解的属性值value(为一个string数组),这里列出处理逻辑
        //如果属性值value为非空数组,直接获取数组内容即可;
        //如果value为空数组,则需将spi接口的类名按照驼峰命名法进行检测,对每个驼峰进行分割并插入"."号,
        //然后转为小写,比如loadbalance经过处理后,得到load.balance
        string[] value = getmethodadaptivevalue(adaptiveannotation);
        
        //判断当前方法的参数列表中是否有类型为org.apache.dubbo.rpc.invocation的参数
        boolean hasinvocation = hasinvocationargument(method);
        
        //为当前方法参数列表中第一个类型为org.apache.dubbo.rpc.invocation的参数生成判null语句以及调用语句
        //if (arg%d == null) throw new illegalargumentexception("invocation == null");
        //string methodname = arg%d.getmethodname();
        //%d是org.apache.dubbo.rpc.invocation类型的参数在参数列表中的下标
        code.append(generateinvocationargumentnullcheck(method));
        
        //使用前面获取到的字符串数组value以及invocation参数存在标识拼接获取扩展类extname的代码串,
        //这是自适应扩展类核心的代码,因为自适应扩展类的目的就是要根据当前运行参数,
        //判断应该获取spi接口的哪一个扩展类,而extensionloader.getextension方法的参数就是这个extname
        //该方法逻辑比较复杂,判断分支较多,详细的分析在下面generateextnameassignment中
        code.append(generateextnameassignment(value, hasinvocation));
        
        //对上一步获取的局部变量extname拼接其判null代码
        //if(extname == null) throw new illegalstateexception("failed to get extension name from...");
        code.append(generateextnamenullcheck(value));
        
        //生成获取extname对应扩展类实例的代码串,如下:
        //%s extension = (%<s)extensionloader.getextensionloader(%s.class).getextension(extname);
        //其中%s为成员变量type.getname,即spi接口的class的全限定名
        code.append(generateextensionassignment());

        //生成目标方法调用逻辑,被@adaptive注解的方法,第一个任务是根据运行时参数获取对应的扩展类实例,
        //第二个任务就是调用这个实例的同名方法。生成的方法调用代码串格式为:
        //(1)如果该方法无返回值"extension.方法名(arg0, arg2, ..., argn);"
        //(2)如果该方法有返回值"return extension.方法名(arg0, arg1, ..., argn);"
        //其中extension为上一步生成的扩展类实例变量名,arg0、arg1就为当前@adaptive注解方法的参数名
        code.append(generatereturnandinvocation(method));
    }

    return code.tostring();
}

/**
 * 扩展名extname获取代码串
 */
private string generateextnameassignment(string[] value, boolean hasinvocation) {    
    string getnamecode = null;
    //反向遍历value,目的是生成从url中获取拓展名的代码,生成的代码会赋值给getnamecode变量
    for (int i = value.length - 1; i >= 0; --i) {
        //当遍历的元素是最后一个元素时
        if (i == value.length - 1) {
            //若默认扩展名不空(即@spi注解的value值不空)
            if (null != defaultextname) {
                //由于protocol是url的成员变量,可通过getprotocol方法获取,其他的则是从
                //url的成员变量parameters(一个map<string, string>)中获取。
                //因为获取方式不同,所以这里要判断value[i]是否为protocol
                if (!"protocol".equals(value[i])) {
                    //需要判断一下hasinvocation标识(即当前方法是否有invocation参数)
                    if (hasinvocation) {     
                        //生成的代码功能等价于下面的代码:
                        //url.getmethodparameter(methodname, value[i], defaultextname)             
                        //注意,methodname是generateinvocationargumentnullcheck生成的局部变量,下同
                        getnamecode = string.format("url.getmethodparameter(methodname, \"%s\", \"%s\")", value[i], defaultextname);
                    } else {
                        //生成的代码功能等价于下面的代码:
                        //url.getparameter(value[i], defaultextname)
                        getnamecode = string.format("url.getparameter(\"%s\", \"%s\")", value[i], defaultextname);
                    }                
                } else {
                    //生成的代码功能等价于下面代码:
                    //( url.getprotocol() == null ? defaultextname : url.getprotocol() )
                    getnamecode = string.format("( url.getprotocol() == null ? \"%s\" : url.getprotocol() )", defaultextname);
                }
            //若默认扩展名为空    
            } else {
                if (!"protocol".equals(value[i])) {
                    if (hasinvocation) {
                        //生成的代码格式同上,即
                        //url.getmethodparameter(methodname, value[i], defaultextname)  
                        getnamecode = string.format("url.getmethodparameter(methodname, \"%s\", \"%s\")", value[i], defaultextname);
                    } else {
                        //生成的代码功能等价于:url.getparameter(value[i])
                        getnamecode = string.format("url.getparameter(\"%s\")", value[i]);
                    }
                } else {
                    //生成的代码功能等价于:url.getprotocol()
                    getnamecode = "url.getprotocol()";
                }
            }
        //当遍历的元素不为最后一个时    
        } else {
            if (!"protocol".equals(value[i])) {
                if (hasinvocation) {
                    //生成的代码同上,即:
                    //url.getmethodparameter(methodname, value[i], defaultextname)      
                    getnamecode = string.format("url.getmethodparameter(methodname, \"%s\", \"%s\")", value[i], defaultextname);
                } else {
                    //在上一次获取的getnamecode代码结果基础上,再次获取getnamecode,即一层层的获取值
                    //生成的代码功能等价于下面的代码:
                    //url.getparameter(value[i], getnamecode)
                    //以transporter接口的connect方法为例,最终生成的代码如下:
                    //url.getparameter("client", url.getparameter("transporter", "netty"))
                    getnamecode = string.format("url.getparameter(\"%s\", %s)", value[i], getnamecode);
                }
            } else {
                //在上一次获取的getnamecode代码结果基础上,再次获取getnamecode,即一层层的获取值
                //生成的代码功能等价于下面的代码:
                //url.getprotocol() == null ? getnamecode : url.getprotocol()
                //以protocol接口的connect方法为例,最终生成的代码如下:
                //url.getprotocol() == null ? "dubbo" : url.getprotocol()
                getnamecode = string.format("url.getprotocol() == null ? (%s) : url.getprotocol()", getnamecode);
            }
        }
    }
    
    //返回生成extname的代码:"string extname = getnamecode;"
    return string.format(code_ext_name_assignment, getnamecode);
}

 

   总结一下生成自适应扩展类代码字符串的逻辑,首先dubbo只会对具有@adaptive注解的方法才生成详细的代码,而没有@adaptive注解的代码直接生成一段抛出异常的代码;其次生成的代码逻辑会根据当前方法的参数以及@adaptive、@spi注解的value值等因素,获取到扩展类的name,从而通过extensionloader拿到相应的扩展类实例,并调用该实例的同名方法,可以说是做了一个“动态分发”。

4.3.2.代码编译

  完成自适应扩展类的代码串生成后,获取一个字节码编译器对代码进行编译和加载:
 classloader classloader = findclassloader();
    //获取编译器实现类,一样是通过adaptiveextension进行获取,获取之后进行编译
    //在这里具体获取compiler的细节略去,最终获取到javassistcompiler类的实例进行编译
    org.apache.dubbo.common.compiler.compiler compiler = extensionloader.getextensionloader(org.apache.dubbo.common.compiler.compiler.class).getadaptiveextension();
    return compiler.compile(code, classloader);

  对于这段代码,我们可以运用本小节学到知识,来分析一下compiler接口的自适应扩展类是什么。

  首先看一下compiler这个spi接口的配置文件:
adaptive=org.apache.dubbo.common.compiler.support.adaptivecompiler
jdk=org.apache.dubbo.common.compiler.support.jdkcompiler
javassist=org.apache.dubbo.common.compiler.support.javassistcompiler

  一共配置了3个扩展类,根据name的名称,可以肯定adaptivecompiler是具有@adaptive注解的扩展类

@adaptive
public class adaptivecompiler implements compiler {

    private static volatile string default_compiler;

    public static void setdefaultcompiler(string compiler) {
        default_compiler = compiler;
    }

    @override
    public class<?> compile(string code, classloader classloader) {
        compiler compiler;
        extensionloader<compiler> loader = extensionloader.getextensionloader(compiler.class);
        string name = default_compiler; // copy reference
        if (name != null && name.length() > 0) {
            compiler = loader.getextension(name);
        } else {
            compiler = loader.getdefaultextension();
        }
        return compiler.compile(code, classloader);
    }

}

  根据4.2小节中的代码逻辑,在具有@adaptive注解修饰的扩展类的前提下,自适应扩展类实例一定获取到这个被修饰的类实例,因此“extensionloader.getextensionloader(....compiler.class).getadaptiveextension();”这段代码获取到了一个adaptivecompiler的实例。adaptivecompiler的compile方法很简单,由于成员变量“default_compiler”始终为null,其会调用extensionloader的getdefaultextension方法获取默认的扩展类实例,即@spi接口注解value值作为name的实例,看看compiler接口代码:

@spi("javassist")
public interface compiler {
    class<?> compile(string code, classloader classloader);
}

  其默认的扩展类的name为“javassist”,即类“org.apache.dubbo.common.compiler.support.javassistcompiler”,这个类的具体compile方法不多赘述,就是校验代码字符串格式的正确性,并进行加载。

 

五、扩展点自动装配

  了解了扩展点自适应后,让我们回到扩展点自动装配这个特性上来。
  在getextension方法的源码分析中,当扩展类实例通过反射被初始化后,便调用injectextension方法为其注入属性,官方将获取扩展类实例过程中的这种行为称为“扩展点自动装配”。下面我们来看一下自动装配的源码,看看dubbo是如何进行自动装配的:
/**
 * 自动装配扩展类实例
 */
private t injectextension(t instance) {
    try {
        //如果objectfactory不为空,则进行自动装配
        if (objectfactory != null) {
            //遍历扩展类实例的所有方法
            for (method method : instance.getclass().getmethods()) {
                 //检测方法是否以set开头、只有一个参数、访问修饰符为public
                if (issetter(method)) {
                    //对于有@disableinject的注解的setter方法,不需要注入
                    if (method.getannotation(disableinject.class) != null) {
                        continue;
                    }
                    //获取setter方法的参数类型
                    class<?> pt = method.getparametertypes()[0];
                    //判断参数类型是否是原始类型(基本类型+string+基本类型的包装类+date)
                    if (reflectutils.isprimitives(pt)) {
                        continue;
                    }
                    try {
                        //获取要注入属性名,比如setname方法中对应的属性名为name
                        string property = getsetterproperty(method);
                        //从objectfactory中根据属性名与属性的class类型获取依赖对象,
                        //获取到的是一个spi接口的自适应扩展类的对象或者spring环境下的一个bean,
                        //objectfactory.getextension方法的细节我们在extensionfactory中将详细讨论
                        object object = objectfactory.getextension(pt, property);
                        if (object != null) {
                            //调用setter方法进行注入
                            method.invoke(instance, object);
                        }
                    } catch (exception e) {
                        logger.error("failed to inject via method ...");
                    }
                }
            }
        }
    } catch (exception e) {
        logger.error(e.getmessage(), e);
    }
    return instance;
}

 

  对扩展类进行自动装配时,装配目标是名为“setxxx”的方法映射出来的属性,并且认为该属性的类型为setxxx方法的参数类型,属性名为“xxx”部分第一个字母小写之后加上其余字母。举个例子,有方法“setmar(car foo)”,则要注入的属性类型为car,属性名为mar。获取到装配目标的类型信息和名称信息后,调用objectfactory的getextension方法获取属性的实例。      
  回忆一下,objectfactory这个对象,是getextensionloader方法中调用extensionloader的私有构造器创建的,代码如下: 
private extensionloader(class<?> type) {
    this.type = type;
    objectfactory = (type == extensionfactory.class ? null : 
                     extensionloader.getextensionloader(extensionfactory.class).getadaptiveextension());
}

   这几行简单的代码包含了很多信息:首先,extensionfactory自身也是一个spi接口,其同样能够通过extensionloader进行初始化。其次,当获取一个spi接口的extensionloader时,如果该spi接口为extensionfactory,则不会设置objectfactory字段值,否则会通过getadaptiveextension方法拿到一个extensionfactory的实例并将字段objectfactory的值设置为它。

  在讲解扩展点自适应的代码编译小节中,我们分析了获取compiler自适应扩展类实例的流程。同样的,在这里我们也可以照葫芦画瓢的来分析获取extensionfactory接口自适应扩展实例的流程。具体过程就不赘述了,最终获取到的自适应扩展实例是一个adaptiveextensionfactory实例对象。下面我们会花几个小节介绍一个extensionfactory相关的家族成员。

5.1.extensionfactory

  extensionfactory是一个spi接口,其源码比较简单,包含一个方法用于根据class以及name获取一个实例对象,方法名getextension暗含了要获取的是扩展类对象。我们可以大胆猜测,这个方法应该是根据spi接口class和其配置文件中扩展类的name获取扩展类的实例。
@spi
public interface extensionfactory {
    <t> t getextension(class<t> type, string name);
}

  extensionfactory这个接口的继承树如下图所示:

 dubbo源码分析01:SPI机制

  它有3个实现类,分别是adaptiveextensionfactory、spiextensionfactory、springextensionfactory,相关的meta-info配置文件内容如下:

#位于dubbo-common模块下的meta-info/dubbo/internal/com.apache.dubbo.common.extensionfactory文件
adaptive=org.apache.dubbo.common.extension.factory.adaptiveextensionfactory
spi=org.apache.dubbo.common.extension.factory.spiextensionfactory

#位于dubbo-spring模块下的meta-inf/dubbo/internal/com.apache.dubbo.common.extensionfactory文件
spring=org.apache.dubbo.config.spring.extension.springextensionfactory

5.2.adaptiveextensionfactory

  其实从配置文件中adaptiveextensionfactory的name可知,其应该为一个具有@adaptive注解的扩展类作为自适应扩展类。
@adaptive
public class adaptiveextensionfactory implements extensionfactory {

    //维护了一组extensionfctory接口扩展类的实例
    private final list<extensionfactory> factories;

    //构造方法,获取所有extensionfactory配置的扩展类实例
    public adaptiveextensionfactory() {
        //加载extensionfactory对应的extensionloader
        extensionloader<extensionfactory> loader = 
            extensionloader.getextensionloader(extensionfactory.class);
        list<extensionfactory> list = new arraylist<extensionfactory>();、
        //getsupportedextensions方法用于获取extensionloader的cachedclasses缓存的keyset
        //即spi接口配置文件中name的set集合    
        for (string name : loader.getsupportedextensions()) {
            //根据name名字,调用getextension方法获取对应扩展类的实例并缓存
            list.add(loader.getextension(name));
        }
        factories = collections.unmodifiablelist(list);
    }

    @override
    public <t> t getextension(class<t> type, string name) {
        for (extensionfactory factory : factories) {
            //调用每一个extensionfactory实现类的getextension方法,并返回第一个不为null的对象
            t extension = factory.getextension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

 

5.3.spiextensionfactory

  该factory只支持具有@spi注解的接口。
public class spiextensionfactory implements extensionfactory {

    @override
    public <t> t getextension(class<t> type, string name) {
        if (type.isinterface() && type.isannotationpresent(spi.class)) {
            extensionloader<t> loader = extensionloader.getextensionloader(type);
            //getsupportedextensions方法用于获取extensionloader的cachedclasses缓存的keyset
            //即spi接口配置文件中name的set集合   
            if (!loader.getsupportedextensions().isempty()) {
                return loader.getadaptiveextension();
            }
        }
        return null;
    }

}

 

5.4.springextensionfactory

  顾名思义,这个factory应用在spring环境下,是dubbo与spring结合使用的extensionfactory。与spiextensionfactory的行为相反,它不支持@spi注解的接口。
public class springextensionfactory implements extensionfactory {
    private static final logger logger = loggerfactory.getlogger(springextensionfactory.class);

    private static final set<applicationcontext> contexts = new concurrenthashset<applicationcontext>();
    private static final applicationlistener shutdown_hook_listener = new shutdownhooklistener();

    public static void addapplicationcontext(applicationcontext context) {
        contexts.add(context);
        if (context instanceof configurableapplicationcontext) {
            ((configurableapplicationcontext) context).registershutdownhook();
            dubboshutdownhook.getdubboshutdownhook().unregister();
        }
        //先springcontext注册dubbo关闭钩子
        beanfactoryutils.addapplicationlistener(context, shutdown_hook_listener);
    }

    //省略了一些contexts的crud方法...

    @override
    @suppresswarnings("unchecked")
    public <t> t getextension(class<t> type, string name) {
        //不支持被@spi注解标注的接口类型
        if (type.isinterface() && type.isannotationpresent(spi.class)) {
            return null;
        }
        //首先根据name从spring上下文获取bean,并验证该bean与type是否一致
        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 ...");
        //如果type的类型为object,直接返回null
        if (object.class == type) {
            return null;
        }
        //尝试根据type从spring上下文获取bean,如果type对应的bean并非唯一,直接报错
        for (applicationcontext context : contexts) {
            try {
                return context.getbean(type);
            } catch (nouniquebeandefinitionexception multibeanexe) {
                logger.warn("find more than 1 spring extensions (beans) of type ...");
            } catch (nosuchbeandefinitionexception nobeanexe) {
                if (logger.isdebugenabled()) {
                    logger.debug("error when get spring extension(bean) for type: ...");
                }
            }
        }
        logger.warn("no spring extension (bean) named: ...");
        //未找到,返回null
        return null;
    }

    private static class shutdownhooklistener implements applicationlistener {
        @override
        public void onapplicationevent(applicationevent event) {
            if (event instanceof contextclosedevent) {
                dubboshutdownhook shutdownhook = dubboshutdownhook.getdubboshutdownhook();
                shutdownhook.dodestroy();
            }
        }
    }
}

   springextensionfactory维护了spring上下文集合“set<applicationcontext>”。在getextension方法中,通过spring上下文去获取实例。并且,springextensionfactory在设置springcontext时,会向获取到的context注册一个spring容器关闭事件的监听钩子,用于关闭dubbo。  

 

六、扩展点自动包装

  扩展点实例自动装配之后,便开始进行自动包装,包装的过程为遍历缓存“cachedwrapperclasses”并将装配好的扩展类实例作为包装扩展类的构造器参数,创建一个新的包装类实例,然后对这个新的实例进行自动装配。
  如何判定一个扩展类是否是包装类呢?在getextension方法的源码分析中已经有提及,就是某个spi接口的扩展类具有一个特定特征的构造函数,这个构造函数是单参数,并且参数类型是该扩展类实现的spi接口类型。举个官网给出的例子:
package com.alibaba.xxx;
 
import org.apache.dubbo.rpc.protocol;
 
public class xxxprotocolwrapper implements protocol {
    protocol impl;
 
    //单参数构造函数,且参数类型为其实现的spi接口类型protocol
    public xxxprotocolwrapper(protocol protocol) { impl = protocol; }
 
    // 接口方法做一个操作后,再调用extension的方法
    public void refer() {
        //... 一些操作
        impl.refer();
        // ... 一些操作
    }
}

  自动包装的机制能够近似的实现aop的功能,通过wrapper类可以把所有扩展点公共逻辑移至wrapper中,新加的wrapper在所有的扩展点上添加了逻辑。

 

七、扩展点自动激活

  还未完成。