利用aop实现热拔插(类似于插件)

现在有这么一个需求:就是我们日志的开与关是交给使用人员来控制的,而不是由我们开发人员固定写死的。大家都知道可以用aop来实现日志管理,但是如何动态的来实现日志管理呢?aop源码中的实现逻辑中有这么一个步骤,就是会依次扫描Advice的实现类,然后执行。我们要做的就是自定义一个advice的实现类然后,在用户想要开启日志的时候就把advice加到项目中来,关闭日志的时候就把advice剔除就行了。好了知道思路了那么开始敲代码吧;

1:编写我们的插件

新建一个maven项目名字为pluginLog,项目中编写一个类名字为pluginLog

public class pluginLog implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("Log");
    }

    public static void main(String[] args) {
        System.out.println("日志");
    }
    public pluginLog() {
    }
}

2:打jar包

利用maven命令把插件打成jar包,这个jar包就是我们的插件。打包过程:win+r输入cmd回车,进入dos窗口切换到我们的插件项目如下,输入mvn clean package回车,jar包就打包好了。或者直接使用 IDEA 打包
在这里插入图片描述
在这里插入图片描述

3:找到jar包

删除plugin-0.0.1-SNAPSHOT.jar把plugin-0.0.1-SNAPSHOT.jar.original的这个jar包改个名字为plugin-0.0.1-SNAPSHOT.jar
在这里插入图片描述复制jar到我们的测试目录,我这里是在d盘下的com下的zzh中
在这里插入图片描述

4:实现插件热拔插核心代码

另外新建一个项目,名字为pluginUse
在这里插入图片描述

编写我们的插件配置信息pluginConfig.json放在resource目录下内容如下

{ “name”:“aop插件”,
“configs”:[{
“id”:“1”,
“name”:“测试插件”,
“active”:false,
//加载类
“className”:“com.zzh.pluginLog”,
//“className”:“com.destiny.plugin.TestPlugin”,
// jar包路径
“jarRemoteUrl”:“jar:file:/D:/com/zzh/pluginLog.jar!/” }] }

接着开始编写我们的激活插件的代码,主要是通过applicationContext来得到所有的bean,然后做判断,哪些bean要被加上我们jar包中的通知。这里我们需要搞清楚这几个概念
Advised: 包含所有的Advisor 和 Advice

Advice: 通知拦截器

Advisor: 通知 + 切入点的适配器

/**
     * @param
     * @method 激活插件
     */
    public void activePlugin(String id) {
        if (!cachePluginConfigs.containsKey(id)) {
            throw new RuntimeException(String.format("这个插件不存在id=%s", id));
        }
        //获取插件的配置信息
        PluginConfig pluginConfig = cachePluginConfigs.get(id);
        //设置激活状态
        pluginConfig.setActive(true);
        //拿到所有的beanDefinition
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        //为符合条件的bean加上插件
        for (String beanDefinition : beanDefinitionNames) {
            Object bean = applicationContext.getBean(beanDefinition);
            //bean是本类跳过
            if (bean == this) {
                continue;
            }
            //bean是null跳过
            if (bean == null) {
                continue;
            }
            //bean
            if (!(bean instanceof Advised)) { // 判断是否属于通知对象
                continue;
            }
            //判断当前bean是否已经有通知拦截器作用,如果有说明已经激活过了直接跳过
            if (find(bean, pluginConfig.getClassName())) {
                continue;
            }
            try {
                //根据插件的配置信息生成通知
                Advice advice = bulidAdvice(pluginConfig);
                //给bean加上通知
                ((Advised) bean).addAdvice(advice);
            } catch (Exception e) {
                System.out.println("插件添加失败");
            }

        }

    }

bulidAdvice

通过类加载器,通过jar中的pluginLog.class实例化对象,然后返回此advice

/**
     * @param
     * @method 创建通知
     */
    public Advice bulidAdvice(PluginConfig pluginConfig) {
        Advice plugin = null;
        try {
            Boolean isLoad = false;
            //获取jar包url路径
            URL targetUrl = new URL(pluginConfig.getJarRemoteUrl());
            //获取系统类加载器
            URLClassLoader jarclassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
            //遍历所有jar包url比较是否已经加载过插件
            URL[] urLs = jarclassLoader.getURLs();
            for (URL url : urLs) {
                if (targetUrl.equals(url)) {
                    out.println("jar包已经加载过了");
                    isLoad = true;
                    break;
                }
            }
            //没有加载过插件时
            if (!isLoad) {
                //加载jar包
                Method addurl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                addurl.setAccessible(true);
                addurl.invoke(jarclassLoader, targetUrl);
            }
            if (cachePlugins.containsKey(pluginConfig.getName())) {
                return cachePlugins.get(pluginConfig.getName());
            }
            //加载jar包中路径为pluginConfig.getClassName()的类,生成class文件
            Class<?> pluginClass = jarclassLoader.loadClass(pluginConfig.getClassName());
            //通过class生成实例对象
            plugin = (Advice) pluginClass.newInstance();
            cachePlugins.put(pluginConfig.getName(), plugin);

        } catch (MalformedURLException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return plugin;
    }

到此激活插件代码就完成了,做测试只需编写controller调用此方法就行了
取消插件代码类似,完整代码链接 https://gitcode.net/qq_42875345/rebacha-master

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小咸鱼的技术窝

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值