MyBatis-Plus结合Swagger实现接口代码及文档自动生成工具(终极篇-插件化工具)
插件(Plugin)是什么不用多说。常用的软件,例如Eclipse、Photoshop、VisualStudio,都支持插件扩展。插件可以动态给软件添加一些功能,也可以随时删除,这样的好处是任何人都可以给这个软件进行功能上的扩展,而不用去改软件本身的代码。
比如需要开发一个系统,用来将一些有数据推送给客户,至于是什么数据不是重点。有三个客户:A客户需要把数据组织成一个xml格式的文件,通过FTP上传到客户服务器上;B客户需要把数据组织成一个json,通过HTTP请求提交;C客户希望生成一个Excel文件再通过E-mail发送…以后可能还会有更多的客户,也还会有更多操蛋的需求。
对于这样一个系统的开发,如果使用普通的方式开发,那么每增加一个客户就要修改一次系统代码,在代码中增加一个针对某个客户的功能,很不灵活。如果再减少一个客户,那么其对应的代码也就没有用了,是不是要删除掉又成了问题。
以上只是一个例子,在实际开发中经常会有类似的情形,此时使用插件化的方式会更灵活。
遇到这种情况,可以把数据的获取和整理这块和客户无关的逻辑放在主程序中,而主程序提供一个客户推送的接口,接口定义一个未实现的抽象方法“推送数据”,这个方法由各个客户对应的插件来实现。这样新增一个客户需求,不需要修改主程序的代码,只需要实现这个接口就行,插件写好打成jar包放在指定目录下,再配置一下,主程序就可以使用这个插件了。当不需要这个插件,也可以通过配置来去掉它。
上面说到主程序可以通过配置来动态添加和删除插件,配置的方式一般有两种:XML或数据库,二者选其一即可。
主程序可以通过一个xml配置文件,动态配置插件。
代码自动生成插件
D:\\mvnrepository\\com\\lyh\\autoCodePluginImpl\\0.0.1-SNAPSHOT\\autoCodePluginImpl-0.0.1-SNAPSHOT.jar
com.lyh.plugin.AutoCodePlugin
D:\\bootspaceq\\autoCode\\src\\main\\java\\com\\zte\\model
D:\\bootspaceq\\autoCode\\target\\classes
D:\\bootspaceq\\autoCode\\src\\main\\java\\com\\zte\\rest
D:\\bootspaceq\\autoCode\\src\\main\\java\\com\\zte\\service
D:\\bootspaceq\\autoCode\\target\\classes\\com\\zte\\model\\
jdbc:sqlserver://11.1.11.50:9143;DatabaseName=mobile
com.microsoft.sqlserver.jdbc.SQLServerDriver
0000
00000
主程序通过解析这个XML来调用插件,
构建插件工程autoCodePlugin:
新建参数类PluginParamsConfig
package com.zte.lyh;
import java.io.Serializable;
public class PluginParamsConfig implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1246187772426993272L;
// 生成的bean路径
private String beanPath;
// 编译路径
private String targetPath;
// 生成接口路径包
private String restPath;
// 生成的service路径
private String servicePath;
// 反射获取bean的calss文件路径
private String classPath;
private String name;
private String jar;
private String className;
private String url;
private String password;
private String user;
private String driver;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJar() {
return jar;
}
public void setJar(String jar) {
this.jar = jar;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getBeanPath() {
return beanPath;
}
public void setBeanPath(String beanPath) {
this.beanPath = beanPath;
}
public String getTargetPath() {
return targetPath;
}
public void setTargetPath(String targetPath) {
this.targetPath = targetPath;
}
public String getRestPath() {
return restPath;
}
public void setRestPath(String restPath) {
this.restPath = restPath;
}
public String getServicePath() {
return servicePath;
}
public void setServicePath(String servicePath) {
this.servicePath = servicePath;
}
public String getClassPath() {
return classPath;
}
public void setClassPath(String classPath) {
this.classPath = classPath;
}
}
提供一个接口来提供给插件开发者来实现:
package com.zte.lyh;
public interface PluginService {
public void service(PluginParamsConfig p);
}
上面是一个接口,包含一个未实现的方法service(),这个方法即和客户相关的逻辑,由插件来实现。
解析XML获取所有插件信息(这里用到dom4j):
package com.zte.lyh;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class XMLParser {
public List getPluginList() throws DocumentException {
List list = new ArrayList();
String path = this.getClass().getResource("/").getPath();
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File(path + "plugin.xml"));
Element root = document.getRootElement();
List plugins = root.elements("plugin");
for (Element pluginObj : plugins) {
Element pluginEle = pluginObj;
PluginParamsConfig plugin = new PluginParamsConfig();
plugin.setName(pluginEle.elementText("name"));
plugin.setJar(pluginEle.elementText("jar"));
plugin.setClassName(pluginEle.elementText("class"));
Element prop = pluginEle.element("properties");
List properties=prop.elements("property");
properties.stream().forEach(p -> {
String name = p.attributeValue("name");
String value = p.getText();
System.out.println("name:"+name+"---value"+value);
if ("beanPath".equals(name)) {
plugin.setBeanPath(value);
}
if ("classPath".equals(name)) {
plugin.setClassPath(value);
}
if ("targetPath".equals(name)) {
plugin.setTargetPath(value);
}
if ("connectionURL".equals(name)) {
plugin.setUrl(value);
}
if ("servicePath".equals(name)) {
plugin.setServicePath(value);
}
if ("restPath".equals(name)) {
plugin.setRestPath(value);
}
if ("password".equals(name)) {
plugin.setPassword(value);
}
if ("driverClass".equals(name)) {
plugin.setDriver(value);
}
if ("userId".equals(name)) {
plugin.setUser(value);
}
});
list.add(plugin);
}
return list;
}
}
使用URLClassLoader动态加载jar文件,实例化插件中的对象:
package com.zte.lyh;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
public class PluginManager {
private URLClassLoader urlClassLoader;
public PluginManager(List plugins) throws MalformedURLException {
init(plugins);
}
private void init(List plugins) throws MalformedURLException {
int size = plugins.size();
URL[] urls = new URL[size];
for(int i = 0; i < size; i++) {
PluginParamsConfig plugin = plugins.get(i);
String filePath = plugin.getJar();
urls[i] = new URL("file:" + filePath);
}
// 将jar文件组成数组,来创建一个URLClassLoader
urlClassLoader = new URLClassLoader(urls);
}
public PluginService getInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// 插件实例化对象,插件都是实现PluginService接口
Class> clazz = urlClassLoader.loadClass(className);
Object instance = clazz.newInstance();
return (PluginService)instance;
}
}
至此插件主程序工程构建完毕,将它打成jar包。
新建插件实现工程autoCodePluginImpl:
将上一步打好的插件jar包引入到该工程,实现插件接口:pluginservice
package com.lyh.plugin;
import java.io.IOException;
import java.sql.SQLException;
import com.lyh.util.AutoCreateBeanUtil;
import com.lyh.util.AutoCreateRestAndServiceUtil;
import com.zte.lyh.PluginParamsConfig;
import com.zte.lyh.PluginService;
import freemarker.template.TemplateException;
public class AutoCodePlugin implements PluginService {
@Override
public void service(PluginParamsConfig p) {
try {
AutoCreateBeanUtil.autoCreateAnntation(p);
AutoCreateRestAndServiceUtil.createRest(p);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TemplateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
然后将前面文章实现的代码自动化工程的代码迁移到插件实现工程其实相对于前面工程只是把main方法替换成了,实现插件的方法,在该方法中调用前面的自动化代码生成方法就实现了前面所说的功能,至此自动化生成代码工具插件已经全部完成,下面就是在你要应用插件的地方调用该插件就行。
package com.zte;
import java.util.List;
import com.zte.lyh.PluginManager;
import com.zte.lyh.PluginParamsConfig;
import com.zte.lyh.PluginService;
import com.zte.lyh.XMLParser;
public class AutoCodePluginTest{
public static void main(String[] args) {
try {
XMLParser xp=new XMLParser();
List pluginList =xp.getPluginList();
PluginManager pluginManager = new PluginManager(pluginList);
for(PluginParamsConfig plugin : pluginList) {
PluginService pluginService = pluginManager.getInstance(plugin.getClassName());
System.out.println("开始执行[" + plugin.getName() + "]插件...");
// 调用插件
pluginService.service(plugin);
System.out.println("[" + plugin.getName() + "]插件执行完成");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
插件定义源码
插件实现原源码
插件测试源码