devtools导致getBean获取不到实例No qualifying bean of type解决

最近在项目A中需要使用组件B的功能,组件B通过maven jar包的方式依赖到项目A中。其中组件B中有一段代码的逻辑是:通过接口的名字(含包名) 反射获取对应的 class,然后通过spring的getBean(class) 获取对应的bean实例,用于后续的操作。

类似下面的代码:

@Autowired
private ApplicationContext ctx;

....

Class api = Class.forName("com.xxx.xxx.api.provider.IUserService");
IUserService userService = (IUserService)ctx.getBean(api);
userService.query(req);
....

但项目A运行时,有些时候上面的这段代码始终获取不到bean。报错:

No qualifying bean of type 'com.xxx.xxx.api.provider.IUserService' available

但在项目A中用相同的代码获取这个bean时,却能正常获得实例。

经过不断的的调试,在执行Class的一段代码中:

    public static Class forName(String className)
                throws ClassNotFoundException {
        Class caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

执行到 项目A中的 ClassLoader.getClassLoader(caller)  是  RestartClassLoader

但执行到组件B的 ClassLoader.getClassLoader(caller) 时 是Luncaher$AppClassLoader

那么就很明显了,执行getBean时 里面的ClassLoader不同导致的问题。那就很容易理解问题产生的原因了,Java的双亲委派为什么会导致这个问题呢?那就要好好挖一挖 RestartClassLoader 是怎么产生的了。

正常情况下 存在Bootstrap ClassLoader   Extension ClassLoader、Application ClassLoader。那RestartClassLoader就属于 自定义的类加载器。

同时,项目的类应该由 Application ClassLoader 进行加载,那为什么项目A的类 却由RestartClassLoader进行加载了呢?

devtools导致getBean获取不到实例No qualifying bean of type解决_第1张图片

通过搜索,关键词:devtools 浮出了水面。

原来Spring的dev-tools为了实现重新装载class自己实现了一个类加载器,来加载项目中会改变(上面说的组件B是通过jar方式导入进来的,那很明显就不会改变,因此使用 appclassloader加载)的类,方便重启时将新改动的内容更新进来,其实其中官方文档中是有做说明的:

By default, any open project in your IDE will be loaded using the 
“restart” classloader, and any regular .jar file will be loaded using 
the “base” classloader. If you work on a multi-module project, and not 
each module is imported into your IDE, you may need to customize 
things. To do this you can create a 
META-INF/spring-devtools.properties file.
The spring-devtools.properties file can contain restart.exclude. and 
restart.include. prefixed properties. The include elements are items 
that should be pulled up into the “restart” classloader, and the 
exclude elements are items that should be pushed down into the “base” 
classloader. The value of the property is a regex pattern that will be 
applied to the classpath.

默认情况下,IDE中任何打开的项目都将使用“restart”类加载器加载,任何常规的.jar文件都将使用“base”类加载器加载。如果您处理的是多模块项目,而不是每个模块都导入到IDE中,那么您可能需要进行自定义。为此,您可以创建一个META-INF/spring-devtools.properties文件。

spring-devtools.properties文件可以包含restart.exclude和restart.include的前缀属性。include元素是应该向上拉入“restart”类加载器的项,exclude元素是应该向下推入“base”类加载器的项。属性的值是将应用于类路径的正则表达式模式。

解决方案

方案一:

在resources目录下面创建META-INF文件夹,然后创建spring-devtools.properties文件,文件加上类似下面的配置:(注意:正则表达式在java字符串的转义, 比如:\w 需要写成  \\w    \.  需要写成\\. )

restart.exclude.companycommonlibs=/xxx-xxx-[\\w\\.-]+\\.jar
restart.include.projectcommon=/zzz-zzz-[\\w\\.-]+\\.jar

All property keys must be unique. As long as a property starts with restart.include. or restart.exclude. it will be considered. All META-INF/spring-devtools.properties from the classpath will be loaded. 
You can package files inside your project, or in the libraries that the project consumes.

可以将spring-devtools.properties 放到当前的项目中,也可以放到被引用的项目jar中。

在上面的问题中,由于组件B 也是我们自己有权限修改的项目,因此可以在项目B中增加上面的配置文件即可解决问题。

方案二:

移除devtools的依赖

        

相关链接:

SpringBoot使用devtools导致的类型转换异常_试水流连的博客-CSDN博客

父类加载器调用子类加载器 RestartClassLoader - 简书

spring boot之 DevTools 热部署,修改代码立刻见效,快速重启_qq_27886997的博客-CSDN博客_spring.devtools.restart.enabled

你可能感兴趣的:(问题,Java,后端,java,ide)