插件里Spring的AnnotationConfigApplicationContext扫描不到bean
-
mirai 2.6.7。
(一个位于com.hundun.mirai.bot.core包的类)使用以下代码创建ApplicationContext并扫描com.hundun.mirai.bot包,打印beans的名字。AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.scan("com.hundun.mirai.bot"); context.refresh(); console.getLogger().info("ApplicationContext created, has beans = " + Arrays.toString(context.getBeanDefinitionNames()));
IDE单元测试里加载插件,可以扫到com.hundun.mirai.bot里的beans:
buildPlugin以后放入mcl,启动后扫不到com.hundun.mirai.bot里的beans:
确认插件jar里com.hundun.mirai.bot包内容正常:
-
具体是想实现什么啊,使用mybaits? 还是 构建 http api ?, spring 框架不建议在插件里使用
-
Spring 默认使用 DefaultResourceLoader 加载类,DefaultResourceLoader 会设置为当前线程的默认类加载器,大概率就是 AppClassLoader
因此,Spring 的 DefaultResourceLoader 只能加载 classpath 下的类
Mirai Console 对每一个插件使用了单独的 URLClassLoader 加载 Jar 包,插件中的类是不在 classpath 下的
-
外部加载 Jar 包是插件系统常用的做法,所以和 Srping 默认从 classpath 下读取 bean 是冲突的,所以我一直不推荐使用 spring 容器开发各种插件。
虽然我没有做过,但是可以提供一个大概率可行的思路。
- 额外开启 一个线程用于用于初始化 SpringContext
- 将这个线程的 TCCL (Thread Context Class Loader) 设置为插件主体(JvmPlugin) 的类加载器
这样 SpringContext 在初始化的时候应该就会取到插件的类加载器。注意,这个 SpringContext 是否能扫描到插件之外的 Bean 取决与 Mirai Console 的实现
-
val thread = Thread.currentThread() val oc = thread.contextClassLoader try { thread.contextClassLoader = XXXPlugin::class.java.classLoader AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.scan("com.hundun.mirai.bot"); context.refresh(); console.getLogger().info("ApplicationContext created, has beans = " + Arrays.toString(context.getBeanDefinitionNames())); finally { thread.contextClassLoader = oc }
大概这样呗
-
-
@ryoii 在 插件里Spring的AnnotationConfigApplicationContext扫描不到bean 中说:
外部加载 Jar 包是插件系统常用的做法,所以和 Srping 默认从 classpath 下读取 bean 是冲突的,所以我一直不推荐使用 spring 容器开发各种插件。
虽然我没有做过,但是可以提供一个大概率可行的思路。
- 额外开启 一个线程用于用于初始化 SpringContext
- 将这个线程的 TCCL (Thread Context Class Loader) 设置为插件主体(JvmPlugin) 的类加载器
这样 SpringContext 在初始化的时候应该就会取到插件的类加载器。注意,这个 SpringContext 是否能扫描到插件之外的 Bean 取决与 Mirai Console 的实现
试了,可行
-
@hundun000 你好,我也是使用Spring框架做机器人项目,我比较好奇你打包的jar包具体内容。能否发一份你打包的使用Spring的插件jar包?如果你当前项目不方便公布的话,能否用spring写个hello world的插件,然后打包发我一份?如果论坛不方便发送的话,我QQ1952511149。非常感谢!
-
@lc6a 在 插件里Spring的AnnotationConfigApplicationContext扫描不到bean 中说:
@hundun000 你好,我也是使用Spring框架做机器人项目,我比较好奇你打包的jar包具体内容。能否发一份你打包的使用Spring的插件jar包?如果你当前项目不方便公布的话,能否用spring写个hello world的插件,然后打包发我一份?如果论坛不方便发送的话,我QQ1952511149。非常感谢!
我之前打算解决这个问题的,被一些事情耽搁了。你如果能顺便提供源码就更好了,我可能会尝试解决此问题。如果我解决了,我可以跟你分享分享。
-
-
继续实验了下,有如下发现:
- 与其新建线程然后让Spring默认使用当前线程的默认类加载器,其实可以直接context.setClassLoader。
- 不一定要插件主体(JvmPlugin) 的类加载器,似乎任意一个插件jar里的类都行,所以这里直接用this.getClass().getClassLoader()。
context = new AnnotationConfigApplicationContext(); context.setClassLoader(this.getClass().getClassLoader()); context.scan("com.hundun.mirai.bot"); context.refresh();
不过上述操作只能让Ioc容器生效。距离我的需求的后两项,试了下,不止@EnableMongoRepositories,@EnableFeignClients这么简单,还需替换其他的Spring默认值,目前还在研究。
-
@hundun000
检索一遍spring 里 classloader的调用呗 -
@hundun000 在 [插件里Spring的AnnotationConfigApplicationContext扫描不到bean]
距离我的需求的后两项,试了下,不止@EnableMongoRepositories,@EnableFeignClients这么简单,还需替换其他的Spring默认值,目前还在研究。
1)@EnableMongoRepositories其实挺简单就能用上,之前是别的地方搞错了。
2) 我别的项目都是直接上springboot全套,才注意到openfeign自动实现是依赖spring-cloud,打算不引入了。手动构造openfeign本身工作量也不大。