Java SPI
Java SPI策略模式,具体实现步骤:
(1) 定义一个接口及对应的方法
(2) 编写接口的实现类、
(3) 在META-INF/services/目前下,创建一个以接口全路径命名的文件,如com.test.spi.PrintService
(4) 文件内容为具体实现类的全路径名,如果有多个,则用分隔符分离
(5) 在代码中通过java.util.ServiceLoader来加载具体实现类
代码
com.test.spi.PrintService
// SPI接口定义
public interface PrintService {
void printInfo();
}
SPI接口实现类
public class PrintServiceImpl implements PrintService {
@Override
public void printInfo() {
System.out.println("Hello world");
}
}
SPI具体实现机制
public static void main(String[] args) {
ServiceLoader<PrintService> serviceLoader = ServiceLoader.load(PrintService.class);
for(PrintService printService : serviceLoader) {
printService.printInfo();
}
}
Java SPI不总:
- JDK标准的SPI会一次性实例化扩展点所有的实现,如果有扩展点没有,也会加载,资料浪费
- 如果扩展点加载失败,会导致调用方报错,而且这个错误很难定位到是这个原因。如JDK标准的
ScriptEngine
,通过getName()
获取脚本名称,如果RubyScriptEngine
因为所依赖的jruby.jar
不存在,导致加载失败,当用户执行Ruby脚本,会报不支持Ruby,而不是真正失败的原因 - 没有对扩展IOC和AOP的支持
Dubbo SPI
- 扩展接口,需要@SPI标记
- 在
resouce
目录下配置META-INF/dubbo
或者META-INF/dubbo/internal
并基于SPI接口创建一个文件
Dubbo 针对的扩展点非常多,可以针对协议、拦截、集群、路由、负载均衡、序列化、容器… 几乎里面用到的所有功能,都可以实现自己的扩展
org.apache.dubbo.common.extension.ExtensionLoader
* @see org.apache.dubbo.common.extension.SPI
* @see org.apache.dubbo.common.extension.Adaptive
* @see org.apache.dubbo.common.extension.Activate
*/
public class ExtensionLoader<T> {
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
/** 全局缓存 扩展类和对应的扩展加载器缓存*/
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
/** 全局缓存 扩展类与类初始化后的实例*/
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
/****************加载spi的扩展类的实例对象**********************/
/**当前扩展点类*/
private final Class<?> type;
private final ExtensionFactory objectFactory;
/**扩展类与扩展名缓存*/
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
/**普通扩展类缓存,不包括自适应扩展类和Wrapper类*/
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
/**扩展名与@Activate的缓存*/
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
/** 扩展名与扩展实例对象缓存*/
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
/** 实例化后的自适应(Adaptive)扩展对象,只能同时存在一个*/
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
/**自适应扩展类缓存*/
private volatile Class<?> cachedAdaptiveClass = null;
/**默认扩展名 如@SPI("dubbo") 默认名dubbo*/
private String cachedDefaultName;
/** 存储自适应扩展点的异常错误*/
private volatile Throwable createAdaptiveInstanceError;
/**Wrapper类缓存*/
private Set<Class<?>> cachedWrapperClasses;
/** 存储加载异常*/
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
}
静态扩展点
LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("random");
System.out.println(loadBalance); // RandomLoadBalance
META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance
random=org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
roundrobin=org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
leastactive=org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
consistenthash=org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
getExtension("random")
-
- 读取SPI对应的路径下的配置文件,根据配置加载所有的的扩展类并缓存(不进行初始化)
-
- 根据传入的名称初始化对应的扩展类
- 3)尝试查找符合条的的包装类: 扩展点的Setter方法 如
setProtocol(Protocol protocol)
方法会自动注入protocol的扩展点实现, 加载包装了Wrapper会加载(ProtocolFilterWrapper(protocol)
-
- 返回对应的实例
ExtensionLoader#getExtensionLoader 获取扩展类加载器实例
// 构造函数 实例化
private ExtensionLoader(Class<?> type) {
this.type = type;
// 如果当前的 type=ExtensionFactory,type,那么 objectFactory=null, 否则会创建一个自适应扩展点给到 objectFacotry
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// 初始化ExtensionLoader
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
getExtension(name)根据实例获取扩展实例
public T getExtension(String name) {
...
// 获取默认的扩展点 如 @SPI(RandomLoadBalance.NAME) random
if ("true".equals(name)) {
return getDefaultExtension();
}
final Holder<Object> holder = getOrCreateHolder(name);
// 缓存一下,如果实例已经加载过了,直接从缓存中读取
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 根据名称创建实例
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
createExtension根据名称创建扩展点
private T createExtension(String name) {
// 获取 加载指定路径下 的扩展点文件 的扩展类
// getExtensionClasses 加载指定路径下的所有文件
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) { // 没有找到,则抛出异常
throw findException(name);
}
try {
// 用chm保存实例,做缓存
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 (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 (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
getExtensionClasses
这个方法会查找指定目录META-INF/dubbo||META-INF/services下对应的type,加载定位扩展点,定位加载
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 加载过程
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
/**
* synchronized in getExtensionClasses
* 加载配置文件过程
* */
private Map<String, Class<?>> loadExtensionClasses() {
// 缓存默认实现 如@SPI("random") ->random
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// 加载路径下面的SPI配置文件
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
..
return extensionClasses;
}
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
// 通过getResouces或getSystemResources得到配置文件
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
// 循环遍历urls,解析字符串,得到扩展实现类,并加入缓存
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: " +
type + ", description file: " + fileName + ").", t);
}
}
Set注入
dubbo的SPI 实现了通过Set方法注入,需要满足一下条件:
- 当前扩展点中依赖其他扩展点
- set方法进行依赖注入
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("registry");
System.out.println(protocol);
Protocol配置文件
filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
rmi=org.apache.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=org.apache.dubbo.rpc.protocol.hessian.HessianProtocol
http-invoker=org.apache.dubbo.rpc.protocol.httpinvoker.HttpInvokerProtocol
org.apache.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=org.apache.dubbo.rpc.protocol.thrift.ThriftProtocol
native-thrift=org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocol
memcached=org.apache.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=org.apache.dubbo.rpc.protocol.redis.RedisProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
xmlrpc=org.apache.dubbo.xml.rpc.protocol.xmlrpc.XmlRpcProtocol
registry=org.apache.dubbo.registry.integration.RegistryProtocol
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
public class RegistryProtocol implements Protocol {
private Cluster cluster;
private Protocol protocol;
...
public void setCluster(Cluster cluster) {
this.cluster = cluster;
}
...
public void setProtocol(Protocol protocol) { // 会注入Protocol$Adapive
this.protocol = protocol;
}
}
RegistryProtocol
中
set注入源码
org.apache.dubbo.common.extension.ExtensionLoader#createExtension
调用injectExtension
方法
private T injectExtension(T instance) {
// 这个不能为空,否则下一步不能获取注入的扩展点对象
if (objectFactory == null) {
return instance;
}
try {
// 获取实例对应的方法
for (Method method : instance.getClass().getMethods()) {
// 判断是否是一个set方法
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
// 可以选择禁用依赖注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 获取的方法的参数,这个参数必须是一个对象类型并且是一个扩展点
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 获取set方法中属性名字,根据属性名字进行加载
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 调用set方法进行赋值
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
org.apache.dubbo.common.extension.factory.SpiExtensionFactory#getExtension
// 获取扩展点对象
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
// 根据类型获取扩展点加载类
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
// 获取自适应扩展类 Protocol$Adaptive
return loader.getAdaptiveExtension();
}
}
return null;
}
Wrapper包装类
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
System.out.println(protocol);// QosProtocolWrapper(ProtocolListenerWrapper(ProtocolFilterWrapper(DubboProtocol))
缓存包装类
org.apache.dubbo.common.extension.ExtensionLoader#loadClass
调用cacheWrapperClass
将相应的包装类缓存到ExtensionLoader
中的成员变量cachedWrapperClasses
中
// org.apache.dubbo.common.extension.ExtensionLoader#loadClass
// 以Protocol为例,这里加载配置org.apache.dubbo.rpc.protocol中含有Wrapper结尾的包装类
// 缓存 ProtocolFilterWrapper,ProtocolListenerWrapper,QosProtocolWrapper
else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
}
..
private void cacheWrapperClass(Class<?> clazz) {
if (cachedWrapperClasses == null) {
cachedWrapperClasses = new ConcurrentHashSet<>();
}
cachedWrapperClasses.add(clazz);
}
创建扩展类是增强
org.apache.dubbo.common.extension.ExtensionLoader#createExtension
// 以protocol为类,这里 WrapperClassess 为 ProtocolFilterWrapper,ProtocolListenerWrapper,QosProtocolWrapper
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
// wrapperClass.getConstructor 调用扩展类的构造方法进行增强
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
最终结果QosProtocolWrapper(ProtocolListenerWrapper(ProtocolFilterWrapper(DubboProtocol)
注意: wrapper类的包装顺序不是以配置文件的中先后为绝对属性,因为存储包装类的容器是
ConcurrentHashSet
,ConcurrentHashSet
又是以new ConcurrentHashMap<E, Object>()
的key实现的。
包装类是某一方面的功能功能增强,无所谓顺序,不想filter
Adaptive 自适应扩展点
Compiler compiler=ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
System.out.println(compiler.getClass());
AdaptiveCompiler 这个类有一个注解@Adaptive,这个就是一个自适应扩展点的标识。它可以修饰在类上,也可以修饰在方法上面。
放在类上,说明当前类是一个确定的自适应扩展点的类。如果是放在方法级别,那么需要生成一个动态字节码,来进行转发
修饰类上
修饰类上:
org.apache.dubbo.common.compiler.support.AdaptiveCompiler
@Override
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
// 最终还是如静态扩展点getExtension("jdk")
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory
objectFactory
在获得 ExtensionLoader 的时候,就对 objectFactory 进行了初始化
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
然后通过 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()去获得一个自适应的扩展点,进
入 ExtensionFactory 这个接口中,可以看到它是一个扩展点,并且有一个自己实现的自适应扩展点 AdaptiveExtensionFactory;
除了自定义的自适应适配器类以外,还有两个实现类,一个是 SPI,一个是 Spring,AdaptiveExtensionFactory
AdaptiveExtensionFactory 轮询这 2 个,从一个中获取到就返回
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
修饰在方法
修饰在方法上,需要在动态字节码
如
@SPI("dubbo")
public interface Protocol {
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
}
基于方法层面的@Adaptive,基本实现原理的图
getAdaptiveExtension
这个方法主要就是要根据传入的接口返回一个自适应的实现类
public T getAdaptiveExtension() {
//cacheAdaptiveInstance,
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建一个自适应扩展点的实现
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
创建一个自适应扩展点的实现
这个方法中做两个事情
- 获得一个自适应扩展点实例
- 实现依赖注入
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 这里是动态生成代理类 如 Protocol$Adaptive
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
Protocol$Adaptive
动态生成的代理类,以下是我通过 debug 拿到的代理类
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
// 最终还是回到调用静态扩展点
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
Activate激活扩展点
自动激活扩展点,有点类似 springboot 的时候用到的 conditional,根据条件进行自动激活。但是这里设计的初衷是,对
于一个类会加载多个扩展点的实现,这个时候可以通过自动激活扩展点进行动态加载, 从而简化配置我们的配置工作
@Activate 提供了一些配置来允许我们配置加载条件,比如 group 过滤,比如 key 过滤
举个例子,我们可以看看 org.apache.dubbo.Filter 这个类,它有非常多的实现,比如说 CacheFilter,这个缓存过滤器,配置信息
如下
cache=org.apache.dubbo.cache.filter.CacheFilter
validation=org.apache.dubbo.validation.filter.ValidationFilter
echo=org.apache.dubbo.rpc.filter.EchoFilter
generic=org.apache.dubbo.rpc.filter.GenericFilter
genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
token=org.apache.dubbo.rpc.filter.TokenFilter
accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter
context=org.apache.dubbo.rpc.filter.ContextFilter
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
exception=org.apache.dubbo.rpc.filter.ExceptionFilter
executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter
trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
monitor=org.apache.dubbo.monitor.support.MonitorFilter
metrics=org.apache.dubbo.monitor.dubbo.MetricsFilter
ExtensionLoader<Filter> loader=ExtensionLoader.getExtensionLoader(Filter.class);
URL url=new URL("","",0);
// url=url.addParameter("cache","cache"); 有和没有的区别
List<Filter> filters=loader.getActivateExtension(url,"cache");
System.out.println(filters.size());
group 表示客户端和和服务端都会加载,value 表示 url 中有 cache_key 的时候
@Activate(group = {CONSUMER, PROVIDER}, value = CACHE_KEY)
public class CacheFilter implements Filter {
...
}