dubbo框架的微内核+插件的机制,其中插件机制就是依赖ExtensionLoader来实现,可以说是dubbo的核心所在。通过插件机制解耦依赖来实现框架设计原则中的针对接口编程而不针对实现。
熟悉ExtensionLoader就熟悉了Dubbo的扩展机制。
JDK的标准SPI对比dubbo的SPI
JDK的SPI,新建接口,然后定义不同实现,然后/META-INF/services定义定义接口的全路径的文件,文件中写上接口的全部实现类,最后代码通过ServiceLoader加载,循环迭代得到所有实现类。标准SPI迭代时会加载所有实现,所以只希望加载某个的,就不现实了。
Dubbo的扩展SPI:
1. 单例,对于某个扩展,只会有一个ExtensionLoader;
2. 延迟加载,可以一次只获取想要的扩展点,一次获取想要的扩展点实现;
3. 对于扩展点的Ioc和Aop,就是一个扩展可以注入到另一个扩展中,也可以对一个扩展做wrap包装实现aop的功能;
4. 对于扩展点的调用,真正调用的时候才能确认具体使用的是那个实现。
源码解析
所有扩展必须加上SPI注解才能通过ExtensionLoader加载。对于一个扩展点只会加载一次,生成一个ExtensionLoader。通过ExtensionLoader.getExtensionLoader(class type)传入一个spi扩展返回一个ExtensionLoader实例。
/** 获取扩展点的ExtensionLoader,需要这里调用的时候需要自己确保线程安全,不过一般也不会有问题 */
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if(!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
//必须加SPI注解
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
//新建个ExtensionLoader返回
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
private ExtensionLoader(Class<?> type) {
this.type = type;
//也是利用扩展实现,后面会看到在注入其他扩展点或bean到当前扩展时使用
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
看下ExtensionLoader常用方法:
getAdaptiveExtension
Dubbo中的扩展点均有多个实现,而框架设计原则又让我们针对接口编程而不是实现,这就需要在运行期才能决定具体使用哪个扩展实现类。Dubbo提供了Adpative注解,让我们自行决定究竟是自己提供扩展的适配还是由Dubbo来帮我们生成动态适配。后面会给出2个自定义扩展实现Demo,一个是我们自定义适配,另一个是Dubbo提供动态适配。
常用方式:ExtensionLoader.getExtensionLoader(扩展点class).getAdaptiveExtension()
- getAdaptiveExtension
/** 获取扩展的适配器类 */
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
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("fail to create adaptive instance: " + t.toString(), t);
}
}
}
}
else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
- createAdaptiveExtension
/** 创建Adpative */
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
2步走:
1. getAdaptiveExtensionClass:获取适配器类;
2. injectExtension:为适配器类的setter方法插入其他扩展点或实现bean。
getAdaptiveExtensionClass:
/** 获取适配器类 */
private Class<?> getAdaptiveExtensionClass() {
//加载所有扩展点实现类
getExtensionClasses();
//如果用户有自定义适配器实现类,return
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//dubbo帮我们动态创建一个适配器类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses
/** 加载扩展类 */
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;
}
/** 从不同目录加载扩展实现 */
private Map<String, Class<?>> loadExtensionClasses() {
//Dubbo的扩展接口必须实现SPI注解
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
//这里是解析SPI注解,看是否有默认实现,后期Dubbo获取扩展的实现,如果获取不到就会有使用这个默认实现
if(defaultAnnotation != null) {
String value = defaultAnnotation.value();
if(value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if(names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if(names.length == 1) cachedDefaultName = names[0];
}
}
//从3个目录加载
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);//META-INF/dubbo/internal/
loadFile(extensionClasses, DUBBO_DIRECTORY);//META-INF/dubbo/
loadFile(extensionClasses, SERVICES_DIRECTORY);//META-INF/services/
return extensionClasses;
}
loadFile从目录加载扩展实现
/** 从目录加载扩展实现 */
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
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;
int i = line.indexOf('=');
if (i > 0) {
//文件采用properties方式的配置,name=实现类的全路径
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
Class<?> clazz = Class.forName(line, true, classLoader);
//必须是该扩展点的实现
if (! type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
//这里判断是否有自定义的适配器类,如果有,后面获取适配器的时候,就可以直接用这个创建返回,不用dubbo动态创建
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
try {
//这里处理的是,实现类的包裹类,必须是构造器注入扩展点,这里后期获取具体扩展点的实现类时,会使用包裹类封装下
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//处理不是包裹类的情况
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//判断下Activate注解,后面讲,这里只需要知道,会缓存Activate注解的实现
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
//缓存扩展实现
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + url + ") in " + url, t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
createAdaptiveExtensionClass
/** Dubbo生成适配类 */
private Class<?> createAdaptiveExtensionClass() {
//动态生成适配器代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//动态编译生成适配器类
return compiler.compile(code, classLoader);
}
createAdaptiveExtensionClassCode方法生成扩展接口的适配:
1. 解析Adpative注解,从url或扩展接口获取扩展接口实现类的名称;
2. 根据名称,获取实现类ExtensionLoader.getExtensionLoader(扩展接口类).getExtension(扩展接口实现类名称)
,然后调用实现类的方法。
createAdaptiveExtensionClassCode生成适配器代码,可以自己debug看看。主要是处理Adpative注解在方法上的方式,(需要明白一点dubbo的内部传参基本上都是基于Url来实现的,例如注册服务的地址,订阅服务的地址啊什么的都是一串url):
适配器类的目的是在运行期获取扩展的真正实现来调用,解耦接口和实现,这样的话要不我们自己实现适配器类,要不dubbo帮我们生成,而这些都是通过Adpative来实现。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
/**
* 从{@link URL}的Key名,对应的Value作为要Adapt成的Extension名。
* <p>
* 如果{@link URL}这些Key都没有Value,使用 用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>
* 比如,<code>String[] {"key1", "key2"}</code>,表示
* <ol>
* <li>先在URL上找key1的Value作为要Adapt成的Extension名;
* <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
* <li>key2没有Value,使用缺省的扩展。
* <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
* </ol>
* <p>
* 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
* 即对于Extension接口{@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}的缺省值为<code>String[] {"yyy.invoker.wrapper"}</code>
*
* @see SPI#value()
*/
String[] value() default {};
}
使用方式:
1. 注解放在扩展接口实现类上,表示这个是自定义的适配器类,由这个类决定运行期返回哪种扩展实现类,这种最简单;
2. 注解放在扩展接口的方法上,这种只能dubbo帮我们生成适配器类了:
- 扩展接口SPI注解无默认值,Adaptive如果注解有value值:例如一个方法上的Adaptive注解是这样
@Adaptive({'p1','p2'}
生成代码中获取扩展名称的代码就是这样url.getParameter("p1", url.getParameter("p2"))
,加入你SPI注解有默认值的话@SPI("PP")
,那么会变成url.getParameter("p1", url.getParameter("p2", "PP"))
,多了个默认值的; - 还有个情况那就是Adaptive放在方法上,没有值,这时候createAdaptiveExtensionClassCode的时候就会解析你扩展接口的名称,处理了一个特例就是protocol协议,如果扩展接口是protocol,会使用url.getprotocol,其他情况会从url的参数里面获取;
这里给出2个适配生成的代码,体会下。
- protocol协议
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
生成的代码:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
}
- Validation
@SPI("jvalidation")
public interface Validation {
@Adaptive(Constants.VALIDATION_KEY)
Validator getValidator(URL url);
}
生成的代码:
package com.alibaba.dubbo.validation;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Validation$Adpative implements com.alibaba.dubbo.validation.Validation {
public com.alibaba.dubbo.validation.Validator getValidator(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("validation", "jvalidation");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.validation.Validation) name from url(" + url.toString() + ") use keys([validation])");
com.alibaba.dubbo.validation.Validation extension = (com.alibaba.dubbo.validation.Validation)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.validation.Validation.class).getExtension(extName);
return extension.getValidator(arg0);
}
}
injectExtension
/** 插入该扩展需要其他扩展或bean */
private T injectExtension(T instance) {
try {
//objectFactory也是一个扩展点,在new ExtensionLoader的时候创建
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
//必须是set方法注入
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) : "";
//objectFactory获取扩展点或bean注入到当前扩展点
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;
}
这里的objectFactory.getExtension(pt, property)
,objectFactory(ExtensionFactory)在new ExtensionLoader时会创建
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
看下ExtensionFactory这个扩展点。
注解Adpative加载AdaptiveExtensionFactory类上表示这是个自定义的适配器类,有2个实现类,一个是Spi(从dubbo 的扩展中获取),一个spring(获取spring的bean),AdaptiveExtensionFactory轮询这2个,从一个中获取到就返回。
getActivateExtension
getActivateExtension方法基本都会传入url作为参数,用法是获取激活条件的所有扩展点实现类。例如:获取当前消费端所有的filter。
这里用到了另一个注解Activate
:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Group过滤条件。
* <br />
* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
* <br />
* 如没有Group设置,则不过滤。
*/
String[] group() default {};
/**
* Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
* <p />
* 示例:<br/>
* 注解的值 <code>@Activate("cache,validatioin")</code>,
* 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
* <br/>
* 如没有设置,则不过滤。
*/
String[] value() default {};
/**
* 排序信息,可以不提供。
*/
String[] before() default {};
/**
* 排序信息,可以不提供。
*/
String[] after() default {};
/**
* 排序信息,可以不提供。
*/
int order() default 0;
}
group基本表示用在服务端还是消费端,value表示激活这个扩展点的条件,before、after、order用于排序。
getActivateExtension方法:
//获取满足激活条件的扩展实现
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
//加载扩展
getExtensionClasses();
//上一步会缓存所有Active注解的实现类到cachedActivates
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
//匹配group,provider还是consumer
if (isMatchGroup(group, activate.group())) {
//获取扩展实现类
T ext = getExtension(name);
//isActive匹配激活条件
if (! names.contains(name)
&& ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
exts.add(ext);
}
}
}
//这里会重新排序,用到Active里面before、after和order
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
....
....
return exts;
}
isMatchGroup:
/** group匹配,provider还是consumer端 */
private boolean isMatchGroup(String group, String[] groups) {
//group没有,那就是不管Active是provider还是consumer端的都可以
if (group == null || group.length() == 0) {
return true;
}
//Active的注解group值,必须配置,不配置,那就都不能使用,配置后匹配
if (groups != null && groups.length > 0) {
for (String g : groups) {
if (group.equals(g)) {
return true;
}
}
}
return false;
}
isActive:
/** Activate激活条件匹配 */
private boolean isActive(Activate activate, URL url) {
String[] keys = activate.value();
//如果Active注解的value为null,那就表示不需要条件都可以使用
if (keys == null || keys.length == 0) {
return true;
}
//value不为null,那就跟url里面的参数匹配下
for (String key : keys) {
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ConfigUtils.isNotEmpty(v)) {
return true;
}
}
}
return false;
}
getActivateExtension大体还算清楚,有的地方有些啰嗦,不确认那些场景用到了。
getExtension
生成的适配器类和getActiveExtension里面都有getExtension方法,用于获取扩展的实现类。
/** 根据名称获取扩展 */
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//如果为名称为true,则获取默认
if ("true".equals(name)) {
return getDefaultExtension();
}
//实例会缓存,所以先从缓存取
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(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) {
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, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//注入其他扩展点或bean
injectExtension(instance);
//包裹类封装
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
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);
}
}
2种自定义扩展实现
在上一个dmeo的新建spi module。
Dubbo提供Adpative动态适配
扩展点1,DirExtension.java
@SPI("dirDefault")
public interface DirExtension {
@Adaptive({SpiConstants.DIR_DEFAULT, SpiConstants.DIR_FIRST})
String sayHello(URL url, String name);
}
2个扩展点实现
DirExtensionDefaultImpl.java
public class DirExtensionDefaultImpl implements DirExtension {
@Override
public String sayHello(URL url, String name) {
return name + "-defualt";
}
}
DirExtensionFirstImpl.java
public class DirExtensionFirstImpl implements DirExtension {
@Override
public String sayHello(URL url, String name) {
return name + "-first";
}
}
常量类SpiConstants.java
public class SpiConstants {
public static final String DIR_DEFAULT = "dirDefault";
public static final String DIR_FIRST = "dirFirst";
public static final String DIR2_DEFAULT = "dir2Default";
public static final String DIR2_FIRST = "dir2First";
}
配置的扩展点com.qbb.spi.DirExtension
dirDefault=com.qbb.spi.DirExtensionDefaultImpl
dirFirst=com.qbb.spi.DirExtensionFirstImpl
测试类DirExtensionTest.java:
public class DirExtensionTest {
public static void main(String[] args){
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(DirExtension.class);
//DirExtension dirExtension = (DirExtension)extensionLoader.getExtension("dirFirst");
DirExtension dirExtension = (DirExtension)extensionLoader.getAdaptiveExtension();
Map<String, String> parameters = new HashMap<String, String>();
//parameters.put("dirFirst", "dirFirst");
parameters.put("dirDefault", "dirDefault");
URL url = new URL("dubbo", "127.0.0.1", 8089, parameters);
System.out.println(dirExtension.sayHello(url, "qbb"));
}
}
自定义Adpative适配
扩展点2,Dir2Extension.java
@SPI("dir2Default")
public interface Dir2Extension {
String sayHello(String name, String type);
}
2个扩展点2的实现
Dir2ExtensionDefaultImpl.java
public class Dir2ExtensionDefaultImpl implements Dir2Extension {
@Override
public String sayHello(String name, String type) {
return "dir2Extension-" + name + "-default";
}
}
Dir2ExtensionFirstImpl.java
public class Dir2ExtensionFirstImpl implements Dir2Extension {
@Override
public String sayHello(String name, String type) {
return "dir2Extension-" + name + "-first";
}
}
自定义Adpative
Dir2ExtensionAdaptive.java
@Adaptive
public class Dir2ExtensionAdaptive implements Dir2Extension{
@Override
public String sayHello(String name, String type) {
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Dir2Extension.class);
Dir2Extension dir2Extension = (Dir2Extension)extensionLoader.getDefaultExtension();
if (type != null && SpiConstants.DIR2_FIRST.equalsIgnoreCase(type.trim())) {
dir2Extension = (Dir2ExtensionDefaultImpl)extensionLoader.getExtension(SpiConstants.DIR2_FIRST);
}
return dir2Extension.sayHello(name, type);
}
}
扩展点2的配置
adaptive=com.qbb.spi2.Dir2ExtensionAdaptive
dir2Default=com.qbb.spi2.Dir2ExtensionDefaultImpl
dir2First=com.qbb.spi2.Dir2ExtensionFirstImpl
测试类
Dir2ExtensionTest.java
public class Dir2ExtensionTest {
public static void main(String[] args){
ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Dir2Extension.class);
Dir2Extension dirExtension = (Dir2Extension)extensionLoader.getAdaptiveExtension();
String type = null;
//type = SpiConstants.DIR2_DEFAULT;
//type = SpiConstants.DIR2_FIRST;
System.out.println(dirExtension.sayHello("qbb2", type));
}
}