java 使用切面+注解的方式对方法中的String类型的参数,转成小写。


java 使用切面+注解的方式对方法中的String类型的参数,转成小写。

@Mapper
public interface DefaultMapper{
	List<Map<String, Object>> querySql(String sql);
}
@Component
public class JdbcUtils{
	private static final Logger log = LoggerFactory.getLogger(JdbcUtils.class);
	
	@Autowired
	private DefaultMapper defaultMapper; 
	
	public List<Map<String, Object>> queryData(Integer page, Integer pageSize){
        Integer offset = (page -1) * pageSize;
		String sql = "select user_id,USER_NAME from user" + "LIMIT ? OFFSET ?";
        String realSql = sql.repalceFirst("\\?", pageSize.toString()).repalceFirst("\\?", offset.toString());
        List<Map<String, Object>> = defaultMapper.querySql(realSql);
	}
}

有一个功能需求,希望代码中硬编码写死的sql语句在Mapper方法中执行的时候,sql需要转成小写。

因为用 List<Map<String, Object>>接收 mybatis 返回时,sql语句中的字段名是大写,那 Map中的key也是大写;sql语句中的字段名是大写,Map中的key也是小写。

现在需要查到的List 中字段名都是小写,那硬编码sql中的字段名就必须也是小写。

所以,需要写一个切面,保证传给mapper中执行的sql 全部是小写。

@StringToLowercase 注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface StringToLowercase {
    String value() default "sql";
}

切面方法 StringToLowercaseAspect

import com.mhd.taskmodel.aspect.annotation.StringToLowercase;
import org.apache.ibatis.annotations.Param;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @Description:
 * @title: StringToLowercaseAspect
 * @Author mhd
 * @Date: 2025/5/30 15:48
 * @Version 1.0
 */
@Aspect
@Component
public class StringToLowercaseAspect {

    /**
     * 使用注解 StringToLowercase 将方法入参中指定参数转成小写
     * @param joinPoint
     * @param stringToLowercase
     * @return
     * @throws Throwable
     */
    @Around("@annotation(stringToLowercase)")
    public Object convertSqlParamToLowercase(ProceedingJoinPoint joinPoint, StringToLowercase stringToLowercase) throws Throwable {
        // 获取目标参数名
        String targetParamName = stringToLowercase.value();

        // 获取方法参数列表
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Parameter[] parameters = method.getParameters();
        Object[] args = joinPoint.getArgs();

        int targetIndex = -1;
        // 双重查找策略:
        // 1. 通过参数名查找
        // 2. 通过@Param注解查找
        // 第一优先级:参数名匹配
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].getName().equals(targetParamName)) {
                targetIndex = i;
                break;
            }
        }

        if (targetIndex == -1) {
            // 第二优先级:@Param注解匹配 需要在 maven 中添加 <arg>-parameters</arg>配置
            for (int i = 0; i < parameters.length; i++) {
                Param paramAnnotation = parameters[i].getAnnotation(Param.class);
                if (paramAnnotation != null && paramAnnotation.value().equals(targetParamName)) {
                    targetIndex = i;
                    break;
                }
            }
        }

        // 转换目标参数
        if (targetIndex != -1 && args[targetIndex] instanceof String) {
            args[targetIndex] = ((String) args[targetIndex]).toLowerCase();
            return joinPoint.proceed(args);
        }
        // 兜底,默认取方法中的第一个参数
        args[0] = ((String) args[0]).toLowerCase();
        return joinPoint.proceed(args);
    }

}

修改后的使用:

@Mapper
public interface DefaultMapper{
    
    @StringToLowercase("sql")
	List<Map<String, Object>> querySql(String sql);
}

配置要求

1. 启用编译参数保留(推荐)

在 Maven 的 pom.xml 中添加以下配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <compilerArgs>
                    <arg>-parameters</arg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

2. 依赖配置

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
    <!-- Spring AOP -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>
</dependencies>

3. 启用AOP代理

在启动类中加上注解 @EnableAspectJAutoProxy

@SpringBootApplication
@EnableAspectJAutoProxy
public class SqlParamConverterApplication {
    public static void main(String[] args) {
        SpringApplication.run(SqlParamConverterApplication.class, args);
    }
}

使用示例

1. 应用注解

@Mapper
public interface UserInfoMapper {
    // 其他方法...
    
    @ConvertSqlParam("sqlStr") // 指定要转换的参数名
    List<Map<String, Object>> querySql(@Param("sql") String sqlStr, 
                                      @Param("tableName") String table);
}

2. 调用方法

@Autowired
private UserInfoMapper userInfoMapper;

public void executeQuery() {
    // 原始SQL包含大写
    String sql = "SELECT * FROM Users WHERE Status = 'ACTIVE'";
    String table = "user_table";
    
    // 执行查询
    List<Map<String, Object>> result = userInfoMapper.querySql(sql, table);
    
    // 实际执行的SQL将是小写:
    // "select * from users where status = 'active'"
}

方案优势

  1. 精确控制
    • 只转换指定的参数
    • 不影响其他参数
  2. 灵活性强
    • 可以应用于任何方法参数
    • 不限于SQL参数
  3. 高兼容性
    • 支持参数名匹配
    • 支持@Param注解匹配
    • 适应不同编译环境
  4. 可扩展性
    • 轻松添加其他转换逻辑(如大小写转换、trim等)
    • 支持多个参数转换

注意事项

  1. 参数名保留
    • 推荐使用编译选项-parameters
    • 或者依赖@Param注解
  2. 数据库兼容性
    • 转换后的小写SQL在大小写敏感数据库中可能出错
    • 确保数据库表名和列名使用统一大小写
  3. SQL注入风险
    • 此转换不影响SQL注入风险
    • 确保传入的SQL是安全的
  4. 性能考虑
    • 参数查找操作开销很小
    • 避免在切面中添加复杂逻辑

这个解决方案通过自定义注解和AOP切面,实现了对指定参数的精确转换,保持了代码的清晰性和可维护性。


高级扩展

1. 支持多个参数转换

// 修改注解定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ConvertSqlParam {
    String[] value(); // 支持多个参数名
}

// 修改切面处理
@Around("@annotation(convertSqlParam)")
public Object convertSqlParamToLowercase(ProceedingJoinPoint joinPoint, 
                                       ConvertSqlParam convertSqlParam) throws Throwable {
    String[] targetParamNames = convertSqlParam.value();
    // 获取方法参数...
    
    for (String targetName : targetParamNames) {
        int index = findParameterIndex(targetName, parameters);
        if (index != -1 && args[index] instanceof String) {
            args[index] = ((String) args[index]).toLowerCase();
        }
    }
    
    return joinPoint.proceed(args);
}

2. 添加转换类型选项

// 扩展注解功能
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface ConvertSqlParam {
    String value();
    CaseType caseType() default CaseType.LOWERCASE;
}

enum CaseType {
    LOWERCASE, UPPERCASE
}

// 修改切面处理
@Around("@annotation(convertSqlParam)")
public Object convertSqlParamToLowercase(ProceedingJoinPoint joinPoint, 
                                       ConvertSqlParam convertSqlParam) throws Throwable {
    // ...
    if (index != -1 && args[index] instanceof String) {
        String value = (String) args[index];
        switch (convertSqlParam.caseType()) {
            case LOWERCASE:
                args[index] = value.toLowerCase();
                break;
            case UPPERCASE:
                args[index] = value.toUpperCase();
                break;
        }
    }
    // ...
}
SpringBoot
Maven
JAVA-技能点
Mysql