# mugen
**Repository Path**: llyb120/mugen
## Basic Information
- **Project Name**: mugen
- **Description**: 插件引擎,允许基于内核规则编写自己的插件部署,并对已上线的插件进行热更新或热修复,为开发提供无限的可能。
- **Primary Language**: Java
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 4
- **Forks**: 2
- **Created**: 2021-10-24
- **Last Updated**: 2025-01-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# M·U·G·E·N
#### 介绍
插件引擎,允许基于内核规则编写自己的插件部署,并对已上线的插件进行热更新或热修复,为开发提供无限的可能。
#### 快速开始
1. pom文件中加入远程仓库地址
```xml
gitee
https://gitee.com/llyb120/commonlib/raw/master/
```
2. pom文件中加入依赖
```xml
com.github.llyb120
mugen
0.0.1
```
3. pom文件中加入编译插件
```xml
org.apache.maven.plugins
maven-compiler-plugin
3.3
1.8
1.8
-parameters
```
4. 初始化一个PluginRunner
```java
PluginOption option = PluginOption
.builder()
//插件所工作的目录,该目录下的所有文件都会被当做插件加载
.pluginDir("D:\\work\\mugen\\target\\test-classes\\com\\github\\llyb120\\mugen\\test")
//插件文件的相对于插件目录的基础包名
.basePackage("com.github.llyb120.mugen.test")
.build();
PluginRunner runner = new PluginRunner(option);
```
5. 书写一个简单的插件
在类名上添加@Plugin注解,在需要对外暴露的方法中添加@Executable注解,如下是将foo方法暴露为一个名字为foo的插件入口
```java
@Plugin
public class Test {
@Executable("foo")
public void foo(){
System.out.println("bar");
}
}
```
6. 运行插件
```java
runner.execute("foo"); //will print "bar"
```
#### 加载器
针对不同类型的插件,可以自定义加载器进行加载,例如对加密插件实现解密功能,或者对插件进行licence授权。
加载器需实现PluginFileLoader接口,该接口包含两个方法,match指明了何时使用该加载器,analyze返回解析后的结果。
以下是一个zip插件的简单实现
```java
public class ZipFileLoader implements PluginFileLoader{
@Override
public boolean match(String extName) {
return extName.equals("zip");
}
@Override
public Map analyze(PluginOption option, List files) {
Map ret = new HashMap<>();
try{
for (File file : files) {
@Cleanup FileInputStream fis = new FileInputStream(file);
ZipInputStream zin = new ZipInputStream(fis);
ZipEntry entry = null;
while((entry=zin.getNextEntry())!=null){
if(entry.isDirectory()||entry.getName().equals("..\\"))
continue;
if(!entry.getName().endsWith(".class")){
continue;
}
String className = option.getBasePackage() + "." + (entry.getName())
.replace(".class", "")
.replaceAll("\\\\|/", ".");;
byte[] bytes = IoUtil.readBytes(zin, false);
ByteClass byteclass = ByteClass.builder()
.className(className)
.bytes(bytes)
.build();
ret.put(byteclass.getClassName(), byteclass);
}
}
} catch (Exception e){
e.printStackTrace();
}
return ret;
}
}
```
在构建PluginOption的时候添加该加载器
```java
PluginOption option = PluginOption
.builder()
.pluginDir("D:\\work\\mugen\\target\\test-classes\\com\\github\\llyb120\\mugen\\test")
.basePackage("com.github.llyb120.mugen.test")
.loader(new ZipFileLoader())
.build();
```
#### 依赖注入
使用@Resource注解可在插件类中注入需要的bean,大体功能同spring的@Resource。
```java
@Plugin
public class Test {
@Resource
PluginRunner runner; //default bean
@Resource
String test; //abc
@Executable("foo")
public void foo(){
System.out.println("bar");
}
}
```
需要注入的bean可在PluginOption中使用inject进行配置,可以对指定类型进行注入,如果类型出现冲突,则可以指定名称进行注入。
```java
PluginOption option = PluginOption
.builder()
.pluginDir("D:\\work\\mugen\\target\\test-classes\\com\\github\\llyb120\\mugen\\test")
.basePackage("com.github.llyb120.mugen.test")
.inject("test", "abc")
.inject(Test.class, new Test())
.build();
```
#### 链路器/锚点
链路器会追踪@Executable的调用链,使用者可以在调用链上插入别的方法,只需要在方法上添加@Hook即可
```java
@Executable("/test/t1")
public void t1(int a){
//do anything
t2();
}
public void t2(){
//do anything
t3();
}
@Executable("/test/c3")
public void t3(){
//do anything
}
@Hook("/test/c1->*->!/test/c3") //从/test/c1开始,经过任何调用,到/test/c3之前,!只能存在于最后,表示调用之前
//@Hook("/test/c1->*->/test/c3") //从/test/c1开始,经过任何调用,到/test/c3之后
//@Hook("/test/c1->/test/c3") //从/test/c1开始,到/test/c3之后
//@Hook("*->/test/c3") //从任何步骤开始,到/test/c3之后
//@Hook("/test/c1->*") //从/test/c1开始,调用任何步骤
public void test(PluginChainContext context){
//读取当时上下文中的a参数
int olda = (int) context.getParameters().get("a");
//修改该参数,并继续调用链
context.getParameters().put("a", olda + 100);
}
```
#### 自定义链路器
实现PluginTracker接口可自定义链路器
```java
public class DefaultTracker implements PluginTracker{
private volatile Map patternMap = new ConcurrentHashMap<>();
@Override
public boolean canTrace(String hookValue, String str) {
Pattern p = patternMap.computeIfAbsent(hookValue, k -> {
return Pattern.compile("^" + hookValue.replace("*->", "(.+->)?").replace("*", "((?!!).+)") + "$");
});
return p.matcher(str).find();
}
}
```
将其注册到PluginOption中
```java
option.tracker("custom", new DefaultTracker());
```
在@Hook中指定自定义的链路器
```java
@Plugin
public class Test {
@Executable("foo")
public void foo(){
System.out.println("bar");
}
@Hook(value = "*->!foo", tracker = "custom")
public void test(){
System.out.println(123);
}
}
```
#### 热响应
如果有新插件被添加,或是修复旧插件的bug,只需要调用refresh方法即可,无需重启应用
```java
runner.refresh()
```