现在有这么一个需求:就是我们日志的开与关是交给使用人员来控制的,而不是由我们开发人员固定写死的。大家都知道可以用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