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 加锁 。加锁后再次检查缓存,并判空 。此时如果 classes 仍为 null,则通过 loadExtensionClasses 加载拓展类 。下面分析 loadExtensionClasses 方法的逻辑 。
private Map<String, Class<?>> loadExtensionClasses() {// 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的final SPI defaultAnnotation = type.getAnnotation(SPI.class);if (defaultAnnotation != null) {String value = https://www.huyubaike.com/biancheng/defaultAnnotation.value();if ((value = value.trim()).length() > 0) {// 对 SPI 注解内容进行切分String[] names = NAME_SEPARATOR.split(value);// 检测 SPI 注解内容是否合法,不合法则抛出异常if (names.length > 1) {throw new IllegalStateException("more than 1 default extension name on extension...");}// 设置默认名称,参考 getDefaultExtension 方法if (names.length == 1) {cachedDefaultName = names[0];}}}Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();// 加载指定文件夹下的配置文件loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);loadDirectory(extensionClasses, DUBBO_DIRECTORY);loadDirectory(extensionClasses, SERVICES_DIRECTORY);return extensionClasses;}loadExtensionClasses 方法总共做了两件事情,一是对 SPI 注解进行解析,二是调用 loadDirectory 方法加载指定文件夹配置文件 。SPI 注解解析过程比较简单,无需多说 。下面我们来看一下 loadDirectory 做了哪些事情 。
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {// fileName = 文件夹路径 + type 全限定名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 resourceURL = urls.nextElement();// 加载资源loadResource(extensionClasses, classLoader, resourceURL);}}} catch (Throwable t) {logger.error("...");}}loadDirectory 方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源 。我们继续跟下去,看一下 loadResource 方法的实现 。
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 方法对类进行缓存loadClass(extensionClasses, resourceURL,Class.forName(line, true, classLoader), name);}} catch (Throwable t) {IllegalStateException e = new IllegalStateException("Failed to load extension class...");}}}} finally {reader.close();}} catch (Throwable t) {logger.error("Exception when load extension class...");}}loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作 。loadClass 方法用于主要用于操作缓存,该方法的逻辑如下:
经验总结扩展阅读
- 10月28日3时42分新疆阿克苏地区沙雅县发生3.0级地震
- 10月28日4时34分新疆阿克苏地区温宿县发生3.0级地震
- 今年第22号台风“尼格”最强可达台风级 30日傍晚前后进入南海海面
- 红米note9pro评测最新_红米note9pro深度测评
- 空调一级能效与三级能效的区别是什么
- iOS 16.3.1正式版续航怎么样 iOS 16.3.1升级建议
- ipx7和ip68哪个更防水 哪个防水等级更高
- 10月28日今起三天陕西持续阴雨模式局地阵风6级 风寒效应明显需保暖
- EasyPoi大数据导入导出百万级实例
- Dubbo-聊聊通信模块设计