博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用Groovy实现动态加载Bean
阅读量:7155 次
发布时间:2019-06-29

本文共 6714 字,大约阅读时间需要 22 分钟。

  hot3.png

先来安利一发Groovy

Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy 可以使用其他 Java 语言编写的库. Groovy是JVM的一个替代语言(替代是指可以用 Groovy 在Java平台上进行 Java 编程),使用方式基本与使用 Java代码的方式相同,该语言特别适合与Spring的动态语言支持一起使用,设计时充分考虑了Java集成,这使 Groovy 与 Java 代码的互操作很容易 简单来说Groovy是一种脚本语言,有脚本语言的优点 快,并且和Java关联性很强,学习成本低,这么多对我来说就够了。

现实中的一个痛点问题:

我们在针对一些变化性很强的规则或是场景的时候需要对一些代码进行修改,但是每次修改就需要上线,这样对我们的开发来说比较痛苦

方案:

  采用zk+groovy的方式来解决普通场景下的这个问题

步骤一: 监听应用节点下的groovy配置项:默认以xxx.xxx.xxx.groovy.groovyname 节点的配置项作为groovy文件 文件名为groovyname,eg:com.src.project.groovy.test 监听zk的应用节点后,在相关的资源文件夹下生成test.groovy文件

步骤二: 刷新当前的SpringIoc相关的groovyBean的上下文,重载groovy文件

action: 

@Servicepublic class ZKGroovyConfigListener extends Observable implements TreeCacheListener, InitializingBean, ApplicationContextAware {    private Logger LOG = LoggerFactory.getLogger(ZKGroovyConfigListener.class);    private TreeCache cache;    private ApplicationContext applicationContext;    private final String GROOVY_CONFIG_PREFIX = "xxx.xxx.groovy.";    @Autowired    @Qualifier(value = "zookeeperConfigProperties")    private ZookeeperConfigProperties properties;    @Autowired    @Qualifier(value = "curator")    private CuratorFramework curator;    @Override    public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {        //监听节点的变化        if (event.getType().equals(TreeCacheEvent.Type.NODE_ADDED)                || event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED) || event.getType().equals(TreeCacheEvent.Type.NODE_REMOVED)) {            String path = event.getData().getPath();            String appName = applicationContext.getEnvironment().getProperty("application.name");            int appNameIndex = path.indexOf("/" + appName + "/");            if (appNameIndex > 0) {                String key = PathKeyUtil.sanitizeKey(path.substring(appNameIndex + appName.length() + 1));                if (StringUtils.isNotBlank(key) && key.startsWith(GROOVY_CONFIG_PREFIX)) {                    String newVal = new String(event.getData().getData(), Charset.forName("UTF-8"));                    EventNode node = buildNode(key.substring(GROOVY_CONFIG_PREFIX.length()), newVal, event.getType());                    setChanged();                    notifyObservers(node);                }            }        }    }    public void initTreeListener() throws Exception {        this.cache = TreeCache.newBuilder(this.curator, "/" + this.properties.getRoot() + "/" + applicationContext.getEnvironment().getProperty("application.name")).build();        this.cache.getListenable().addListener(this);        this.cache.start();    }    @Override    public void afterPropertiesSet() throws Exception {        try {            initTreeListener();        } catch (Exception e) {            LOG.error("ZKGroovyConfigListener.initTreeListener error!! errorMsg:{}", e.getMessage(), e);        }    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    private EventNode buildNode(String groovyName, String content, TreeCacheEvent.Type type) {        EventNode eventNode = new EventNode();        eventNode.setType(type);        eventNode.setGroovyFileName(groovyName);        eventNode.setGroovyContent(content);        return eventNode;    }    @Data    public static class EventNode {        private TreeCacheEvent.Type type;        private String groovyFileName;        private String groovyContent;    }}

此类用作监听相关节点的变化

@Servicepublic class GroovyLoaderObserver implements Observer, InitializingBean, ApplicationContextAware {    private static final String DEFAULT_GROOVY_FILE_PATH_PREFIX = "/script/groovy";    @Resource    ZKGroovyConfigListener listener;    private ApplicationContext applicationContext;    @Override    public void update(Observable o, Object arg) {        ZKGroovyConfigListener.EventNode eventNode = (ZKGroovyConfigListener.EventNode) arg;        if (Optional.ofNullable(eventNode).isPresent()) {            StringBuilder pathsb = new StringBuilder(this.getClass().getClassLoader().getResource(DEFAULT_GROOVY_FILE_PATH_PREFIX).getPath());            pathsb.setLength(pathsb.length() - 1);            String filePath = pathsb.append(File.separator).append(eventNode.getGroovyFileName()).append(".groovy").toString();            String finalFilePath = filePath.replace("/", File.separator).substring(1);            File file = new File(finalFilePath);            String content = TreeCacheEvent.Type.NODE_REMOVED == eventNode.getType() ? "" : eventNode.getGroovyContent();            try {                Files.write(content, file, Charsets.UTF_8);            } catch (IOException e) {                e.printStackTrace();            }            //refresh SpringContent            refreshSpringContentDueToGroovy(file);        }    }    private void refreshSpringContentDueToGroovy(File file) {        if (Optional.ofNullable(this.applicationContext).isPresent()) {            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((AnnotationConfigEmbeddedWebApplicationContext) this.applicationContext).getBeanFactory();            GenericBeanDefinition bd = new GenericBeanDefinition();            bd.setBeanClassName("org.springframework.scripting.groovy.GroovyScriptFactory");            bd.setAttribute("org.springframework.scripting.support.ScriptFactoryPostProcessor.language", "groovy");            bd.setAttribute("org.springframework.scripting.support.ScriptFactoryPostProcessor.refreshCheckDelay", 10000);            bd.getConstructorArgumentValues().addIndexedArgumentValue(0, "classpath:" + DEFAULT_GROOVY_FILE_PATH_PREFIX + "/" + file.getName());            beanFactory.registerBeanDefinition(file.getName().replace(".groovy", ""), bd);            ScriptFactoryPostProcessor processor = applicationContext.getBean(ScriptFactoryPostProcessor.class);            try {                processor.postProcessBeforeInstantiation(Class.forName(bd.getBeanClassName()), file.getName().replace(".groovy", ""));            } catch (ClassNotFoundException e) {                e.printStackTrace();            }        }    }    @Override    public void afterPropertiesSet() throws Exception {        listener.addObserver(this);    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }}

此类用作更新本地的groovy文件,并刷新相关Groovy Bean的IOC容器

配置文件

这样就ok了

       zk负责动态修改/新增相关的groovy脚本,zklistener监听节点的变化,更新本地的脚本,observer刷新当前的IOC容器中Bean的部分,完成脚本与Spring的结合。

      对于一些动态内容的修改,我们可以直接修改groovy脚本就可以啦。

 

转载于:https://my.oschina.net/guanhe/blog/1832843

你可能感兴趣的文章
6月14日奋战es5基础-1
查看>>
Socket IO与NIO(六)
查看>>
一图胜千言,8张图理解Java
查看>>
[算法]动态规划之最长递增子序列
查看>>
好程序员告诉你Java架构师学习路线
查看>>
Redis之主从集群环境搭建
查看>>
tab切换小例子
查看>>
Java封装
查看>>
【快学springboot】9.使用 @Transactional 注解配置事务管理
查看>>
匿名对象方案与实体对象方案对比
查看>>
NTP服务放大攻击的解决办法
查看>>
SQL SERVER 占用资源高的SQL语句
查看>>
lombok 安装
查看>>
virtualbox+centos 7 实现宿主机器互通
查看>>
Python爬虫学习笔记——防豆瓣反爬虫
查看>>
安装MySQL最后一步出现错误Error Nr.1045
查看>>
基于注解实现SpringBoot多数据源配置
查看>>
02 面向对象之:类空间问题以及类之间的关系
查看>>
LeetCode 402: Remove K Digits
查看>>
我的Python成长之路---第八天---Python基础(23)---2016年3月5日(晴)
查看>>