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);
    }
相关推荐
zhongjunyao4 分钟前
Spec-Driven Development (SDD): The Definitive 2026 Guide
javascript·spring boot·python·eclipse·代理模式
程序员爱钓鱼8 小时前
Rust Copy详解:隐式复制与轻量数据类型
前端·后端·rust
仙人球部落 揞殺9 小时前
细说ASP.NET的各种异步操作
后端·asp.net
卷无止境11 小时前
当独立开发者也能造出3A画质的游戏:Godot引擎深度解析
后端·python·godot
vx-程序开发12 小时前
springboot农产品运输服务平台---附源码75498
java·javascript·spring boot·python·eclipse·django·php
攻城有术12 小时前
专项攻克——spring、springMVC、springBoot、springCloud的启动流程
spring boot·spring·spring cloud
独行侠影a12 小时前
SpringBoot 分布式锁实战:Redisson 解决订单超卖并发问题
spring boot·分布式·后端
妙码生花13 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十八):后台系统配置管理实现
前端·后端·go
IT_陈寒13 小时前
Java并行流把我坑惨了:原来不是线程安全的!
前端·人工智能·后端