Spring自动注入遇到的坑:不同类加载器加载进同一个class,类型无法匹配

0x0 背景

最近在测试从项目外部加载class进来,然后通过BeanFactoryPostProcessor注入到spring容器中,结果出了一点问题:外部加载的class中@Autoware和@Resource注解都无法注入属性!
报错如下:

Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'testService' is expected to be of type 'com.fly.architecture.service.TestService' but was actually of type 'com.fly.architecture.service.TestService'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:395) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]

可以看到,所需注入的属性类是com.fly.architecture.service.TestService,实际注入的属性类是com.fly.architecture.service.TestService,看起来完全一样!

完整代码如下:

@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    private static final String CLASS_PATH = "C:\\Users\\code\\";

    private Class compileBeanClass() throws IOException, ClassNotFoundException {
        //自定义一个类加载器,加载class
        URL url = new URL("file:///" + CLASS_PATH );
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
        return urlClassLoader.loadClass("TestController");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("调用了自定义的BeanFactoryPostProcessor加载bean");

        BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;

        Class beanClass;
        try {
            beanClass = compileBeanClass();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            throw new BeanCreationException(e.getMessage());
        }
        //将加载的class设置为bean definition,然后注入到spring容器中
        AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(beanClass);

        registry.registerBeanDefinition(beanClass.getSimpleName(), beanDefinition);
    }
}

0x1 原因

进入debug,从报错的地方下断点,发现所需的类和bean的类虽然名字相同,但是Class对象却不是同一个:


image.png

从图中可以看出,两个对象虽然名称一致,但是hashCode以及内部的ClassLoader都不相同,因此spring在判断注入的bean类型和所需类型是否相同时,发现两者不一致!

0x2 解决方法

将类加载的地方,改成如下方式,利用class loader的上级委托机制,实现不重复加载相同类:

        //正确做法,先获取当前类的classloader
        ClassLoader classLoader = getClass().getClassLoader();
        //这里很重要,需要设置当前classloader为父加载器,否则会加载重复的类进来
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, classLoader);
        
        return urlClassLoader.loadClass("TestController");

你可能感兴趣的:(Spring自动注入遇到的坑:不同类加载器加载进同一个class,类型无法匹配)