有点晚了,空了就写,会把整个开发过程的源码贴上的
----------------------------以上是原内容-----------------------------
时隔快一年了,每天有太多的砖要搬,差点把这里的事情太监。现在趁着国庆陪在父母身边,陪他们看电视的时候,把这个账还了。
不过经过这段时间的体会,个人建议大家采用osgi,毕竟这个方案不支持热插拔。
--------------------------------以下正文--------------------------------
承接上文,进入编码阶段。由于源码为去年编写,此篇内容可能与实际源码有出入,在这些地方我会以红色斜体字说明。
所有源码我皆从github拷贝:https://github.com/zhengdaone/spring-plugin-cli
主程序
主程序主要包涵配置读取、类加载进classLoader、spring托管三个部分。
1、配置读取
读取配置这一部分,我通过数据库直接实现,如果各位需要更灵活,建议自行定义接口。
数据库结构
名称 | 类型 | 长度 | 是否为null | 是否主键 | 备注 |
id | int | 4 | N | Y | |
bootClass | varchar | 200 | Y | N | spring初始化结束后,插件进行初始化的类。 示例:xxx.xxx.xxx.XxxBootClass |
name | varchar | 50 | N | N | 插件名不为空是为了输出插件的加载情况 |
url | varchar | 500 | N | N | 插件路径。 示例:F:\xxx\xxx\xxx.jar |
scanPath | varchar | 500 | N | N | 需要spring进行管理的类的包。 示例:xxx/xxx/xxx |
isEnable | bit | 1 | N | N | 是否启用插件 |
插件配置实体
package com.pinyi.mian;
import java.util.Arrays;
import java.util.List;
public class Plugin {
private Integer id;
private String name;
private String url;
private String bootClass;
private String scanPath;
private String config;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getBootClass() {
return bootClass;
}
public void setBootClass(String bootClass) {
this.bootClass = bootClass;
}
public List<String> getScanPath() {
return Arrays.asList(scanPath.split(","));
}
public void setScanPath(String scanPath) {
this.scanPath = scanPath;
}
public String getConfig() {
return config;
}
public void setConfig(String config) {
this.config = config;
}
}
isEnable字段是没有的,需自行添加。
根据实际情况,config字段可移除。
JDBCManager
package com.pinyi.mian;
import java.sql.*;
public class JDBCManager {
public static String url;
public static String username;
public static String password;
public static final String driver = "com.mysql.cj.jdbc.Driver";
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
throw new RuntimeException("加载数据库驱动失败");
}
}
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
throw new RuntimeException("建立数据库连接失败");
}
}
public static void closeConnection(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
try {
if (resultSet != null) resultSet.close();
if (preparedStatement != null) preparedStatement.close();
if (connection != null) connection.close();
} catch (SQLException e) {
throw new RuntimeException("建立数据库连接失败");
}
}
}
建立JDBCManager的原因如下:
1)我采用数据库实现
2)此时spring还未启动结束,无法使用jpa
此类未将异常堆栈输出,请自行修改代码。
配置读取
private List<Plugin> getPlugins() {
Connection connection = JDBCManager.getConnection();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
List<Plugin> pluginList = new ArrayList<>();
try {
String sql = "select * from plugin";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
Plugin plugin = new Plugin();
plugin.setId(resultSet.getInt("id"));
plugin.setBootClass(resultSet.getString("boot_class"));
plugin.setName(resultSet.getString("name"));
plugin.setUrl(resultSet.getString("url"));
plugin.setScanPath(resultSet.getString("scan_path"));
pluginList.add(plugin);
}
} catch (SQLException e) {
throw new RuntimeException("从数据库获取插件配置失败");
} finally {
JDBCManager.closeConnection(resultSet, preparedStatement, connection);
}
return pluginList;
}
这段代码在类com.pinyi.mian.PluginManager中。
isEnable字段未读取,需自行添加。
2、类加载进classloader
恰饭了,剩余内容参见:https://blog.csdn.net/ZhengDa_LY/article/details/102067614