这篇文章主要讲解了“Dubbo的SPI机制介绍以及SPI加载class的方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Dubbo的SPI机制介绍以及SPI加载class的方法”吧!
@SPI public interface Robot { void sayHello(); } public class OptimusPrime implements Robot{ @Override public void sayHello() { System.out.println("Hello, I am Optimus Prime."); } } public class Bumblebee implements Robot{ @Override public void sayHello() { System.out.println("Hello, I am Bumblebee."); } }
public class DubboSPITest { @Test public void sayHelloDubbo() throws Exception { ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee"); bumblebee.sayHello(); } }
输出: Hello, I am Optimus Prime. Hello, I am Bumblebee.
ExtensionLoader<robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); 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!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } // 从缓存中获取与拓展类对应的ExtensionLoader ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { // 若缓存未命中,则创建一个新的实例,先简单看下new ExtensionLoader<T>(type) EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; }
这里的objectFactory创建可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入
private ExtensionLoader(Class<?> type) { this.type = type; // 这里的type是Robot.class,所以会执行后面的代码,加载并创建SpiExtensionFactory和SpringExtensionFactory, objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
public T getExtension(String name) { if ("true".equals(name)) { return getDefaultExtension(); } // Holder也是用于持有对象的,用作缓存 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) { // 创建拓展实例,下面分析createExtension(name) instance = createExtension(name); holder.set(instance); } } } return (T) instance; }
这里依赖注入可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入
private T createExtension(String name) { // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表 // 加载完后根据name获取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime // 待会重点分析getExtensionClasses(),删去了一些非关键代码 Class<?> clazz = getExtensionClasses().get(name); try { // 也是尝试先从缓存获取,获取不到通过反射创建一个并放到缓存中 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 依赖注入和cachedWrapperClasses,后面分析 injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } }
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() { // 获取SPI注解,这里的type是在调用getExtensionLoader方法时传入的 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { // 抛异常 } // 获取@SPI注解中的值,并缓存起来,可以关注下,后面会用到 if (names.length == 1) cachedDefaultName = names[0]; } } Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>(); // 加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/ loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); // 我是放在这个目录下的 loadDirectory(extensionClasses, DUBBO_DIRECTORY); loadDirectory(extensionClasses, SERVICES_DIRECTORY); return extensionClasses; }
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { // fileName是META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot 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()) { // 一个resourceURL就是一个文件 java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } }
private void loadResource(Map<string, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); try { String line; // 按行读取配置内容 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) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { // 真正的去加载类 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } } } } } }
标注1,这里的可以参考: Dubbo的SPI机制分析2-Adaptive详解
标注2,这里的可以参考: Dubbo的SPI机制分析4-Dubbo通过Wrapper实现AOP
标注3,这里的cachedActivates可以参考: Dubbo的SPI机制分析5-Activate详解
private void loadClass(Map<string, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { // clazz必须是type类型的,否则抛异常 if (!type.isAssignableFrom(clazz)) { } // 判断clazz是否为标注了@Adaptive注解,标注1 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { // 抛异常,不能有多个标注有@Adaptive注解的类 } } // 判断是否是Wrapper类型,标注2 else if (isWrapperClass(clazz)) { Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } // 程序进入此分支,表明clazz是一个普通的拓展类,Robot就是一个普通的拓展类 else { // 检测clazz是否有默认的构造方法,如果没有,则抛出异常 clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { // 抛异常 } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { // 用于@Activate根据条件激活,标注3 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) { // 存储名称到class的映射关系,这样就解析好了一行 extensionClasses.put(n, clazz); } else if (c != clazz) { // 抛异常 } } } } }
感谢各位的阅读,以上就是“Dubbo的SPI机制介绍以及SPI加载class的方法”的内容了,经过本文的学习后,相信大家对Dubbo的SPI机制介绍以及SPI加载class的方法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。