Quellcode durchsuchen

!479 update 优化 使用预扫描实体类提升代码性能
* 优化了数据库字段加解密的缓存机制.在自动配置类启动时,就把有加密注解的类进行缓存,以提高速度.

_老马_ vor 1 Jahr
Ursprung
Commit
cad250f02a

+ 11 - 0
ruoyi-common/ruoyi-common-encrypt/pom.xml

@@ -42,6 +42,17 @@
             <artifactId>spring-webmvc</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.mybatis</groupId>
+                    <artifactId>mybatis-spring</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 74 - 2
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java

@@ -1,5 +1,11 @@
 package org.dromara.common.encrypt.config;
 
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.io.Resources;
+import org.dromara.common.encrypt.annotation.EncryptField;
 import org.dromara.common.encrypt.core.EncryptorManager;
 import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor;
 import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor;
@@ -8,7 +14,20 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.type.ClassMetadata;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.springframework.util.StringUtils.tokenizeToStringArray;
 
 /**
  * 加解密配置
@@ -16,17 +35,66 @@ import org.springframework.context.annotation.Bean;
  * @author 老马
  * @version 4.6.0
  */
-@AutoConfiguration
+@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
 @EnableConfigurationProperties(EncryptorProperties.class)
 @ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
+@Slf4j
 public class EncryptorAutoConfiguration {
 
     @Autowired
     private EncryptorProperties properties;
+    @Autowired
+    private MybatisPlusProperties mybatisPlusProperties;
 
     @Bean
     public EncryptorManager encryptorManager() {
-        return new EncryptorManager();
+        Map<Class<?>, Set<Field>> fieldCache = scanEncryptClasses(mybatisPlusProperties.getTypeAliasesPackage());
+        return new EncryptorManager(fieldCache);
+    }
+
+    // 通过typeAliasesPackage设置的扫描包,来确定哪些实体类进行缓存
+    private Map<Class<?>, Set<Field>> scanEncryptClasses(String typeAliasesPackage) {
+        Map<Class<?>, Set<Field>> fieldCache = new HashMap<>();
+        try {
+            String[] packagePatternArray = tokenizeToStringArray(typeAliasesPackage,
+                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
+            for (String packagePattern : packagePatternArray) {
+                Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+                    + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
+                for (Resource resource : resources) {
+                    ClassMetadata classMetadata = new CachingMetadataReaderFactory().getMetadataReader(resource).getClassMetadata();
+                    Class<?> clazz = Resources.classForName(classMetadata.getClassName());
+                    Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
+                    if(CollectionUtil.isNotEmpty(encryptFieldSet)) {
+                        fieldCache.put(clazz, encryptFieldSet);
+                    }
+                }
+            }
+        }catch (Exception e) {
+            log.error("初始化数据安全缓存时出错:{}", e.getMessage());
+        }
+        return fieldCache;
+    }
+
+    // 获得一个类的加密字段集合
+    private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
+        Set<Field> fieldSet = new HashSet<>();
+        // 判断clazz如果是接口,内部类,匿名类就直接返回
+        if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
+            return fieldSet;
+        }
+        while (clazz != null) {
+            Field[] fields = clazz.getDeclaredFields();
+            fieldSet.addAll(Arrays.asList(fields));
+            clazz = clazz.getSuperclass();
+        }
+        fieldSet = fieldSet.stream().filter(field ->
+                field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
+            .collect(Collectors.toSet());
+        for (Field field : fieldSet) {
+            field.setAccessible(true);
+        }
+        return fieldSet;
     }
 
     @Bean
@@ -38,4 +106,8 @@ public class EncryptorAutoConfiguration {
     public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
         return new MybatisDecryptInterceptor(encryptorManager, properties);
     }
+
 }
+
+
+

+ 17 - 19
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java

@@ -1,16 +1,14 @@
 package org.dromara.common.encrypt.core;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
+import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.dromara.common.encrypt.annotation.EncryptField;
 
 import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
 
 /**
  * 加密管理类
@@ -19,6 +17,7 @@ import java.util.stream.Collectors;
  * @version 4.6.0
  */
 @Slf4j
+@NoArgsConstructor
 public class EncryptorManager {
 
     /**
@@ -31,25 +30,24 @@ public class EncryptorManager {
      */
     Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
 
+    /**
+     * 构造方法传入类加密字段缓存
+     *
+     * @param fieldCache 类加密字段缓存
+     */
+    public EncryptorManager(Map<Class<?>, Set<Field>> fieldCache) {
+        this.fieldCache = fieldCache;
+    }
+
+
     /**
      * 获取类加密字段缓存
      */
     public Set<Field> getFieldCache(Class<?> sourceClazz) {
-        return fieldCache.computeIfAbsent(sourceClazz, clazz -> {
-            Set<Field> fieldSet = new HashSet<>();
-            while (clazz != null) {
-                Field[] fields = clazz.getDeclaredFields();
-                fieldSet.addAll(Arrays.asList(fields));
-                clazz = clazz.getSuperclass();
-            }
-            fieldSet = fieldSet.stream().filter(field ->
-                    field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
-                .collect(Collectors.toSet());
-            for (Field field : fieldSet) {
-                field.setAccessible(true);
-            }
-            return fieldSet;
-        });
+        if(ObjectUtil.isNotNull(fieldCache)) {
+            return fieldCache.get(sourceClazz);
+        }
+        return null;
     }
 
     /**

+ 4 - 0
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java

@@ -73,7 +73,11 @@ public class MybatisDecryptInterceptor implements Interceptor {
             list.forEach(this::decryptHandler);
             return;
         }
+        // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
         Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
+        if(ObjectUtil.isNull(fields)){
+            return;
+        }
         try {
             for (Field field : fields) {
                 field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));

+ 4 - 0
ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java

@@ -82,7 +82,11 @@ public class MybatisEncryptInterceptor implements Interceptor {
             list.forEach(this::encryptHandler);
             return;
         }
+        // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
         Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
+        if(ObjectUtil.isNull(fields)){
+            return;
+        }
         try {
             for (Field field : fields) {
                 field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));