03.Dubbo 应用之扩展机制

1. Java SPI

1.1 配置文件
1. META-INF/services/pers.masteryourself.study.java.spi.Car
pers.masteryourself.study.java.spi.BlackCar
pers.masteryourself.study.java.spi.RedCar
1.2 代码
1. SpiTest
public class SpiTest {

    @Test
    public void test() {
        ServiceLoader<Car> serviceLoader = ServiceLoader.load(Car.class);
        Iterator<Car> iterator = serviceLoader.iterator();
        while (iterator.hasNext()) {
            Car car = iterator.next();
            // black
            // red
            System.out.println(car.getCarInfo());
        }
    }
}

2. Dubbo SPI

2.1 @SPI
  • 修饰在接口上,注解的值代表的该接口默认的扩展点名
2.2 @Activate
  • 表示实现类是否可以被激活。通常被用在一个接口有很多实现类,但是这些实现类在特定条件下才能使用。比如 RouterFactory 接口,这是路由工厂接口,而它的实现类有 TagRouterFactoryConditionRouterFactory 等等

  • 在调用的时候,需要根据配置的一些信息来决定需不需加载,而 @Activate 就提供了这个功能,修饰了这个注解的类,如果注解上没有任何值,那么表示无条件自动激活,当 vaue 有值,表示 URL 的参数为有效值时激活,比如配了 cache="lru",自动激活 CacheFilter,如果 group= provider,表示只对提供方激活, group 可选 providerconsumer

2.3 配置文件
1. META-INF/services/org.apache.dubbo.rpc.Filter
timeSpendProvider=pers.masteryourself.study.dubbo.extension.spi.filter.TimeSpendProviderFilter
2.4 代码
1. TimeSpendProviderFilter
// 在 provider 端激活
@Activate(group = CommonConstants.PROVIDER_SIDE)
public class TimeSpendProviderFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long start = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long end = System.currentTimeMillis();
        System.out.println(invoker.getInterface() + " 接口耗时:" + (end - start) + "ms");
        return result;
    }

}

3. Dubbo 之自动包装

  • 代码已经上传至 https://github.com/masteryourself/study-dubbo.git ,分支是 master,工程是 dubbo-extension/dubbo-aop

  • 对于一个接口的实现,可以对它的外层进行包装,类似 AOP

  • wrapper 不需要定义 key,它是无序的,存储在 set 集合中

3.1 配置文件
1. META-INF/services/org.apache.dubbo.rpc.Protocol
## wrapper 一般省略名称,无序
pers.masteryourself.study.dubbo.extension.aop.protocol.RegistryProtocolWrapper
3.2 代码
public class RegistryProtocolWrapper implements Protocol {

    private static final String REGISTRY = "registry";

    private Protocol protocol;

    /**
     * 必须要有,这是判断是否是 wrapper 类的标识
     *
     * @param protocol
     */
    public RegistryProtocolWrapper(Protocol protocol) {
        this.protocol = protocol;
    }

    @Override
    public int getDefaultPort() {
        return this.protocol.getDefaultPort();
    }

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        Exporter<T> export = null;
        if (CommonConstants.DUBBO.equals(invoker.getUrl().getProtocol())) {
            System.out.println("注册 dubbo 服务之前,do something...");
            export = this.protocol.export(invoker);
            System.out.println("注册 dubbo 服务之后,do something...");
        }
        return export != null ? export : this.protocol.export(invoker);
    }

    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        return this.protocol.refer(type, url);
    }

    @Override
    public void destroy() {
        this.protocol.destroy();
    }
}

4. Dubbo 之自动注入

4.1 @Adaptive
  • 当修饰在类上时,表示该类为所实现接口的代理类的实现

  • 当修饰在接口的方法上,一般是没有人工的代理类实现,需要依赖 Dubbo 自动生成代理类,而这个代理类所对应的实例在调用某个方法时,如果这个方法被 @Adaptive 修饰了,则会从 URL 中取值作为相应的扩展点名去加载实现类并实例化,最后再使用这个实例调用对应的方法,@Adaptive 的值一般就是在这个场景中才有用,用来指定可以用 URL 中的哪个 key 可以获取到值

4.2 自动注入
  • 在注入时,会根据 setXxx 中的名字,先使用 Dubbo 默认的 SpiExtensionFactory 获取 set 方法参数类型的一个代理类,如果找不到,则去 spring 容器中查找对象
4.3 配置文件
1. META-INF/dubbo/org.apache.dubbo.rpc.Filter
autoSetTestFilter=pers.masteryourself.study.dubbo.extension.auto.set.filter.AutoSetTestFilter
2. META-INF/dubbo/pers.masteryourself.study.dubbo.extension.auto.set.dubbo.Subject
english=pers.masteryourself.study.dubbo.extension.auto.set.dubbo.EnglishSubject
math=pers.masteryourself.study.dubbo.extension.auto.set.dubbo.MathSubject
4.4 代码
1. AutoSetTestFilter
@Activate(group = CommonConstants.PROVIDER_SIDE)
public class AutoSetTestFilter implements Filter {

    private SpringBean springBean;

    private Subject subject;

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.println("spring bean 执行:" + this.springBean.doSomeThing());
        System.out.println("dubbo bean 执行:" + this.subject.getSubjectInfo(invoker.getUrl()));
        return invoker.invoke(invocation);
    }

    /**
     * 以 setXxx 开头的方法会自动注入,这里是注入 spring 中的 bean(SpringBean)
     *
     * @param springBean
     */
    public void setSpringBean(SpringBean springBean) {
        this.springBean = springBean;
    }

    /**
     * 以 setXxx 开头的方法会自动注入,这里是注入 dubbo 中的 bean(Subject$Adaptive)
     *
     * @param subject
     */
    public void setSubject(Subject subject) {
        this.subject = subject;
    }

}
2. Subject
// 默认使用 math
@SPI("math")
public interface Subject {

    /**
     * 会自动生成  Subject$Adaptive
     * 先调用      String value = url.getParameter("subject") 方法找出对应的值
     * 再调用      Subject subject = (Subject)ExtensionLoader.getExtensionLoader(Subject.class).getExtension(value);
     * 最后调用    subject.getSubjectInfo(url) 方法返回
     *
     * @param url
     * @return
     */
    @Adaptive("subject")
    String getSubjectInfo(URL url);

}
3. EnglishSubject
public class EnglishSubject implements Subject {

    @Override
    public String getInfo(URL url) {
        return "上英语课";
    }

}
4. MathSubject
public class MathSubject implements Subject {

    @Override
    public String getInfo(URL url) {
        return "上数学课";
    }

}
5. Subject$Adaptive
  • Dubbo 自动生成的代理类
public class Subject$Adaptive implements Subject {
    public Subject$Adaptive() {
    }

    public String getSubjectInfo(URL var1) {
        if (var1 == null) {
            throw new IllegalArgumentException("url == null");
        } else {
            String var3 = var1.getParameter("subject", "math");
            if (var3 == null) {
                throw new IllegalStateException("Failed to get extension (pers.masteryourself.study.dubbo.extension.auto.set.dubbo.Subject) name from url (" + var1.toString() + ") use keys([subject])");
            } else {
                Subject var4 = (Subject)ExtensionLoader.getExtensionLoader(Subject.class).getExtension(var3);
                return var4.getSubjectInfo(var1);
            }
        }
    }
}
发布了37 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/masteryourself/article/details/103759020