{"record":{"id":"50b0b61158d3251c","repo":"dromara/Sa-Token","slug":"spi-e-getmessage","errorCode":null,"errorMessage":"SPI 插件加载失败: \" + e.getMessage()","messagePattern":"SPI 插件加载失败: \" \\+ e\\.getMessage\\(\\)","errorType":"exception","errorClass":"SaTokenPluginException","httpStatus":null,"severity":"error","filePath":"sa-token-core/src/main/java/cn/dev33/satoken/plugin/SaTokenPluginHolder.java","lineNumber":111,"sourceCode":"\t\ttry {\n\t\t\tClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n\t\t\tEnumeration<URL> resources = classLoader.getResources(path);\n\t\t\twhile (resources.hasMoreElements()) {\n\t\t\t\tURL url = resources.nextElement();\n\t\t\t\ttry (InputStream is = url.openStream()) {\n\t\t\t\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(is));\n\t\t\t\t\tString line;\n\t\t\t\t\twhile ((line = reader.readLine()) != null) {\n\t\t\t\t\t\tline = line.trim();\n\t\t\t\t\t\t// 忽略空行和注释行\n\t\t\t\t\t\tif (!line.isEmpty() && !line.startsWith(\"#\")) {\n\t\t\t\t\t\t\tClass<?> clazz = Class.forName(line, true, classLoader);\n\t\t\t\t\t\t\tT instance = serviceInterface.cast(clazz.getDeclaredConstructor().newInstance());\n\t\t\t\t\t\t\tproviders.add(instance);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new SaTokenPluginException(\"SPI 插件加载失败: \" + e.getMessage(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new SaTokenPluginException(\"SPI 插件加载失败: \" + e.getMessage(), e);\n\t\t}\n\t\treturn providers;\n\t}\n\n\n\n\t// ------------------- 插件管理 -------------------\n\n\t/**\n\t * 所有插件的集合\n\t */\n\tprivate final List<SaTokenPlugin> pluginList = new ArrayList<>();\n\n\t/**","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-core/src/main/java/cn/dev33/satoken/plugin/SaTokenPluginHolder.java#L93-L129","documentation":"SaTokenPluginException thrown by SaTokenPluginHolder's SPI loader (outer catch) when loading sa-token plugins from META-INF/services fails for any reason outside the per-file reading block — e.g. failing to locate/open the service resource itself, classloader errors, or exceptions raised while iterating providers. The message embeds the underlying cause's message ('SPI 插件加载失败: ' + e.getMessage()) and the original exception is chained, so the real reason is in getCause().","triggerScenarios":"SaTokenPluginHolder's service-loading routine runs at startup; any exception other than the inner line-parsing/instantiation block (e.g. getResources() failure, stream/IO errors at the outer level) lands in this outer catch and is rethrown as 'SPI 插件加载失败: ...'.","commonSituations":"Broken fat-jar/shaded jar that mangles META-INF/services entries; classloader restrictions in app servers (Tomcat shared libs, OSGi) preventing resource access; a service file naming a class whose static initializer throws; conflicting sa-token plugin versions on the classpath.","solutions":["Inspect the chained cause: catch SaTokenPluginException and read getCause() to find the real failure (ClassNotFound, IOException, etc.)","Verify the sa-token integration jars' META-INF/services files survive packaging (enable ServicesResourceTransformer in maven-shade-plugin)","Align all sa-token modules to one version and remove duplicates from the dependency tree (mvn dependency:tree)"],"exampleFix":"// before: shade plugin merges jars without merging services\n// startup -> SaTokenPluginException: SPI 插件加载失败: ...\n\n// after (pom.xml)\n<plugin>\n  <groupId>org.apache.maven.plugins</groupId>\n  <artifactId>maven-shade-plugin</artifactId>\n  <configuration>\n    <transformers>\n      <transformer implementation=\"org.apache.maven.plugins.shade.resource.ServicesResourceTransformer\"/>\n    </transformers>\n  </configuration>\n</plugin>","handlingStrategy":"try-catch","validationCode":"// before relying on SPI plugins, verify the service resources are visible:\nEnumeration<URL> urls = getClass().getClassLoader().getResources(\"META-INF/services/cn.dev33.satoken.plugin.SaTokenPlugin\");\nif (!urls.hasMoreElements()) { /* no plugins will load; check packaging */ }","typeGuard":null,"tryCatchPattern":"try {\n    // trigger plugin loading\n} catch (SaTokenPluginException e) {\n    Throwable cause = e.getCause();\n    // classify: ClassNotFound -> version/classpath; IOException -> packaging; log cause for diagnosis\n    throw e;\n}","preventionTips":["Keep all sa-token modules at one version and prune duplicates from the dependency tree","Configure shade/assembly plugins to merge META-INF/services entries","Run the app once in CI with verbose classloading to catch SPI breakage early"],"tags":["spi","plugin-system","classpath","packaging","sa-token"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}