反正我收藏了!Apache Dubbo介绍以及扩展机制SPI( 三 )
上面的代码并不复杂 , 主要的步骤是:
- 通过 getExtensionClasses 获取所有拓展类
- 通过反射创建拓展对象
- 向拓展对象注入依赖
- 将拓展对象包裹进对应的 Wrapper 对象中
- 初始化对象
第一步第一步主要是获取所有拓展类 。 我们来看看 getExtensionClasses() 的代码 。
private Map> getExtensionClasses() {//从spi注解中获取默认名字并挂载在cachedDefaultName这个属性上Map> classes = cachedClasses.get();//如果为空 , 则进行全部加载if (classes == null) {synchronized (cachedClasses) {classes = cachedClasses.get();if (classes == null) {//加载扩展类classes = loadExtensionClasses();cachedClasses.set(classes);}}}return classes;}
上面代码主要是为加载拓展类进行判断private Map> loadExtensionClasses() {//缓存组件的名称cacheDefaultExtensionName();Map> extensionClasses = new HashMap<>();//加载策略for (LoadingStrategy strategy : strategies) {loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());}return extensionClasses;}private void cacheDefaultExtensionName() {//获取注解 @SPIfinal SPI defaultAnnotation = type.getAnnotation(SPI.class);if (defaultAnnotation == null) {return;}//注解的值String value = http://kandian.youth.cn/index/defaultAnnotation.value();if ((value = value.trim()).length()> 0) {String[] names = NAME_SEPARATOR.split(value);if (names.length > 1) {//throw Exception}if (names.length == 1) {cachedDefaultName = names[0];}}}
loadDirectory 方法就是加载某目录下的资源 , 然后通过 loadResource 方法加载资源 。 我们继续跟下去 。private void loadDirectory(Map> extensionClasses, String dir, String type,boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {String fileName = dir + type;try {Enumeration urls = null;//获取 classLoaderClassLoader classLoader = findClassLoader();//获取 ClassLoader 这里会尝试以拓展类所在的类加载器为首要对象if (extensionLoaderClassLoaderFirst) {ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {urls = extensionLoaderClassLoader.getResources(fileName);}}if (urls == null || !urls.hasMoreElements()) {if (classLoader != null) {urls = classLoader.getResources(fileName);} else {urls = ClassLoader.getSystemResources(fileName);}}if (urls != null) {while (urls.hasMoreElements()) {java.net.URL resourceURL = urls.nextElement();loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);}}} catch (Throwable t) {//...}}
loadResource 方法主要是读取文件 , 然后解析文件内容 , 通过反射加载类后 , 然后调用 loadClass 方法进行操作缓存 。 代码如下:private void loadResource(Map> extensionClasses, ClassLoader classLoader,java.net.URL resourceURL, boolean overridden, String... excludedPackages) {try {try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {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}} catch (Throwable t) {//throw Exception}}}}} catch (Throwable t) {//...}}
loadClassprivate void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class> clazz, String name,boolean overridden) throws NoSuchMethodException {if (!type.isAssignableFrom(clazz)) {throw new IllegalStateException("Error occurred when loading extension class (interface: " +type + ", class line: " + clazz.getName() + "), class "+ clazz.getName() + " is not subtype of interface.");}//如果 class 有 Adapter 注解if (clazz.isAnnotationPresent(Adaptive.class)) {cacheAdaptiveClass(clazz, overridden);} else if (isWrapperClass(clazz)) { //是否是扩展类 , 是的话就加入 cachedWrapperClasses 属性cacheWrapperClass(clazz);} else {clazz.getConstructor();//检测是否有默认构造起if (StringUtils.isEmpty(name)) {//// 如果 name 为空 , 则尝试从 Extension 注解中获取 name , 或使用小写的类名作为 namename = findAnnotationName(clazz);if (name.length() == 0) {//throw Exception}}//分割名字String[] names = NAME_SEPARATOR.split(name);if (ArrayUtils.isNotEmpty(names)) {//存储 name 和 Activate 到 cachedActivatescacheActivateClass(clazz, names[0]);for (String n : names) {//存储 Class 到名字的映射关系cacheName(clazz, n);//存储名字到 Class 的映射关系saveInExtensionClass(extensionClasses, clazz, n, overridden);}}}}
- 先别|用了周冬雨的照片,我会成为下一个被告?自媒体创作者先别自乱阵脚
- 当初|这是我的第一部华为手机,当初花6799元买的,现在“一文不值”?
- 发展|我省要求互联网平台坚持依法合规经营 推动线上经济健康规范发展
- 页面|流程图怎样画?老板要我帮他做个组织结构图
- 深度|iPhone12到底值得买吗 深度体验一周我发现了这些
- 效果|周冬雨化身美妆效果评测员?相比美妆数码宅的我更期待OPPO新机
- 退费|女子公众号上买菜,出现问题时已充上万元,公司:我们没有退费规矩
- 自助|新型通道-健康码自助核验闸机
- 环境|环境标识认知转盘游戏
- 手机壳里头|为什么要在手机壳里面夹钱?10个有9个不懂,我才知道大有讲究