Spring动态加载/刷新class文件,注入Bean到Spring容器

  1. 加载类信息:
@Slf4j
public class ClassLoadUtil {

    public static Class<?> loadClass(String classPath, String className) {
        MyClassLoader loader = new MyClassLoader(classPath);
        return loader.findClass(className);
    }

    private static class MyClassLoader extends ClassLoader {
        /**
         * 需要加载类的路径,E:\Hello.class
         */
        private final String classPath;

        public MyClassLoader(String classPath) {
            super();
            this.classPath = classPath;
        }

        @Override
        protected Class<?> findClass(String className) {
            Class<?> clazz = null;
            // 获取class文件字节码数组
            byte[] clazzByteArr = getData();
            if (clazzByteArr != null) {
                // 将class的字节码数组转换成class类的实例
                clazz = defineClass(className, clazzByteArr, 0, clazzByteArr.length);
            }
            return clazz;
        }

        /**
         * 获取class文件字节数组
         * @return 字节码
         */
        private byte[] getData() {
            File file = new File(this.classPath);
            if (file.exists()) {
                try (FileInputStream in = new FileInputStream(file)) {
                    return IOUtils.toByteArray(in);
                } catch (IOException e) {
                    log.error("读取文件异常:", e);
                }
            }
            return null;
        }
    }
}
  1. Bean容器工具类:
@Component
public class SpringContextUtil implements BeanFactoryAware {

    public static DefaultListableBeanFactory listableBeanFactory;

    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    public static <T> void registerSingleton(Class<T> type) {
        T obj = listableBeanFactory.createBean(type);
        String beanName = beanName(type.getName());
        listableBeanFactory.registerSingleton(beanName, obj);
    }

    public static void destroy(String className) {
        String beanName = beanName(className);
        listableBeanFactory.destroySingleton(beanName);
    }

    public static String beanName(String className){
        String[] path = className.split("\\.");
        String beanName = path[path.length - 1];
        return Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
    }

    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name){
        return (T) listableBeanFactory.getBean(name);
    }

    public static <T> T getBean(Class<T> type) {
        return listableBeanFactory.getBean(type);
    }

    public static <T> Map<String, T> getBeans(Class<T> type) {
        return listableBeanFactory.getBeansOfType(type);
    }
}
  1. 测试:
    每次加载类信息时,先卸载旧的Class,当然已有的Bean也要卸载,要卸载Bean,必须也要把类加载器卸载掉。JVM判断类卸载的条件是:
    1.该class的引用和实例已经不存在;
    2.classLoader已经不存在;
@SneakyThrows
public void testBean(String classPath, String className) {
  	//销毁Bean
    SpringContextUtil.destroy(className);
    //每次都是new新的ClassLoader对象
    Class<?> type = ClassLoadUtil.loadClass(classPath, className);
    SpringContextUtil.registerSingleton(type);
    String beanName = SpringContextUtil.beanName(className);
    Object obj = SpringContextUtil.getBean(beanName);
    Method m = type.getMethod("test", String.class);
    m.invoke(obj, beanName);
}
@Slf4j
public class BeanTest {

    public void test(String name) {
        log.info("Hello,Hello,Hello,Hello,{}", name);
    }
}

接口测试一下:
测试结果
测试结果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值