1. Java SPI
-
代码已经上传至 https://github.com/masteryourself/study-dubbo.git ,分支是
master
,工程是dubbo-extension/java-spi
-
根据接口和 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
- 代码已经上传至 https://github.com/masteryourself/study-dubbo.git ,分支是
master
,工程是dubbo-extension/dubbo-spi
2.1 @SPI
- 修饰在接口上,注解的值代表的该接口默认的扩展点名
2.2 @Activate
-
表示实现类是否可以被激活。通常被用在一个接口有很多实现类,但是这些实现类在特定条件下才能使用。比如
RouterFactory
接口,这是路由工厂接口,而它的实现类有TagRouterFactory
、ConditionRouterFactory
等等 -
在调用的时候,需要根据配置的一些信息来决定需不需加载,而
@Activate
就提供了这个功能,修饰了这个注解的类,如果注解上没有任何值,那么表示无条件自动激活,当 vaue 有值,表示 URL 的参数为有效值时激活,比如配了cache="lru"
,自动激活CacheFilter
,如果group= provider
,表示只对提供方激活,group
可选provider
或consumer
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 之自动注入
- 代码已经上传至 https://github.com/masteryourself/study-dubbo.git ,分支是
master
,工程是dubbo-extension/dubbo-auto_set
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);
}
}
}
}