Browse Source

DataUpgrade updates.

deason 3 months ago
parent
commit
c86af36d40

+ 1 - 1
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/annotation/DataUpgradeVersion.java

@@ -19,7 +19,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
 public @interface DataUpgradeVersion {
 
     /**
-     * 匹配版本
+     * 匹配版本号,格式如:1.0.0
      */
     String value();
 

+ 25 - 0
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/config/DataUpgradeConstants.java

@@ -0,0 +1,25 @@
+package com.qmth.boot.data.upgrade.config;
+
+public interface DataUpgradeConstants {
+
+    String ENABLE = "com.qmth.data.upgrade.enable";
+
+    String BASE_PACKAGE = "com.qmth.data.upgrade.base-package";
+
+    String FIRST_INSTALL = "com.qmth.data.upgrade.first-install";
+
+    String SUPER_DB_USERNAME = "com.qmth.data.upgrade.super-username";
+
+    String SUPER_DB_PASSWORD = "com.qmth.data.upgrade.super-password";
+
+    String DB_USERNAME = "com.qmth.datasource.username";
+
+    String DB_PASSWORD = "com.qmth.datasource.password";
+
+    String DB_URL = "com.qmth.datasource.url";
+
+    String APP_CODE = "com.qmth.solar.app-code";
+
+    String APP_VERSION = "com.qmth.solar.app-version";
+
+}

+ 158 - 104
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/config/DataUpgradeListener.java

@@ -1,25 +1,26 @@
 package com.qmth.boot.data.upgrade.config;
 
 import com.qmth.boot.data.upgrade.annotation.DataUpgradeVersion;
+import com.qmth.boot.data.upgrade.model.BootAppInfo;
+import com.qmth.boot.data.upgrade.service.DataInitService;
 import com.qmth.boot.data.upgrade.service.DataUpgradeService;
+import com.qmth.boot.data.upgrade.utils.ClassHelper;
 import com.qmth.boot.data.upgrade.utils.VersionComparator;
 import com.zaxxer.hikari.HikariDataSource;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.boot.context.event.ApplicationPreparedEvent;
-import org.springframework.boot.jdbc.DatabaseDriver;
 import org.springframework.context.ApplicationListener;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
 import org.springframework.transaction.support.TransactionTemplate;
 import org.springframework.util.ClassUtils;
 
 import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -31,78 +32,140 @@ import java.util.Map;
  * @author: Deason
  * @since: 2025/3/3
  */
-public class DataUpgradeListener implements ApplicationListener<ApplicationPreparedEvent> {
+public class DataUpgradeListener implements ApplicationListener<ApplicationPreparedEvent>, DataUpgradeConstants {
 
     private static final Logger log = LoggerFactory.getLogger(DataUpgradeListener.class);
 
-    private static final String DB_URL = "com.qmth.datasource.url";
+    private DataUpgradeProperties properties;
 
-    private static final String DB_USERNAME = "com.qmth.datasource.username";
+    @Override
+    public void onApplicationEvent(ApplicationPreparedEvent event) {
+        try {
+            properties = new DataUpgradeProperties(event.getApplicationContext().getEnvironment());
+            if (!properties.getEnable()) {
+                // 未启用时,不再进行后续处理
+                return;
+            }
 
-    private static final String DB_PASSWORD = "com.qmth.datasource.password";
+            if (properties.getFirstInstall()) {
+                // 首次安装时,仅执行数据初始化
+                this.handlerDataInit();
+            } else {
+                // 否则执行数据升级
+                this.handlerDataUpgrade();
+            }
+        } catch (Exception e) {
+            log.error("数据操作异常终止!原因:{}", e.getMessage(), e);
+            System.exit(1);
+        }
+    }
 
-    private static final String APP_VERSION = "com.qmth.solar.app-version";
+    public void handlerDataInit() {
+        try (HikariDataSource dataSource = this.initDataSource()) {
+            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
 
-    private static final String BASE_PACKAGE = "com.qmth.data.upgrade.base-package";
+            log.warn("待初始化应用码:{} 待初始化应用版本号:{}", properties.getAppCode(), properties.getAppVersion());
+            if (this.existBootAppInfo(jdbcTemplate)) {
+                throw new RuntimeException("【应用信息表】已存在,不允许重复数据初始化!");
+            }
 
-    @Override
-    public void onApplicationEvent(ApplicationPreparedEvent event) {
-        ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
-        try (HikariDataSource dataSource = this.initDataSource(environment)) {
+            List<DataInitService> services = ClassHelper.findInterfaceInstances(DataInitService.class, properties.getBasePackage());
+            if (services.isEmpty()) {
+                log.warn("未找到数据初始化的具体实现!{}:{}", BASE_PACKAGE, properties.getBasePackage());
+                throw new RuntimeException("未找到数据初始化的具体实现!");
+            }
+
+            if (services.size() > 1) {
+                throw new RuntimeException("不允许存在多个数据初始化的具体实现!");
+            }
+
+            log.warn("*************** data init start ***************");
+            try {
+                services.get(0).process(jdbcTemplate);
+            } catch (Exception e) {
+                throw new RuntimeException("SQL语句执行异常!", e);
+            }
+
+            // 数据初始化完成后,初始应用信息表
+            this.initBootAppInfo(jdbcTemplate, properties.getAppCode(), properties.getAppVersion());
+            log.warn("*************** data init finish ***************");
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    public void handlerDataUpgrade() {
+        try (HikariDataSource dataSource = this.initDataSource()) {
             JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
             TransactionTemplate transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(dataSource));
 
-            String appVersion = environment.getProperty(APP_VERSION, String.class);
-            if (StringUtils.isBlank(appVersion)) {
-                throw new RuntimeException(APP_VERSION + " 配置值不能为空!");
+            if (!this.existBootAppInfo(jdbcTemplate)) {
+                throw new RuntimeException("【应用信息表】不存在!");
+            }
+
+            BootAppInfo appInfo = this.queryBootAppInfo(jdbcTemplate, properties.getAppCode());
+            if (appInfo == null) {
+                throw new RuntimeException("【应用信息表】中未找到匹配的数据!appCode:" + properties.getAppCode());
             }
 
-            // 检查当前版本是否需要数据升级
-            String currentVersion = this.queryCurrentVersion(jdbcTemplate);
-            if (new VersionComparator().compare(currentVersion, appVersion) >= 0) {
-                log.warn("跳过数据升级!currentVersion:{} appVersion:{}", currentVersion, appVersion);
+            log.warn("待升级应用码:{} 待升级应用版本号:{} 当前应用码:{} 当前应用版本号:{}", properties.getAppCode(),
+                    properties.getAppVersion(), appInfo.getAppCode(), appInfo.getAppVersion());
+
+            int compareValue = new VersionComparator().compare(appInfo.getAppVersion(), properties.getAppVersion());
+            if (compareValue > 0) {
+                throw new RuntimeException("待升级的版本号不能低于当前版本号!");
+            }
+            if (compareValue == 0) {
+                log.warn("跳过数据升级,版本号一致!");
                 return;
             }
 
-            String basePackage = environment.getProperty(BASE_PACKAGE, String.class);
-            if (StringUtils.isBlank(basePackage)) {
-                basePackage = "com.qmth";// 未配置扫描包名,赋默认值
+            Map<String, DataUpgradeService> serviceMaps = this.matchServices(appInfo.getAppVersion(), properties.getAppVersion());
+            if (serviceMaps.isEmpty()) {
+                log.warn("未找到数据升级的具体实现!{}:{}", BASE_PACKAGE, properties.getBasePackage());
+                throw new RuntimeException("未找到数据升级的具体实现!");
             }
 
-            List<DataUpgradeService> services = this.matchServices(basePackage, currentVersion, appVersion);
-            if (!services.isEmpty()) {
-                log.warn("*************** data upgrade start ***************");
-                for (DataUpgradeService service : services) {
-                    service.process(jdbcTemplate, transactionTemplate);
+            // 按版本号由小到大排序
+            List<String> versionKeys = new ArrayList<>(serviceMaps.keySet());
+            versionKeys.sort(new VersionComparator());
+
+            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+                @Override
+                protected void doInTransactionWithoutResult(TransactionStatus status) {
+                    try {
+                        for (String version : versionKeys) {
+                            log.warn("*************** data upgrade version:{} start ***************", version);
+                            serviceMaps.get(version).process(jdbcTemplate);
+                        }
+                    } catch (Exception e) {
+                        // DML-SQL语句支持事务‌,执行失败时触发自动回滚。
+                        status.setRollbackOnly();
+                        // DDL-SQL语句不支持事务‌,执行失败时请采用其它补偿机制回滚!!!
+                        throw new RuntimeException("SQL语句执行异常!", e);
+                    }
                 }
-            } else {
-                log.warn("未找到数据升级的具体实现!{}:{}", BASE_PACKAGE, basePackage);
-            }
+            });
 
-            // 数据升级完成后修改为当前版本号
-            updateCurrentVersion(appVersion, jdbcTemplate);
+            // 数据升级完成后,更新应用信息表
+            this.updateBootAppInfo(jdbcTemplate, properties.getAppCode(), properties.getAppVersion());
             log.warn("*************** data upgrade finish ***************");
         } catch (Exception e) {
-            log.error("数据升级异常,终止运行。{}", e.getMessage(), e);
-            System.exit(1);
+            throw new RuntimeException(e.getMessage(), e);
         }
     }
 
     /**
-     * 获取数据升级的具体实现(按版本号由小到大排序、支持跨版本)
-     * 1、出现同个版本的重复实现,则异常终止
-     * 2、未扫描到任何实现 或 所有实现都与待升级目标版本的版本区间不匹配,则忽略
+     * 获取数据升级的具体实现集合
      */
-    private List<DataUpgradeService> matchServices(String basePackage, String currentVersion, String appVersion) {
-        List<Class<?>> implClasses = this.findInterfaceImpls(DataUpgradeService.class, basePackage);
+    private Map<String, DataUpgradeService> matchServices(String currentVersion, String appVersion) {
+        List<Class<?>> implClasses = ClassHelper.findInterfaceImpls(DataUpgradeService.class, properties.getBasePackage());
         if (implClasses.isEmpty()) {
-            return new ArrayList<>();
+            return new HashMap<>();
         }
 
         VersionComparator vc = new VersionComparator();
-        List<String> keys = new ArrayList<>();
         Map<String, DataUpgradeService> serviceMaps = new HashMap<>();
-
         for (Class<?> clazz : implClasses) {
             DataUpgradeVersion annotation = clazz.getAnnotation(DataUpgradeVersion.class);
             if (annotation == null) {
@@ -110,100 +173,91 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
                 continue;
             }
 
-            String value = annotation.value();
-            if (vc.compare(value, currentVersion) > 0 && vc.compare(value, appVersion) <= 0) {
-                // log.info("{} {}", clazz.getSimpleName(), value);
-                if (serviceMaps.containsKey(value)) {
-                    throw new RuntimeException("同个版本数据升级的实现出现重复!@DataUpgradeVersion:" + value);
+            String implVersion = annotation.value();
+            // 校验规则:只允许x.x.x格式的版本号 todo
+
+            if (vc.compare(implVersion, currentVersion) > 0 && vc.compare(implVersion, appVersion) <= 0) {
+                // log.info("{} {}", clazz.getSimpleName(), implVersion);
+                if (serviceMaps.containsKey(implVersion)) {
+                    throw new RuntimeException("同个版本数据升级的实现出现重复!@DataUpgradeVersion:" + implVersion);
                 }
 
                 try {
                     Constructor<?> constructor = ClassUtils.getConstructorIfAvailable(clazz);
                     if (constructor != null) {
-                        serviceMaps.put(value, (DataUpgradeService) constructor.newInstance());
-                        keys.add(value);
+                        serviceMaps.put(implVersion, (DataUpgradeService) constructor.newInstance());
                     }
                 } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
             }
         }
+        return serviceMaps;
+    }
 
-        List<DataUpgradeService> result = new ArrayList<>();
-        if (!keys.isEmpty()) {
-            // 按版本号由小到大排序
-            keys.sort(vc);
-            for (String key : keys) {
-                result.add(serviceMaps.get(key));
-            }
+    private void updateBootAppInfo(JdbcTemplate jdbcTemplate, String appCode, String appVersion) {
+        if (StringUtils.isBlank(appCode)) {
+            throw new RuntimeException("appCode的值不能为空!");
+        }
+        if (StringUtils.isBlank(appVersion)) {
+            throw new RuntimeException("appVersion的值不能为空!");
+        }
+        try {
+            jdbcTemplate.update("update boot_app_info set app_version = ? where app_code = ?", appVersion, appCode);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
-        return result;
     }
 
-    private String queryCurrentVersion(JdbcTemplate jdbcTemplate) {
+    private BootAppInfo queryBootAppInfo(JdbcTemplate jdbcTemplate, String appCode) {
         try {
-            jdbcTemplate.execute("create table if not exists app_version (current_version varchar(10) not null, unique key(current_version))");
-            String currentVersion = jdbcTemplate.queryForObject("select max(current_version) from app_version", String.class);
-            // 未定义时,默认值为0,即最小版本号从0开始
-            return StringUtils.isNotBlank(currentVersion) ? currentVersion : "0";
+            List<BootAppInfo> list = jdbcTemplate.query("select app_code,app_version from boot_app_info where app_code = ?",
+                    new String[]{appCode}, new BeanPropertyRowMapper<>(BootAppInfo.class));
+            return list.isEmpty() ? null : list.get(0);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    private void updateCurrentVersion(String appVersion, JdbcTemplate jdbcTemplate) {
+    private void initBootAppInfo(JdbcTemplate jdbcTemplate, String appCode, String appVersion) {
+        if (StringUtils.isBlank(appCode)) {
+            throw new RuntimeException("appCode的值不能为空!");
+        }
         if (StringUtils.isBlank(appVersion)) {
             throw new RuntimeException("appVersion的值不能为空!");
         }
-
         try {
-            jdbcTemplate.update("replace into app_version (current_version) values (?)", appVersion);
-            jdbcTemplate.update("delete from app_version where current_version != ?", appVersion);
+            String createTableSql = "create table if not exists boot_app_info (" +
+                    " app_code varchar(20) not null," +
+                    " app_version varchar(10) not null," +
+                    " unique key(app_code)" +
+                    ")";
+            jdbcTemplate.execute(createTableSql);
+
+            jdbcTemplate.update("insert into boot_app_info (app_code,app_version) values(?,?)", appCode, appVersion);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
-    private HikariDataSource initDataSource(ConfigurableEnvironment environment) {
-        String dbUrl = environment.getProperty(DB_URL, String.class);
-        if (StringUtils.isBlank(dbUrl)) {
-            throw new RuntimeException(DB_URL + " 配置值不能为空!");
-        }
-        String dbUsername = environment.getProperty(DB_USERNAME, String.class);
-        if (StringUtils.isBlank(dbUsername)) {
-            throw new RuntimeException(DB_USERNAME + " 配置值不能为空!");
-        }
-        String dbPassword = environment.getProperty(DB_PASSWORD, String.class);
-        if (StringUtils.isBlank(dbPassword)) {
-            throw new RuntimeException(DB_PASSWORD + " 配置值不能为空!");
+    private boolean existBootAppInfo(JdbcTemplate jdbcTemplate) {
+        try {
+            // 是否存在应用信息表:boot_app_info
+            List<String> exists = jdbcTemplate.queryForList("SHOW TABLES LIKE 'boot_app_info'", String.class);
+            return !exists.isEmpty();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
-        String driverClassName = DatabaseDriver.fromJdbcUrl(dbUrl).getDriverClassName();
+    }
 
+    private HikariDataSource initDataSource() {
         HikariDataSource dataSource = new HikariDataSource();
         dataSource.setPoolName("dataUpgradeDataSource");
-        dataSource.setDriverClassName(driverClassName);
-        dataSource.setJdbcUrl(dbUrl);
-        dataSource.setUsername(dbUsername);
-        dataSource.setPassword(dbPassword);
+        dataSource.setDriverClassName(properties.getDriverClassName());
+        dataSource.setJdbcUrl(properties.getDbUrl());
+        dataSource.setUsername(properties.getDbUsername());
+        dataSource.setPassword(properties.getDbPassword());
         return dataSource;
     }
 
-    private List<Class<?>> findInterfaceImpls(Class<?> interfaceClass, String basePackage) {
-        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(interfaceClass));
-        List<Class<?>> impls = new ArrayList<>();
-        scanner.findCandidateComponents(basePackage).forEach(bean -> {
-            try {
-                Class<?> clazz = ClassUtils.forName(bean.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-                // 排除接口和抽象类
-                if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
-                    impls.add(clazz);
-                }
-            } catch (Exception e) {
-                log.warn("{} class load fail. {}", bean.getBeanClassName(), e.getMessage());
-            }
-        });
-        return impls;
-    }
-
 }

+ 144 - 0
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/config/DataUpgradeProperties.java

@@ -0,0 +1,144 @@
+package com.qmth.boot.data.upgrade.config;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.jdbc.DatabaseDriver;
+import org.springframework.core.env.ConfigurableEnvironment;
+
+public class DataUpgradeProperties implements DataUpgradeConstants {
+
+    /**
+     * 是否启用数据升级处理,默认值为true
+     */
+    private Boolean enable;
+
+    /**
+     * 数据升级服务实现类所在位置的包名,默认扫描包名为:com.qmth
+     */
+    private String basePackage;
+
+    /**
+     * 是否首次安装初始化,默认值为false
+     */
+    private Boolean firstInstall;
+
+    /**
+     * DB用户名
+     */
+    private String dbUsername;
+
+    /**
+     * DB用户密码
+     */
+    private String dbPassword;
+
+    /**
+     * DB链接地址
+     */
+    private String dbUrl;
+
+    /**
+     * DB驱动名称
+     */
+    private String driverClassName;
+
+    /**
+     * 待升级应用码
+     */
+    private String appCode;
+
+    /**
+     * 待升级应用版本号
+     */
+    private String appVersion;
+
+    public DataUpgradeProperties(ConfigurableEnvironment environment) {
+        this.enable = environment.getProperty(ENABLE, Boolean.class, true);
+        if (!enable) {
+            // 未启用时,不再进行后续配置处理
+            return;
+        }
+
+        this.basePackage = environment.getProperty(BASE_PACKAGE, String.class, "com.qmth");
+        this.firstInstall = environment.getProperty(FIRST_INSTALL, Boolean.class, false);
+
+        this.appCode = environment.getProperty(APP_CODE, String.class);
+        if (StringUtils.isBlank(appCode)) {
+            throw new RuntimeException(APP_CODE + " 配置值不能为空!");
+        }
+
+        this.appVersion = environment.getProperty(APP_VERSION, String.class);
+        if (StringUtils.isBlank(appVersion)) {
+            throw new RuntimeException(APP_VERSION + " 配置值不能为空!");
+        }
+
+        this.dbUsername = environment.getProperty(DB_USERNAME, String.class);
+        if (StringUtils.isBlank(dbUsername)) {
+            throw new RuntimeException(DB_USERNAME + " 配置值不能为空!");
+        }
+
+        this.dbPassword = environment.getProperty(DB_PASSWORD, String.class);
+        if (StringUtils.isBlank(dbPassword)) {
+            throw new RuntimeException(DB_PASSWORD + " 配置值不能为空!");
+        }
+
+        this.dbUrl = environment.getProperty(DB_URL, String.class);
+        if (StringUtils.isBlank(dbUrl)) {
+            throw new RuntimeException(DB_URL + " 配置值不能为空!");
+        }
+
+        String superDbUsername = environment.getProperty(SUPER_DB_USERNAME, String.class);
+        if (StringUtils.isNotBlank(superDbUsername)) {
+            // 若配置了更高权限的DB账号,则优先使用
+            this.dbUsername = superDbUsername;
+        }
+
+        String superDbPassword = environment.getProperty(SUPER_DB_PASSWORD, String.class);
+        if (StringUtils.isNotBlank(superDbPassword)) {
+            // 若配置了更高权限的DB账号,则优先使用
+            this.dbPassword = superDbPassword;
+        }
+
+        DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(dbUrl);
+        if (DatabaseDriver.UNKNOWN == databaseDriver) {
+            throw new RuntimeException(DB_URL + " 配置值有误,不能有效识别出DriverClassName!");
+        }
+        this.driverClassName = databaseDriver.getDriverClassName();
+    }
+
+    public Boolean getEnable() {
+        return enable;
+    }
+
+    public String getBasePackage() {
+        return basePackage;
+    }
+
+    public Boolean getFirstInstall() {
+        return firstInstall;
+    }
+
+    public String getDbUsername() {
+        return dbUsername;
+    }
+
+    public String getDbPassword() {
+        return dbPassword;
+    }
+
+    public String getDbUrl() {
+        return dbUrl;
+    }
+
+    public String getDriverClassName() {
+        return driverClassName;
+    }
+
+    public String getAppCode() {
+        return appCode;
+    }
+
+    public String getAppVersion() {
+        return appVersion;
+    }
+
+}

+ 29 - 0
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/model/BootAppInfo.java

@@ -0,0 +1,29 @@
+package com.qmth.boot.data.upgrade.model;
+
+import java.io.Serializable;
+
+public class BootAppInfo implements Serializable {
+
+    private static final long serialVersionUID = 5490392146617139892L;
+
+    private String appCode;
+
+    private String appVersion;
+
+    public String getAppCode() {
+        return appCode;
+    }
+
+    public void setAppCode(String appCode) {
+        this.appCode = appCode;
+    }
+
+    public String getAppVersion() {
+        return appVersion;
+    }
+
+    public void setAppVersion(String appVersion) {
+        this.appVersion = appVersion;
+    }
+
+}

+ 20 - 0
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/service/DataInitService.java

@@ -0,0 +1,20 @@
+package com.qmth.boot.data.upgrade.service;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * 数据初始化服务类
+ *
+ * @author: Deason
+ * @since: 2025/3/3
+ */
+public interface DataInitService {
+
+    /**
+     * 数据初始化处理
+     *
+     * @throws Exception 处理失败时抛出异常
+     */
+    void process(JdbcTemplate jdbcTemplate) throws Exception;
+
+}

+ 1 - 2
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/service/DataUpgradeService.java

@@ -1,7 +1,6 @@
 package com.qmth.boot.data.upgrade.service;
 
 import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.transaction.support.TransactionTemplate;
 
 /**
  * 数据升级服务类
@@ -16,6 +15,6 @@ public interface DataUpgradeService {
      *
      * @throws Exception 处理失败时抛出异常
      */
-    void process(JdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) throws Exception;
+    void process(JdbcTemplate jdbcTemplate) throws Exception;
 
 }

+ 57 - 0
data-upgrade/src/main/java/com/qmth/boot/data/upgrade/utils/ClassHelper.java

@@ -0,0 +1,57 @@
+package com.qmth.boot.data.upgrade.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class ClassHelper {
+
+    private static final Logger log = LoggerFactory.getLogger(ClassHelper.class);
+
+    public static <T> List<T> findInterfaceInstances(Class<T> interfaceClass, String basePackage) {
+        List<Class<?>> implClasses = ClassHelper.findInterfaceImpls(interfaceClass, basePackage);
+
+        List<T> instances = new ArrayList<>();
+        for (Class<?> clazz : implClasses) {
+            try {
+                Constructor<?> constructor = ClassUtils.getConstructorIfAvailable(clazz);
+                if (constructor != null) {
+                    instances.add((T) constructor.newInstance());
+                }
+            } catch (Exception e) {
+                log.warn("{} class newInstance fail. {}", clazz.getCanonicalName(), e.getMessage());
+            }
+        }
+
+        return instances;
+    }
+
+    public static List<Class<?>> findInterfaceImpls(Class<?> interfaceClass, String basePackage) {
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(interfaceClass));
+
+        List<Class<?>> impls = new ArrayList<>();
+        scanner.findCandidateComponents(basePackage).forEach(bean -> {
+            try {
+                Class<?> clazz = ClassUtils.forName(Objects.requireNonNull(bean.getBeanClassName()), ClassUtils.getDefaultClassLoader());
+                // 排除:接口和抽象类
+                if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
+                    impls.add(clazz);
+                }
+            } catch (Exception e) {
+                log.warn("{} class load fail. {}", bean.getBeanClassName(), e.getMessage());
+            }
+        });
+
+        return impls;
+    }
+
+}