MyBatis-Plus結合Swagger實現接口代碼及文檔自動生成工具(終極篇-插件化工具)
插件(Plugin)是什麼不用多說。常用的軟件,例如Eclipse、Photoshop、VisualStudio,都支持插件擴展。插件可以動態給軟件添加一些功能,也可以隨時刪除,這樣的好處是任何人都可以給這個軟件進行功能上的擴展,而不用去改軟件本身的代碼。
適用場景
比如需要開發一個系統,用來將一些有數據推送給客戶,至於是什麼數據不是重點。有三個客戶:A客戶需要把數據組織成一個xml格式的文件,通過FTP上傳到客戶服務器上;B客戶需要把數據組織成一個json,通過HTTP請求提交;C客戶希望生成一個Excel文件再通過E-mail發送…以後可能還會有更多的客戶,也還會有更多操蛋的需求。
對於這樣一個系統的開發,如果使用普通的方式開發,那麼每增加一個客戶就要修改一次系統代碼,在代碼中增加一個針對某個客戶的功能,很不靈活。如果再減少一個客戶,那麼其對應的代碼也就沒有用了,是不是要刪除掉又成了問題。
以上只是一個例子,在實際開發中經常會有類似的情形,此時使用插件化的方式會更靈活。
遇到這種情況,可以把數據的獲取和整理這塊和客戶無關的邏輯放在主程序中,而主程序提供一個客戶推送的接口,接口定義一個未實現的抽象方法“推送數據”,這個方法由各個客戶對應的插件來實現。這樣新增一個客戶需求,不需要修改主程序的代碼,只需要實現這個接口就行,插件寫好打成jar包放在指定目錄下,再配置一下,主程序就可以使用這個插件了。當不需要這個插件,也可以通過配置來去掉它。
主程序配置插件
上面說到主程序可以通過配置來動態添加和刪除插件,配置的方式一般有兩種:XML或數據庫,二者選其一即可。
xml
主程序可以通過一個xml配置文件,動態配置插件。
<?xml version="1.0" encoding="UTF-8"?>
<plugins>
<plugin>
<name>代碼自動生成插件</name>
<jar>D:\\mvnrepository\\com\\lyh\\autoCodePluginImpl\\0.0.1-SNAPSHOT\\autoCodePluginImpl-0.0.1-SNAPSHOT.jar</jar>
<class>com.lyh.plugin.AutoCodePlugin</class>
<properties>
<property name="beanPath">D:\\bootspaceq\\autoCode\\src\\main\\java\\com\\zte\\model</property>
<property name="targetPath">D:\\bootspaceq\\autoCode\\target\\classes</property>
<property name="restPath">D:\\bootspaceq\\autoCode\\src\\main\\java\\com\\zte\\rest</property>
<property name="servicePath">D:\\bootspaceq\\autoCode\\src\\main\\java\\com\\zte\\service</property>
<property name="classPath">D:\\bootspaceq\\autoCode\\target\\classes\\com\\zte\\model\\</property>
<property name="connectionURL">jdbc:sqlserver://11.1.11.50:9143;DatabaseName=mobile</property>
<property name="driverClass">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="password">0000</property>
<property name="userId">00000</property>
</properties>
</plugin>
</plugins>
主程序通過解析這個XML來調用插件,<plugin>元素即一個插件,可以通過添加和刪除<plugin>元素來動態的添加和刪除插件。<name>是插件名稱,<jar>是插件jar文件所在的路徑,<class>是插件實現主程序接口的類。
構建插件工程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<PluginParamsConfig> getPluginList() throws DocumentException {
List<PluginParamsConfig> list = new ArrayList<PluginParamsConfig>();
String path = this.getClass().getResource("/").getPath();
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File(path + "plugin.xml"));
Element root = document.getRootElement();
List<Element> 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<Element> 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<PluginParamsConfig> plugins) throws MalformedURLException {
init(plugins);
}
private void init(List<PluginParamsConfig> 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<PluginParamsConfig> 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();
}
}
}