首页 > 其他分享 >利用aop实现热拔插(类似于插件)

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

时间:2024-01-20 22:03:05浏览次数:32  
标签:插件 pluginConfig plugin jar 热拔 bean 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 打包

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


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

3:找到jar包

删除plugin-0.0.1-SNAPSHOT.jar把plugin-0.0.1-SNAPSHOT.jar.original的这个jar包改个名字为plugin-0.0.1-SNAPSHOT.jar

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

复制jar到我们的测试目录,我这里是在d盘下的com下的zzh中

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

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

另外新建一个项目,名字为pluginUse

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

编写我们的插件配置信息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


标签:插件,pluginConfig,plugin,jar,热拔,bean,aop,加载
From: https://blog.51cto.com/u_16414043/9346321

相关文章

  • spring--AOP通知类型有哪些
    SpringAOP(Aspect-OrientedProgramming,面向切面编程)提供了五种类型的通知(advice),这些通知定义了切面(aspect)是在目标对象的方法执行的哪个点被应用。以下是这五种通知类型:前置通知(Beforeadvice):在目标方法执行之前执行的通知,无法阻止方法的继续执行(除非它抛出一个异常)。后置......
  • spring--AOP的实现原理
    SpringAOP(面向切面编程)是Spring框架的一个关键组成部分,它提供了一种将横切关注点(如日志记录、事务管理、安全等)与业务逻辑分离的方法。SpringAOP的实现主要依赖于代理模式,以下是其工作原理的简要概述:代理模式:SpringAOP通过创建目标对象的代理来应用横切逻辑。这些代......
  • 下一代云原生网关Higress:基于Wasm开发JWT认证插件
    什么是HigressHigress是基于阿里内部的EnvoyGateway实践沉淀、以开源Istio+Envoy为核心构建的下一代云原生网关,实现了流量网关+微服务网关+安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不打......
  • AOP 编程
    AOP编程目录AOP编程1.AOP和OOP2.AOP中的一些概念术语通知类型3.AOP实现方式4.AOP使用1.添加依赖:在项目的pom.xml文件中添加SpringAOP依赖,以确保AOP模块可用。2.创建切面类:创建一个Java类,并使用@Aspect注解标记它,这个类将充当切面。1.AOP和OOPAOP为AspectOrie......
  • Spring AOP原来是这样实现的
    SpringAOP技术实现原理在Spring框架中,AOP(面向切面编程)是通过代理模式和反射机制来实现的。本文将详细介绍SpringAOP的技术实现原理,包括JDK动态代理和CGLIB代理的使用,并通过实例演示其在实际项目中的应用。1.AOP的实现原理概述SpringAOP的实现基于代理模式,通过代理对象来包......
  • 金蝶云星空创建表单插件项目
     一、新建一个空的解决方案  二、新建一个类库     三、添加引用添加金蝶安装目录的引用,如果是协同开发,那就是使用工作空间下的dll。本地金蝶云安装目录:C:\ProgramFiles(x86)\Kingdee\K3Cloud\WebSite\bin协同开发工作目录:D:\WorkSpace\XXXX\mm_k3Cloud\K3......
  • centOS7安装GLPI 和fusioninventory插件
    centOS7安装GLPI,一、关闭防火墙(不关闭只能本机访问):systemctlstopfirewalld.service#停止firewalld服务systemctldisablefirewalld.service#设置开机默认不启动#生产环境单独在防火墙上开启端口和策略#firewall-cmd--permanent--zone=public--add-port=80/tcp#firewa......
  • DB107-ASEMI插件小方桥DB107
    编辑:llDB107-ASEMI插件小方桥DB107型号:DB107品牌:ASEMI正向电流(Id):1A反向耐压(VRRM):1000V正向浪涌电流:50A正向电压(VF):1.05V引脚数量:4芯片个数:4芯片尺寸:50MIL功率(Pd):小功率设备封装:DB-4工作温度:-55°C~150°C类型:插件、小方桥、小电流DB107描述:ASEMI品牌DB107是采用工......
  • SOLIDWORKS插件SolidKits.BOMs工具之属性修改
    SOLIDWORKS模型的属性信息可以写在自定义属性中,也可以写在配置特定属性中,这些我们在制作SOLIDWORKS模板的时候就已经定义好了,如果随着企业的发展,属性名做了调整,就可以使用SOLIDWORKS属性修改插件-SolidKits.BOMs工具来批量完成属性名的修改,比如将代号改为图号,将备注属性删除等操作......
  • Spring Boot入坑-AOP、Interceptor和Filter
    AOP概述面向切面编程(AspectOrientedProgramming),从不同的维度或角度给已有程序添加附加功能的一种技术实现的方式是Spring容器为程序创建代理,让调用方无感知的调用指定方法,在运行期间动态的“织入”其他逻辑主要目的是为了解耦弥补OOP中只能继承类或实现接口进行功......