赞
踩
现在有这么一个需求:就是我们日志的开与关是交给使用人员来控制的,而不是由我们开发人员固定写死的。大家都知道可以用aop来实现日志管理,但是如何动态的来实现日志管理呢?aop源码中的实现逻辑中有这么一个步骤,就是会依次扫描Advice的实现类,然后执行。我们要做的就是自定义一个advice的实现类然后,在用户想要开启日志的时候就把advice加到项目中来,关闭日志的时候就把advice剔除就行了。好了知道思路了那么开始敲代码吧;
新建一个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() {
}
}
利用maven命令把插件打成jar包,这个jar包就是我们的插件。打包过程:win+r输入cmd回车,进入dos窗口切换到我们的插件项目如下,输入mvn clean package回车,jar包就打包好了。或者直接使用 IDEA 打包
删除plugin-0.0.1-SNAPSHOT.jar把plugin-0.0.1-SNAPSHOT.jar.original的这个jar包改个名字为plugin-0.0.1-SNAPSHOT.jar
复制jar到我们的测试目录,我这里是在d盘下的com下的zzh中
另外新建一个项目,名字为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("插件添加失败"); } } }
通过类加载器,通过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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。