springBoot动态加载jar,将类注册到IOC

具体实现

Service

java 复制代码
public interface JarService {
    Boolean loadJarByName(LoadJarDTO dto);
}

impl:

java 复制代码
@Service
@RequiredArgsConstructor
@Slf4j
public class JarServiceImpl implements JarService {

    private final SpringContextUtil springContextUtil;
    @Override
    public Boolean loadJarByName(LoadJarDTO dto) {
        //初始化File-加载jar所在目录
        File jarFile = new File(dto.getJarPath());
        if (!jarFile.exists()){
            return false;
        }
        List<String> loadClassList = this.getLoadClass(jarFile.getAbsolutePath());
        if (CollectionUtils.isEmpty(loadClassList)){
            return false;
        }
        //加载jar
        ClassLoader classLoader = ClassLoaderUtil.getJarClassLoader(jarFile);
        for (String loadClassPath : loadClassList) {
            Class<?> clazz = null;
            try {
                clazz = classLoader.loadClass(loadClassPath);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            if(!this.isLoadClass(clazz)){
                continue;
            }
            //生成需要注册到ioc的bean名称
            // clazz.getSimpleName()用于获取表示该类的简单名称(不包括包名)。简单名称就是类名本身,而不带任何修饰或包的前缀。
            String beanName = getBeanName(dto.getPreBeanName(),clazz.getSimpleName()) ;
            //加载需要加载的
            if (!dto.getIsOverLoad() && springContextUtil.containsBeanDefinition(beanName)){
                continue;
            }
            if (dto.getIsOverLoad() && springContextUtil.containsBeanDefinition(beanName)){
                springContextUtil.unregisterBean(beanName);
            }
            springContextUtil.registerBean(beanName,clazz);
        }

        return true;
    }

    private static String getBeanName(String name, String className) {
        return StringUtils.uncapitalize(name) + StringUtils.capitalize(className);
    }


    private boolean isLoadClass(Class<?> clazz) {
        if (clazz == null){
            return false;
        }
        //是否是接口
        if (clazz.isInterface()){
            return false;
        }
        //是否是抽象类
        if (Modifier.isAbstract(clazz.getModifiers())){
            return false;
        }
        if (clazz.getAnnotation(Service.class) != null){
            return true;
        }
        if (clazz.getAnnotation(Component.class) != null){
            return true;
        }
        if (clazz.getAnnotation(Repository.class) != null){
            return true;
        }
        if (clazz.getAnnotation(Configuration.class) != null){
            return true;
        }
        return false;
    }

    private List<String> getLoadClass(String jarPath) {
        Set<String> classPathList = new HashSet<>();
        File file = new File(jarPath);
        //获取jar的流,打开jar文件
        try ( JarInputStream jarInputStream = new JarInputStream(FileUtil.getInputStream(file))){
            //逐个获取jar种文件
            JarEntry jarEntry = jarInputStream.getNextJarEntry();
            //遍历
            while (jarEntry != null){
                //获取文件路径
                String name = jarEntry.getName();
                if (name.endsWith(".class")){
                    String classNamePath = name.replace(".class", "").replace("/",".");
                    classPathList.add(classNamePath);
                }
                jarEntry = jarInputStream.getNextJarEntry();
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new RuntimeException("获取加载类路径失败");
        }
        return new ArrayList<>(classPathList);
    }
}

使用:

新建一模块,写一个简单类:

java 复制代码
@Service
public class CalculateServiceImpl implements CalculateService {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int minus(int a, int b) {
        return a - b;
    }
}

建立一个公共接口,放在公共模块,以供加载jar时候,可以获取接口方法

java 复制代码
public interface CalculateService {
    int add(int a,int b);

    int minus(int a ,int b);
}

然后 cleam-install打成jar

建立一加载使用:

java 复制代码
    @GetMapping("/calculate/{beanName}/{a}/{b}")
    private Integer calculate( @PathVariable(value = "beanName") String beanName,
            @PathVariable(value = "a") Integer a, @PathVariable("b") Integer b){
        return jarService.calculate(beanName,a,b);
    }

 @Override
    public Integer calculate(String beanName, Integer a, Integer b) {
        CalculateService calculateService = SpringContextUtil.getBean(getBeanName(beanName,beanName+"Impl"), CalculateService.class);
        int add = calculateService.add(a, b);
        return add;
    }
    private static String getBeanName(String name, String className) {
        return StringUtils.uncapitalize(name) + StringUtils.capitalize(className);
    }
相关推荐
小蒜学长43 分钟前
编程语言在线学习平台的设计与实现(代码+数据库+LW)
java·spring boot·后端·编程语言在线学习平台·在线编码
zhoujungis2 小时前
登录态 1 小时就掉?Django + Vue 双 Token 认证实战 | 尘世栈帧
后端
Félix2512 小时前
Nacos学习笔记(SpringBoot/SpringCloud)
java·spring boot·spring
武子康2 小时前
同一台 ESP32,GSM 能消回声,VoIP 为什么不行?
人工智能·后端·agent
用户69371750013842 小时前
AI 能写代码,但工程架构还是应该由人来定
前端·后端
lqbzlll2 小时前
Spring Boot 开启虚拟线程后接口反而变慢了?4 个排查方向和对应修法
java·spring boot·后端
回家路上绕了弯3 小时前
让 AI 成为团队的共同能力
后端
70后写java的老码农3 小时前
一个 starter 通吃 9 家主流大模型:Spring Boot 3.x + Spring AI 1.x 极简落地
后端
企业数字化笔记3 小时前
业务单据怎样接入流程中心?启动条件、实例关联与失败回退
java·spring boot·mysql
武子康3 小时前
MultiNet 只听到一次命令,为什么连续触发?
人工智能·后端·agent