第一章 Dubbo风格的SPI-ExtensionLoader
来源:互联网 发布:知豆汽车能开几年 编辑:程序博客网 时间:2024/04/30 01:21
java原生SPI
1、原生SPI的使用方式
1、路径 META-INF/services
2、文件名 interface 完整限定名
3、文件内容 implement 完整限定名
4、使用方式,通过迭代器迭代实现类型。
2、原生API的优缺点。
优点1、不需要引入第三方包,util包中既有api使用,实现方便,功能简单。
缺点2、不能指定想要的实现类,需要迭代获取;迭代的时候会加载用不到的实现类型,对资源造成浪费。
2 Motan ExtensionLoader实现服务发现。
Motan SPI的使用方式
1、路径 META-INF/services/
2、文件名@Spi注解的接口的完整限定名。
3、文件内容 每行一个实现类型。可以使用 #注释(注释内容在#右侧)
4、使用方式,通过name获取,或者通过关键字过滤获取拓展点集合
3 Dubbo ExtensionLoader实现服务发现。
Dubbo SPI的使用方式
1、路径 META-INF/services/;META-INF/dubbo/;META-INF/dubbo/internal/。
2、文件名@SPI注解的接口的完整限定名。
3、文件内容,name=implemts(完整限定名)。
4、使用方式,通过名称获取实现,或者通过URL获取可激活实现类型
4 Dubbo服务发现的特性。
1默认实现
通过SPI注解的value值来设置该接口所拥有的默认实现
public @interface SPI { /** * 缺省扩展点名。 */ String value() default "";}
2扩展点实例属性自动注入
在创建拓展点实例后会自动注入实例中所包含的属性通过ExtensionFactory注入拓展点实现。
private T injectExtension(T instance) { try { for (Method method : instance.getClass().getMethods()) { /** *如果是set方法,设置值到实例中 **/ Object object = objectFactory.getExtension(pt, property); if (object != null) { method.invoke(instance, object); } } return instance; }
3 Adaptive拓展点,通过Adaptive注解标识一个类型为适配类型,或者通过Compiler的Adaptive实现(通过application标签的Compiler属性可以设置为jdk或者javasist)动态生成Adaptive类型
1、具体生成规则。
①、针对具有Adaptive注解的方法生成方法,如果没有Adaptive注解,该方法跑出运行时异常
②、根据URL属性,Adaptive注解方法中必须有URL参数,或者带URL属性的参数。或者Invocation属性获取拓展点名称(获取属性)
③、通过ExtensionLoader根据名称获取拓展点实例,然后调用方法。
④、将拼成的code生成CLass并返回。
private String createAdaptiveExtensionClassCode() { /** *省略部分 **/ //遍历接口中的方法,检查是否存在Adaptive注解的方法 for(Method m : methods) { if(m.isAnnotationPresent(Adaptive.class)) { hasAdaptiveAnnotation = true; break; } } // 完全没有Adaptive方法,则不需要生成Adaptive类 if(! hasAdaptiveAnnotation) throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!"); /** *准备生成类型,生成,包名,引用,类型名(interfaceName+$+Adaptive),实现接口 **/ /** *遍历方法,根据Adaptive注解决定生成方式等。 **/ for (Method method : methods) { Class<?> rt = method.getReturnType(); Class<?>[] pts = method.getParameterTypes(); Class<?>[] ets = method.getExceptionTypes(); Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); StringBuilder code = new StringBuilder(512); //没有注解,那这个方法有问题,不清真,没法用Adaptive方式调用 if (adaptiveAnnotation == null) { code.append("throw new UnsupportedOperationException(\"method ") .append(method.toString()).append(" of interface ") .append(type.getName()).append(" is not adaptive method!\");"); } else { int urlTypeIndex = -1; for (int i = 0; i < pts.length; ++i) { if (pts[i].equals(URL.class)) { urlTypeIndex = i; break; } } // 有类型为URL的参数 if (urlTypeIndex != -1) { // Null Point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");", urlTypeIndex); code.append(s); s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex); code.append(s); } // 参数没有URL类型,那从参数的属性中找,找不到就别玩了,抛异常,这个类型不清真,不能生成Adaptive类型 else { String attribMethod = null; // 找到参数的URL属性 LBL_PTS: for (int i = 0; i < pts.length; ++i) { Method[] ms = pts[i].getMethods(); for (Method m : ms) { String name = m.getName(); if ((name.startsWith("get") || name.length() > 3) && Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()) && m.getParameterTypes().length == 0 && m.getReturnType() == URL.class) { urlTypeIndex = i; attribMethod = name; break LBL_PTS; } } } if(attribMethod == null) { throw new IllegalStateException("fail to create adative class for interface " + type.getName() + ": not found url parameter or url attribute in parameters of method " + method.getName()); } // Null point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");", urlTypeIndex, pts[urlTypeIndex].getName()); code.append(s); s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");", urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod); code.append(s); s = String.format("%s url = arg%d.%s();",URL.class.getName(), urlTypeIndex, attribMethod); code.append(s); } /** * 获取注解的值 */ String[] value = adaptiveAnnotation.value(); // 没有设置Key,则使用拓展点的名字 if(value.length == 0) { char[] charArray = type.getSimpleName().toCharArray(); StringBuilder sb = new StringBuilder(128); for (int i = 0; i < charArray.length; i++) { if(Character.isUpperCase(charArray[i])) { if(i != 0) { sb.append("."); } sb.append(Character.toLowerCase(charArray[i])); } else { sb.append(charArray[i]); } } value = new String[] {sb.toString()}; } //如果有Invocation参数,那么从方法参数中去获取扩展点名称 boolean hasInvocation = false; for (int i = 0; i < pts.length; ++i) { if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) { // Null Point check String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i); code.append(s); s = String.format("\nString methodName = arg%d.getMethodName();", i); code.append(s); hasInvocation = true; break; } } //从方法参数中获取扩展点名称,或者从URL参数中获取拓展点名称,如果是协议拓展,那么从url中获取协议即可 String defaultExtName = cachedDefaultName; String getNameCode = null; for (int i = value.length - 1; i >= 0; --i) { if(i == value.length - 1) { if(null != defaultExtName) { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName); } else { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\")", value[i]); else getNameCode = "url.getProtocol()"; } } else { if(!"protocol".equals(value[i])) if (hasInvocation) getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); else getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode); else getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode); } } code.append("\nString extName = ").append(getNameCode).append(";"); // check extName == null? String s = String.format("\nif(extName == null) " + "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");", type.getName(), Arrays.toString(value)); code.append(s); s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);", type.getName(), ExtensionLoader.class.getSimpleName(), type.getName()); code.append(s); // return statement if (!rt.equals(void.class)) { code.append("\nreturn "); } s = String.format("extension.%s(", method.getName()); code.append(s); for (int i = 0; i < pts.length; i++) { if (i != 0) code.append(", "); code.append("arg").append(i); } code.append(");"); } //实现接口中定义的方法,将上面拼好的方法放进去。 codeBuidler.append("\npublic " + rt.getCanonicalName() + " " + method.getName() + "("); for (int i = 0; i < pts.length; i ++) { if (i > 0) { codeBuidler.append(", "); } codeBuidler.append(pts[i].getCanonicalName()); codeBuidler.append(" "); codeBuidler.append("arg" + i); } codeBuidler.append(")"); if (ets.length > 0) { codeBuidler.append(" throws "); for (int i = 0; i < ets.length; i ++) { if (i > 0) { codeBuidler.append(", "); } codeBuidler.append(ets[i].getCanonicalName()); } } codeBuidler.append(" {"); codeBuidler.append(code.toString()); codeBuidler.append("\n}"); } codeBuidler.append("\n}"); if (logger.isDebugEnabled()) { logger.debug(codeBuidler.toString()); } return codeBuidler.toString(); }
4 Activate拓展点,通过Activate注解标识一个类型为可激活类型,
public List getActivateExtension(URL url, String[] values, String group) {
List exts = new ArrayList();
List names = values == null ? new ArrayList(0) : Arrays.asList(values);
if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry
wrapper类型增强扩展点类型
1.wapper类型判断条件
一、在SPI接口的拓展点中,如果存在以接口类型作为唯一参数的拓展点类型,那就是一个拓展点类型
//如果有以接口类型作为入参的构造,那这就是个wrapper try { clazz.getConstructor(type); Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); }
二、warpper类型被载入的时机
在创建Extension实例的时候,将wrapper类型包装在实例之外。以达到功能增强目的。
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); } 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); } }
5 三种SPI的比较
2 0
- 第一章 Dubbo风格的SPI-ExtensionLoader
- dubbo spi extensionloader 插件化
- Dubbo源码 SPI实现ExtensionLoader
- 怎么将dubbo的SPI移植到开源项目中,附dubbo的ExtensionLoader<T> 的实现原理分析
- Dubbo ExtensionLoader分析
- Dubbo ExtensionLoader源码解读
- 深入dubbo之ExtensionLoader,灵活的扩展点加载机制
- Dubbo的SPI实现
- Dubbo源码阅读之 ExtensionLoader
- dubbo 1 ExtensionLoader 插件化
- [Dubbo源码剖析]ExtensionLoader机制
- 构造函数线程安全-即dubbo的extensionLoader存在线程安全问题
- Dubbo源码分析(五)ExtensionLoader
- Dubbo 源码解析系列一 ExtensionLoader
- 从ExtensionLoader看Dubbo插件化
- 从ExtensionLoader看Dubbo插件化
- Dubbo系列-3.扩展核心ExtensionLoader
- Dubbo-SPI
- 段寄存器
- Struts2+Spring+Hibernate 框架在IntelliJ的集成
- 问题 A: 第几天(简单模拟)
- 对象模型 ------- Constructor 构造函数
- poj 1408 Fishnet
- 第一章 Dubbo风格的SPI-ExtensionLoader
- 3D游戏引擎系列一
- nyoj1057寻找最大数3
- Visual Studio Code 配置指南
- HDU3739 Anti LIS 最大流Dinic
- 蓝桥杯- 基础练习 闰年判断
- 学会用好 Visual Studio Code
- the output path is not specified for module
- margin层叠情况,及表现