فهرست منبع

优化JSON类型字段处理器自动配置逻辑

woody 1 سال پیش
والد
کامیت
0cf9eb9552

+ 24 - 3
framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java

@@ -14,6 +14,7 @@ import java.util.Set;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import com.beust.jcommander.internal.Sets;
 import com.chelvc.framework.base.util.SpringUtils;
@@ -42,6 +43,8 @@ import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RestController;
 
 /**
  * 应用上下文工具类
@@ -606,6 +609,27 @@ public class ApplicationContextHolder implements ApplicationContextAware {
         }
     }
 
+    /**
+     * 查找接口对象实例
+     *
+     * @return 接口对象实例列表
+     */
+    public static List<Object> lookupControllers() {
+        return lookupControllers(getApplicationContext());
+    }
+
+    /**
+     * 查找接口对象实例
+     *
+     * @param applicationContext 应用上下文
+     * @return 接口对象实例列表
+     */
+    public static List<Object> lookupControllers(@NonNull ApplicationContext applicationContext) {
+        return Stream.of(applicationContext.getBeansWithAnnotation(Controller.class).values(),
+                applicationContext.getBeansWithAnnotation(RestController.class).values())
+                .flatMap(Collection::stream).distinct().collect(Collectors.toList());
+    }
+
     /**
      * 查找类对象所属资源
      *
@@ -658,9 +682,6 @@ public class ApplicationContextHolder implements ApplicationContextAware {
         });
 
         // 根据包扫描路径查找类对象
-        if (packages.isEmpty()) {
-            return SpringUtils.lookupClasses(StringUtils.EMPTY, condition);
-        }
         return packages.stream().flatMap(pack -> SpringUtils.lookupClasses(pack, condition).stream())
                 .distinct().collect(Collectors.toList());
     }

+ 17 - 30
framework-base/src/main/java/com/chelvc/framework/base/util/SpringUtils.java

@@ -4,7 +4,6 @@ import java.io.File;
 import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -14,7 +13,7 @@ import java.util.stream.Stream;
 
 import com.chelvc.framework.common.util.StringUtils;
 import lombok.NonNull;
-import org.springframework.context.ApplicationContext;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.core.io.support.ResourcePatternResolver;
@@ -38,6 +37,7 @@ import org.springframework.web.bind.annotation.RestController;
  * @author Woody
  * @date 2024/1/30
  */
+@Slf4j
 public final class SpringUtils {
     /**
      * 资源路径匹配器
@@ -235,27 +235,25 @@ public final class SpringUtils {
     /**
      * 查找工程内部的对象类型
      *
-     * @param pack 包
+     * @param pack 包路径
      * @return 类对象列表
      */
-    public static List<Class<?>> lookupClasses(@NonNull String pack) {
+    public static List<Class<?>> lookupClasses(String pack) {
         return lookupClasses(pack, clazz -> true);
     }
 
     /**
      * 查找工程内部的对象类型
      *
-     * @param pack      包
+     * @param pack      包路径
      * @param condition 类对象条件判断
      * @return 类对象列表
      */
-    public static List<Class<?>> lookupClasses(@NonNull String pack, @NonNull Predicate<Class<?>> condition) {
+    public static List<Class<?>> lookupClasses(String pack, @NonNull Predicate<Class<?>> condition) {
+        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
+                (pack == null || (pack = pack.trim()).isEmpty() ? "**/*.class" :
+                        (ClassUtils.convertClassNameToResourcePath(pack) + "/**/*.class"));
         ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
-        String pattern = (pack = pack.trim()).isEmpty() ? "**/*.class" : (
-                ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
-                        + ClassUtils.convertClassNameToResourcePath(pack)
-                        + "/**/*.class"
-        );
 
         // 获取对象资源文件
         Resource[] resources;
@@ -270,29 +268,18 @@ public final class SpringUtils {
         MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
         ClassLoader classLoader = Objects.requireNonNull(ClassUtils.getDefaultClassLoader());
         for (Resource resource : resources) {
+            // 加载类对象
+            Class<?> clazz = null;
             try {
-                // 加载类对象
                 MetadataReader reader = readerFactory.getMetadataReader(resource);
-                Class<?> clazz = classLoader.loadClass(reader.getClassMetadata().getClassName());
-                if (clazz != null && condition.test(clazz)) {
-                    classes.add(clazz);
-                }
-            } catch (IOException | ClassNotFoundException e) {
-                throw new RuntimeException(e);
+                clazz = classLoader.loadClass(reader.getClassMetadata().getClassName());
+            } catch (Throwable t) {
+                log.warn("Class load failed: {}", t.getMessage());
+            }
+            if (clazz != null && condition.test(clazz)) {
+                classes.add(clazz);
             }
         }
         return classes.build().distinct().collect(Collectors.toList());
     }
-
-    /**
-     * 查找接口对象实例
-     *
-     * @param applicationContext 应用上下文
-     * @return 接口对象实例列表
-     */
-    public static List<Object> lookupControllers(@NonNull ApplicationContext applicationContext) {
-        return Stream.of(applicationContext.getBeansWithAnnotation(Controller.class).values(),
-                applicationContext.getBeansWithAnnotation(RestController.class).values())
-                .flatMap(Collection::stream).distinct().collect(Collectors.toList());
-    }
 }

+ 6 - 6
framework-database/src/main/java/com/chelvc/framework/database/config/DatabaseConfigurer.java

@@ -26,8 +26,8 @@ import com.chelvc.framework.database.handler.ModificationTypeHandler;
 import com.chelvc.framework.database.handler.PeriodTypeHandler;
 import com.chelvc.framework.database.handler.RegionTypeHandler;
 import com.chelvc.framework.database.interceptor.DeletedExcludeInterceptor;
+import com.chelvc.framework.database.interceptor.MybatisConfigureInterceptor;
 import com.chelvc.framework.database.support.EnhanceSqlInjector;
-import com.chelvc.framework.database.support.JsonTypeHandlerBinder;
 import com.chelvc.framework.redis.context.RedisContextHolder;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
@@ -53,8 +53,8 @@ import org.springframework.transaction.annotation.Transactional;
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class DatabaseConfigurer {
     static {
-        // 初始化JSON类型绑定处理工具
-        JsonTypeHandlerBinder.initialize();
+        // 初始化Mybatis配置拦截器
+        MybatisConfigureInterceptor.initialize();
     }
 
     private final ApplicationContext applicationContext;
@@ -112,9 +112,9 @@ public class DatabaseConfigurer {
 
         // 设置自定义租户隔离拦截器
         Collection<InnerInterceptor> wheres = this.applicationContext.getBeansOfType(InnerInterceptor.class).values();
-        wheres.stream().sorted(Comparator.comparingInt(where ->
-                ObjectUtils.ifNull(where.getClass().getAnnotation(Order.class), Order::value, () -> 0))
-        ).forEach(interceptor::addInnerInterceptor);
+        wheres.stream().sorted(Comparator.comparingInt(
+                where -> ObjectUtils.ifNull(where.getClass().getAnnotation(Order.class), Order::value, () -> 0)
+        )).forEach(interceptor::addInnerInterceptor);
 
         // 设置删除数据过滤拦截器
         interceptor.addInnerInterceptor(new TenantLineInnerInterceptor() {

+ 1 - 1
framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java

@@ -581,7 +581,7 @@ public final class DatabaseContextHolder {
      * @param pack 包路径
      * @return 数据模型对象列表
      */
-    public static List<Class<?>> lookupEntityClasses(@NonNull String pack) {
+    public static List<Class<?>> lookupEntityClasses(String pack) {
         return SpringUtils.lookupClasses(pack, clazz -> Entity.class.isAssignableFrom(clazz)
                 && !Modifier.isInterface(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers()));
     }

+ 34 - 129
framework-database/src/main/java/com/chelvc/framework/database/support/JsonTypeHandlerBinder.java → framework-database/src/main/java/com/chelvc/framework/database/interceptor/JsonHandlerConfigureInterceptor.java

@@ -1,4 +1,4 @@
-package com.chelvc.framework.database.support;
+package com.chelvc.framework.database.interceptor;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.ParameterizedType;
@@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicLong;
 
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
 import com.baomidou.mybatisplus.core.metadata.TableInfo;
 import com.chelvc.framework.common.model.File;
@@ -42,118 +41,35 @@ import com.google.common.collect.Maps;
 import javassist.ClassPool;
 import javassist.CtClass;
 import javassist.CtConstructor;
-import javassist.CtMethod;
-import javassist.CtNewMethod;
-import javassist.NotFoundException;
-import lombok.NonNull;
 import org.apache.ibatis.parsing.XNode;
 import org.apache.ibatis.type.UnknownTypeHandler;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
 /**
- * JSON类型绑定处理工具类
+ * JSON类型字段处理器配置拦截器
  *
  * @author Woody
- * @date 2024/4/5
+ * @date 2024/4/6
  */
-public final class JsonTypeHandlerBinder {
+public class JsonHandlerConfigureInterceptor extends MybatisConfigureInterceptor {
     /**
      * JSON类型处理器计数器
      */
-    private static final AtomicLong JSON_HANDLER_COUNTER = new AtomicLong(0);
+    private final AtomicLong counter = new AtomicLong(0);
 
     /**
      * JSON类型/处理器映射表
      */
-    private static final Map<Type, Class<?>> JSON_HANDLER_CLASSES = Maps.newConcurrentMap();
-
-    private JsonTypeHandlerBinder() {
-    }
-
-    /**
-     * 初始化JSON类型处理器绑定工具
-     */
-    public static void initialize() {
-        try {
-            initializeMapperListener();
-            initializeTableInfoListener();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * 初始化Mapper解析监听器
-     *
-     * @throws Exception 初始化异常
-     */
-    private static void initializeMapperListener() throws Exception {
-        ClassPool pool = ClassPool.getDefault();
-
-        // 替换XMLMapperBuilder.parse方法逻辑,加入Mapper解析监听回调逻辑
-        CtClass clazz = pool.get("org.apache.ibatis.builder.xml.XMLMapperBuilder");
-        CtMethod method = clazz.getDeclaredMethod("parse");
-        method.setBody(String.format("{\n" +
-                "if (!this.configuration.isResourceLoaded(this.resource)) {\n" +
-                "   %s node = this.parser.evalNode(\"/mapper\");\n" +
-                "   %s.configure(node);\n" +
-                "   this.configurationElement(node);\n" +
-                "   this.configuration.addLoadedResource(this.resource);\n" +
-                "   this.bindMapperForNamespace();\n" +
-                "}\n" +
-                "this.parsePendingResultMaps();\n" +
-                "this.parsePendingCacheRefs();\n" +
-                "this.parsePendingStatements();\n" +
-                "}", XNode.class.getName(), JsonTypeHandlerBinder.class.getName()));
-        clazz.toClass();
-    }
-
-    /**
-     * 初始化数据模型表信息监听器
-     *
-     * @throws Exception 初始化异常
-     */
-    private static void initializeTableInfoListener() throws Exception {
-        ClassPool pool = ClassPool.getDefault();
-
-        // 获取原始initTableFields方法
-        CtMethod initTableFields = null;
-        CtClass clazz = pool.get("com.baomidou.mybatisplus.core.metadata.TableInfoHelper");
-        for (CtMethod method : clazz.getDeclaredMethods()) {
-            if (method.getName().equals("initTableFields")) {
-                initTableFields = method;
-            }
-        }
-        if (initTableFields == null) {
-            throw new NotFoundException("initTableFields");
-        }
-
-        // 重命名initTableFields方法名
-        CtMethod copy = CtNewMethod.copy(initTableFields, clazz, null);
-        copy.setName("doInitTableFields");
-        clazz.removeMethod(initTableFields);
-        clazz.addMethod(copy);
-
-        // 替换initTableFields方法,加入表信息初始化回调逻辑
-        CtMethod replace = CtNewMethod.make(String.format("private static void initTableFields(" +
-                        "Class clazz, %s globalConfig, %s tableInfo, java.util.List excludeProperty) {\n" +
-                        "doInitTableFields(clazz, globalConfig, tableInfo, excludeProperty);\n" +
-                        "%s.configure(tableInfo);\n" +
-                        "}",
-                GlobalConfig.class.getName(), TableInfo.class.getName(), JsonTypeHandlerBinder.class.getName()), clazz
-        );
-        clazz.addMethod(replace);
-        clazz.toClass();
-    }
+    private final Map<Type, Class<?>> handlers = Maps.newConcurrentMap();
 
     /**
      * 绑定字段JSON类型处理器
      *
      * @param field 表字段信息
      */
-    private static void bindJsonHandler(TableFieldInfo field) {
-        Class<?> handler = lookupJsonHandlerClass(field.getField());
+    private void bindJsonHandler(TableFieldInfo field) {
+        Class<?> handler = this.lookupJsonHandlerClass(field.getField());
         ObjectUtils.setValue(field, "typeHandler", handler);
         String el = ObjectUtils.getValue(field, "el") + ",typeHandler=" + handler.getName();
         ObjectUtils.setValue(field, "el", el);
@@ -165,7 +81,7 @@ public final class JsonTypeHandlerBinder {
      * @param field JSON字段实例
      * @return JSON类型处理器对象类型
      */
-    private static Class<?> lookupJsonHandlerClass(Field field) {
+    private Class<?> lookupJsonHandlerClass(Field field) {
         Type type = field.getGenericType();
         if (ObjectUtils.isOnlyMap(type)) {
             return MapTypeHandler.class;
@@ -205,9 +121,9 @@ public final class JsonTypeHandlerBinder {
                 return ListsTypeHandler.class;
             }
         }
-        return JSON_HANDLER_CLASSES.computeIfAbsent(type, t -> {
+        return this.handlers.computeIfAbsent(type, t -> {
             try {
-                return initializeJsonHandlerClass(field);
+                return this.initializeJsonHandlerClass(field);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
@@ -222,12 +138,11 @@ public final class JsonTypeHandlerBinder {
      * @return JSON类型处理器对象类型
      * @throws Exception 对象初始化异常
      */
-    private static Class<?> initializeJsonHandlerClass(Field field) throws Exception {
+    private Class<?> initializeJsonHandlerClass(Field field) throws Exception {
         ClassPool pool = ClassPool.getDefault();
         CtClass handler = pool.makeClass(String.format(
                 "%s.JsonTypeHandler_%d",
-                field.getDeclaringClass().getPackage().getName(),
-                JSON_HANDLER_COUNTER.incrementAndGet()
+                field.getDeclaringClass().getPackage().getName(), this.counter.incrementAndGet()
         ));
         handler.setSuperclass(pool.get(JsonTypeHandler.Simple.class.getName()));
         CtConstructor constructor = new CtConstructor(new CtClass[]{}, handler);
@@ -242,7 +157,7 @@ public final class JsonTypeHandlerBinder {
      * @param field 数据模型字段
      * @return true/false
      */
-    private static boolean isJsonHandlerConfigurable(Field field) {
+    private boolean isJsonHandlerConfigurable(Field field) {
         TableField annotation = field.getAnnotation(TableField.class);
         if (annotation == null || annotation.exist()) {
             Class<?> handler = ObjectUtils.ifNull(annotation, TableField::typeHandler);
@@ -258,10 +173,10 @@ public final class JsonTypeHandlerBinder {
      * @param clazz 数据模型对象
      * @return true/false
      */
-    private static boolean isJsonHandlerConfigurable(Class<?> clazz) {
+    private boolean isJsonHandlerConfigurable(Class<?> clazz) {
         Reference<Boolean> configurable = new Reference<>();
         ObjectUtils.iterateFields(clazz, (i, field) -> {
-            if (isJsonHandlerConfigurable(field)) {
+            if (this.isJsonHandlerConfigurable(field)) {
                 configurable.set(true);
                 return false;
             }
@@ -276,7 +191,7 @@ public final class JsonTypeHandlerBinder {
      * @param field 表字段信息
      * @return true/false
      */
-    private static boolean isJsonHandlerConfigurable(TableFieldInfo field) {
+    private boolean isJsonHandlerConfigurable(TableFieldInfo field) {
         return field.getTypeHandler() == null
                 && !DatabaseContextHolder.isTypeHandlerRegistered(field.getPropertyType());
     }
@@ -287,7 +202,7 @@ public final class JsonTypeHandlerBinder {
      * @param node 查询节点
      * @return 对象类型
      */
-    private static Class<?> getSelectResultType(XNode node) {
+    private Class<?> getSelectResultType(XNode node) {
         String type = node.getStringAttribute("resultType");
         if (StringUtils.isEmpty(type)) {
             return null;
@@ -305,7 +220,7 @@ public final class JsonTypeHandlerBinder {
      * @param node 查询节点
      * @return 对象类型
      */
-    private static Class<?> getResultMappingType(XNode node) {
+    private Class<?> getResultMappingType(XNode node) {
         try {
             return Class.forName(node.getStringAttribute("type"));
         } catch (ClassNotFoundException e) {
@@ -320,11 +235,10 @@ public final class JsonTypeHandlerBinder {
      * @param clazz    对象类型
      * @return 节点元素
      */
-    private static Element initializeResultMapping(Document document, Class<?> clazz) {
+    private Element initializeResultMapping(Document document, Class<?> clazz) {
         // 构建resultMap节点元素
         Element element = document.createElement("resultMap");
-        String id = "__" + StringUtils.hump2underscore(clazz.getSimpleName()).toUpperCase() + "_RESULT_MAP";
-        element.setAttribute("id", id);
+        element.setAttribute("id", StringUtils.uuid());
         element.setAttribute("type", clazz.getName());
 
         // 添加resultMap字段映射
@@ -339,8 +253,8 @@ public final class JsonTypeHandlerBinder {
             child.setAttribute("property", field.getName());
 
             // 设置json类型处理器
-            if (isJsonHandlerConfigurable(field)) {
-                Class<?> handler = lookupJsonHandlerClass(field);
+            if (this.isJsonHandlerConfigurable(field)) {
+                Class<?> handler = this.lookupJsonHandlerClass(field);
                 child.setAttribute("typeHandler", handler.getName());
             }
             element.appendChild(child);
@@ -348,24 +262,20 @@ public final class JsonTypeHandlerBinder {
         return element;
     }
 
-    /**
-     * 配置Mapper XML节点
-     *
-     * @param mapper Mapper XML节点
-     */
-    public static void configure(@NonNull XNode mapper) {
+    @Override
+    public void intercept(XNode mapper) {
         // 设置已有resultMap的json类型处理器
         List<XNode> maps = mapper.evalNodes("resultMap");
         if (ObjectUtils.notEmpty(maps)) {
             maps.forEach(map -> {
-                Class<?> clazz = getResultMappingType(map);
+                Class<?> clazz = this.getResultMappingType(map);
                 for (XNode child : map.getChildren()) {
                     // 判断当前是有存在typeHandler,如果不存在且字段满足配置json类型处理器条件,则自动绑定json类型处理器
                     if (StringUtils.isEmpty(child.getStringAttribute("typeHandler"))) {
                         String property = child.getStringAttribute("property");
                         Field field = ObjectUtils.getField(clazz, property);
-                        if (isJsonHandlerConfigurable(field)) {
-                            Class<?> handler = lookupJsonHandlerClass(field);
+                        if (this.isJsonHandlerConfigurable(field)) {
+                            Class<?> handler = this.lookupJsonHandlerClass(field);
                             ((Element) child.getNode()).setAttribute("typeHandler", handler.getName());
                         }
                     }
@@ -380,10 +290,10 @@ public final class JsonTypeHandlerBinder {
             Map<Class<?>, Element> cache = Maps.newHashMap();
             selects.forEach(select -> {
                 // 判断resultType是否可配置json类型处理器
-                Class<?> clazz = getSelectResultType(select);
-                if (clazz != null && isJsonHandlerConfigurable(clazz)) {
+                Class<?> clazz = this.getSelectResultType(select);
+                if (clazz != null && this.isJsonHandlerConfigurable(clazz)) {
                     // 构建并注册resultMap
-                    Element map = cache.computeIfAbsent(clazz, c -> initializeResultMapping(document, clazz));
+                    Element map = cache.computeIfAbsent(clazz, c -> this.initializeResultMapping(document, clazz));
                     mapper.getNode().appendChild(map);
 
                     // 用户新生成的resultMap替换resultType
@@ -395,17 +305,12 @@ public final class JsonTypeHandlerBinder {
         }
     }
 
-    /**
-     * 配置数据模型表信息
-     *
-     * @param table 数据模型表信息
-     */
-    public static void configure(@NonNull TableInfo table) {
+    @Override
+    public void intercept(TableInfo table) {
         // 自动绑定JSON类型字段处理器
         List<TableFieldInfo> fields = table.getFieldList();
         if (ObjectUtils.notEmpty(fields)) {
-            fields.stream().filter(JsonTypeHandlerBinder::isJsonHandlerConfigurable)
-                    .forEach(JsonTypeHandlerBinder::bindJsonHandler);
+            fields.stream().filter(this::isJsonHandlerConfigurable).forEach(this::bindJsonHandler);
         }
     }
 }

+ 183 - 0
framework-database/src/main/java/com/chelvc/framework/database/interceptor/MybatisConfigureInterceptor.java

@@ -0,0 +1,183 @@
+package com.chelvc.framework.database.interceptor;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import com.baomidou.mybatisplus.core.config.GlobalConfig;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.chelvc.framework.base.util.SpringUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.google.common.collect.Lists;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.CtNewMethod;
+import javassist.NotFoundException;
+import lombok.NonNull;
+import org.apache.ibatis.parsing.XNode;
+import org.springframework.core.annotation.Order;
+
+/**
+ * Mybatis配置拦截器抽象实现类
+ *
+ * @author Woody
+ * @date 2024/4/6
+ */
+public abstract class MybatisConfigureInterceptor {
+    /**
+     * 是否已初始化
+     */
+    private static boolean INITIALIZED;
+
+    /**
+     * 拦截器实例列表
+     */
+    private static List<MybatisConfigureInterceptor> INSTANCES = Collections.emptyList();
+
+    /**
+     * 初始化配置拦截器
+     */
+    public synchronized static void initialize() {
+        if (!INITIALIZED) {
+            try {
+                initializeMapperListener();
+                initializeTableInfoListener();
+                INSTANCES = Lists.newArrayList(initializeInterceptorInstances());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            } finally {
+                INITIALIZED = true;
+            }
+        }
+    }
+
+    /**
+     * 初始化Mapper解析监听器
+     *
+     * @throws Exception 初始化异常
+     */
+    private static void initializeMapperListener() throws Exception {
+        ClassPool pool = ClassPool.getDefault();
+
+        // 替换XMLMapperBuilder.parse方法逻辑,加入Mapper解析监听回调逻辑
+        CtClass clazz = pool.get("org.apache.ibatis.builder.xml.XMLMapperBuilder");
+        CtMethod method = clazz.getDeclaredMethod("parse");
+        method.setBody(String.format("{\n" +
+                "if (!this.configuration.isResourceLoaded(this.resource)) {\n" +
+                "   %s node = this.parser.evalNode(\"/mapper\");\n" +
+                "   %s.configure(node);\n" +
+                "   this.configurationElement(node);\n" +
+                "   this.configuration.addLoadedResource(this.resource);\n" +
+                "   this.bindMapperForNamespace();\n" +
+                "}\n" +
+                "this.parsePendingResultMaps();\n" +
+                "this.parsePendingCacheRefs();\n" +
+                "this.parsePendingStatements();\n" +
+                "}", XNode.class.getName(), MybatisConfigureInterceptor.class.getName()));
+        clazz.toClass();
+    }
+
+    /**
+     * 初始化数据模型表信息监听器
+     *
+     * @throws Exception 初始化异常
+     */
+    private static void initializeTableInfoListener() throws Exception {
+        ClassPool pool = ClassPool.getDefault();
+
+        // 获取原始initTableFields方法
+        CtMethod initTableFields = null;
+        CtClass clazz = pool.get("com.baomidou.mybatisplus.core.metadata.TableInfoHelper");
+        for (CtMethod method : clazz.getDeclaredMethods()) {
+            if (method.getName().equals("initTableFields")) {
+                initTableFields = method;
+            }
+        }
+        if (initTableFields == null) {
+            throw new NotFoundException("initTableFields");
+        }
+
+        // 重命名initTableFields方法名
+        CtMethod copy = CtNewMethod.copy(initTableFields, clazz, null);
+        copy.setName("doInitTableFields");
+        clazz.removeMethod(initTableFields);
+        clazz.addMethod(copy);
+
+        // 替换initTableFields方法,加入表信息初始化回调逻辑
+        CtMethod replace = CtNewMethod.make(String.format("private static void initTableFields(" +
+                        "Class clazz, %s globalConfig, %s tableInfo, java.util.List excludeProperty) {\n" +
+                        "doInitTableFields(clazz, globalConfig, tableInfo, excludeProperty);\n" +
+                        "%s.configure(tableInfo);\n" +
+                        "}",
+                GlobalConfig.class.getName(), TableInfo.class.getName(), MybatisConfigureInterceptor.class.getName()),
+                clazz
+        );
+        clazz.addMethod(replace);
+        clazz.toClass();
+    }
+
+    /**
+     * 初始化Mybatis配置拦截器实例
+     *
+     * @return Mybatis配置拦截器列表
+     * @throws Exception 初始化异常
+     */
+    private static List<MybatisConfigureInterceptor> initializeInterceptorInstances() throws Exception {
+        // 查找当前包目录下MybatisConfigureInterceptor实现
+        List<Class<?>> classes = SpringUtils.lookupClasses(
+                MybatisConfigureInterceptor.class.getPackage().getName(),
+                clazz -> clazz != MybatisConfigureInterceptor.class
+                        && MybatisConfigureInterceptor.class.isAssignableFrom(clazz)
+        );
+
+        // 基于@Order注解对拦截器对象排序
+        classes.sort(Comparator.comparingInt(
+                clazz -> ObjectUtils.ifNull(clazz.getAnnotation(Order.class), Order::value, () -> 0)
+        ));
+
+        // 初始化对象实例
+        if (ObjectUtils.isEmpty(classes)) {
+            return Collections.emptyList();
+        }
+        List<MybatisConfigureInterceptor> interceptors = Lists.newArrayListWithCapacity(classes.size());
+        for (Class<?> clazz : classes) {
+            interceptors.add((MybatisConfigureInterceptor) clazz.newInstance());
+        }
+        return interceptors;
+    }
+
+    /**
+     * 配置Mapper XML节点
+     *
+     * @param mapper Mapper XML节点
+     */
+    public static void configure(@NonNull XNode mapper) {
+        INSTANCES.forEach(interceptor -> interceptor.intercept(mapper));
+    }
+
+    /**
+     * 配置数据模型表信息
+     *
+     * @param table 数据模型表信息
+     */
+    public static void configure(@NonNull TableInfo table) {
+        INSTANCES.forEach(interceptor -> interceptor.intercept(table));
+    }
+
+    /**
+     * 拦截Mapper配置
+     *
+     * @param mapper Mapper XML节点
+     */
+    public void intercept(XNode mapper) {
+    }
+
+    /**
+     * 拦截数据模型配置
+     *
+     * @param table 数据模型表信息
+     */
+    public void intercept(TableInfo table) {
+    }
+}

+ 1 - 1
framework-oauth/src/main/java/com/chelvc/framework/oauth/config/OAuthConfigurer.java

@@ -154,7 +154,7 @@ public class OAuthConfigurer extends WebSecurityConfigurerAdapter {
                 ApplicationContextHolder.getApplicationResources() : Collections.emptyList();
 
         // 将所有使用@Authorize注解并且enabled = true的接口地址排除认证
-        SpringUtils.lookupControllers(this.applicationContext).forEach(controller -> {
+        ApplicationContextHolder.lookupControllers(this.applicationContext).forEach(controller -> {
             Class<?> clazz = AopProxyUtils.ultimateTargetClass(controller);
 
             // 如果启用多服务MVC配置则将服务名作为接口地址前缀