当前位置: 首页 > news >正文

Java加载 Grovy 类实现类的自动切换

Grovy 代码

import java.util.function.Function

class DynamicService implements Function<String, String> {

    void accept(String t) {
        println "acceptFunction"
    }

    @Override
    String apply(String o) {
        print("Function")
        return "Ok";
    }
}

Grovy 动态加载代码


import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.control.CompilationFailedException;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Function;

public class GroovyDynamicCompiler1 {

    // 缓存结构:文件路径 -> (类对象, 最后修改时间)
    private static final Map<String, CacheEntry> CLASS_CACHE = new ConcurrentHashMap<>();

    // 定时更新线程池
    private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);

    // 缓存有效期(30分钟)
    private static final Duration CACHE_DURATION = Duration.ofMinutes(30);

    static {
        // 启动定时清理任务
        SCHEDULER.scheduleAtFixedRate(() ->
                CLASS_CACHE.entrySet().removeIf(entry ->
                        entry.getValue().isExpired() || !entry.getValue().isValid()
                ), 5, 5, TimeUnit.MINUTES);

        // 关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            SCHEDULER.shutdown();
            CLASS_CACHE.clear();
        }));
    }
    
        public static void main(String[] args) throws Exception {
        // 填写文件地址
        Class<?> groovyClass = compileGroovyFile("");
        Object instance = groovyClass.newInstance();

        ((Function) instance).apply("s");
    }

    public static Class<?> compileGroovyFile(final String filePath) {
        // 1. 验证路径是否合法
        String fileName = validateFilePathAndGetFileName(filePath);

        // 2.如果函数返回 null,则删除映射(如果最初不存在,则保持不存在)。
        return CLASS_CACHE.compute(fileName, (name, oldEntry) -> {
            try {
                File file = new File(filePath);
                FileTime lastModified = Files.getLastModifiedTime(file.toPath());

                // 需要重新编译的情况:缓存不存在/文件被修改/ 缓存过期机制
                if (oldEntry == null || oldEntry.isExpired() || lastModified.compareTo(oldEntry.lastModified) != 0) {
                    //
                    try (GroovyClassLoader loader = new GroovyClassLoader()) {
                        Class<?> clazz = loader.parseClass(file);
                        CacheEntry newEntry = new CacheEntry(clazz, lastModified);
                        return newEntry;
                    }
                }
                return oldEntry;
            } catch (CompilationFailedException | IOException e) {
//                抛异常 还是标记错误
                throw new RuntimeException("Groovy编译失败: " + e.getMessage(),e);
            }
        }).clazz;
    }

    private static String validateFilePathAndGetFileName(String filePath) {
        // 缓存穿透防护
        if (filePath == null || filePath.trim().isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }

        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            throw new IllegalArgumentException("无效文件路径: " + filePath);
        }
        return file.getName();
    }

    // 缓存条目结构
    private static class CacheEntry {
        final Class<?> clazz;
        final FileTime lastModified;
        final long createTime;

        CacheEntry(Class<?> clazz, FileTime lastModified) {
            this.clazz = clazz;
            this.lastModified = lastModified;
            this.createTime = System.currentTimeMillis();
        }

        boolean isExpired() {
            return System.currentTimeMillis() - createTime > CACHE_DURATION.toMillis();
        }

        boolean isValid() {
            try {
                return clazz != null && clazz.getClassLoader() != null;
            } catch (Exception e) {
                return false;
            }
        }
    }


}

相关文章:

  • Linux中断处理流程
  • jdk21使用Vosk实现语音文字转换,免费的语音识别
  • RL基础以及AlphaGo、AlphaGo Zero原理
  • 英伟达GPU SKU设计核心策略
  • 【log4j】配置Slf4j
  • 机器学习——GBDT、GBRT
  • Vue下 Sortable 实现 table 列表字段可拖拽排序,显示隐藏组件开发
  • 食品计算—Nutrition5k: Towards Automatic Nutritional Understanding of Generic Food
  • 5、类的6个默认成员函数和特性--类的新功能
  • 高级java每日一道面试题-2025年3月14日-微服务篇[Eureka篇]-Eureka如何保证高可用性?
  • freecad手动装插件 add on
  • 最大数字(java)(DFS实现)
  • AMD机密计算虚拟机介绍
  • ubuntu系统安装docker
  • 天梯赛 L2-022 重排链表
  • 自顶向下学习K8S--部署Agones
  • Lua 数组
  • 平安证券 NoETL 指标平台实践:统一数据口径,驱动高效经营分析与智能决策
  • Ubuntu 使用终端手动连接无线网络(wlan0)完整流程 + 故障排查记录
  • Vue3.5 企业级管理系统实战(十一):全屏切换组件
  • 博物馆日|为一个展奔赴一座城!上海171家博物馆等你来
  • 新修订的《餐饮业促进和经营管理办法》公布,商务部解读
  • 《制止滥用行政权力排除、限制竞争行为规定(修订草案征求意见稿)》公开征求意见
  • 一图读懂丨创新创业人才最高补贴500万元!临港新片区发布创客新政“十二条”
  • 张涌任西安市委常委,已卸任西安市副市长职务
  • 第十二届警博会在京开幕:12个国家和地区835家企业参展