跳至主要內容

AOP

chanchaw大约 3 分钟languagejava

获取 request

项目 https://gitee.com/chanchaw/zipperdye.git MybatisAspect.java 中有下面代码

import org.springframework.web.context.request.RequestContextHolder;

@Before("logDao()")
public void logDaoBefore(JoinPoint joinPoint) throws Exception {
    // 获取 request
    ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    // 前端的请求地址:http://localhost:7075/zipperdye/contact/selectByPrimaryKey/12
    log.info("dao请求地址:" + request.getRequestURL().toString());
    // 请求方法:GET
    log.info("dao请求类型:" + request.getMethod());
    // 获得请求头中指定名称属性的值
    String barreluser = request.getHeader("barreluser");
    log.info("请求头barreluser:{}", barreluser);

    String methodName = joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
    log.info("SqlSessionFactory:" + sqlSessionFactory);//org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
    log.info("dao实现方法:" + methodName);//com.xdf.zipperdye.dao.business.ContactMapper.selectByPrimaryKey
    log.info("dao参数:" + Arrays.toString(joinPoint.getArgs()));//获取发送请求时候传递来的参数(数组):[12]

打印带参 mybatis sql

使用 aop 在执行前打印出拼接参数后的 sql。初次试验在项目 https://gitee.com/chanchaw/zipperdyeopen in new window 改造步骤:

  1. pom.xml 添加依赖
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>
  1. springboot 项目开启 aop 功能
@EnableAspectJAutoProxy
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class ZipperdyeApplication {...}
  1. Mybatis工具类 - 拼接参数到 sql 中
package com.xdf.zipperdye.utils;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.session.SqlSessionFactory;

import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chanchaw
 * @create 2022-09-30 9:37
 */
public class MyBatisSqlUtils {
    public String execute(String sqlId, Object[] args, SqlSessionFactory sqlSessionFactory) throws Exception {
        Map<String, Object> paramMap = initParamMap(args);
        MappedStatement ms = sqlSessionFactory.getConfiguration().getMappedStatement(sqlId);
        BoundSql boundSql = ms.getBoundSql(paramMap);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        List<Object> paramValues = getParamValues(paramMap, parameterMappings);
        // 获取执行的sql,此时未设置参数
        String sql = boundSql.getSql();
//        if(args!=null&&args.length==1&&(args[0] instanceof String)){
//            paramValues.add(args[0]);
//        }
        for (int i = 0; i < args.length; i++) {
            paramValues.add(args[0]);
        }

        String execSql = getExecuteSql(sql, paramValues.stream().filter(item -> item !=null).collect(Collectors.toList()));
        return execSql;
    }

    /**
     * 设置查询参数值,返回可直接执行的sql
     */
    private String getExecuteSql(String sql, List<Object> paramValues) {
        while(sql.indexOf("?") != -1 && paramValues!=null&&paramValues.size() > 0&& paramValues.get(0)!=null) {
            Object paramValue = paramValues.get(0);
            if (paramValue != null) {
                String value = paramValue.toString();
                if (paramValue instanceof String) {
                    value = "'" + paramValue.toString() + "'";
                }
                else if (paramValue instanceof Date || paramValue instanceof Timestamp) {
//                    value = DateUtil.dateToStr((Date) paramValue,"yyyy-MM-dd HH:mm:ss");
                    try {
                        // 将日期类型转换为格式 "yyyy-MM-dd HH:mm:ss" 的字符串,赋值给 value
                        value = DatetimeUtils.formatDate((Date)paramValue);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    value = "str_to_date('" + value + "','%Y-%m-%d %T')";
                }
                sql = sql.replaceFirst("\\?", value);
                paramValues.remove(0);
            }
        }

        return sql;
    }

    /**
     * 根据动态查询条件获取查询参数值
     */
    private List<Object> getParamValues(Map<String,Object> paramMap,
                                        List<ParameterMapping> parameterMappings) {
        if (parameterMappings == null) {
            return new ArrayList<Object>();
        }
        List<Object> paramValues = new ArrayList<Object>();
        for (ParameterMapping pm : parameterMappings) {
            if (pm.getMode() != ParameterMode.OUT) {
                String paramName = pm.getProperty();
                Object paramValue = paramMap.get(paramName);
                paramValues.add(paramValue);
            }
        }
        return paramValues;
    }

    /**
     * 初始化查询参数
     */
    private Map<String, Object> initParamMap(Object[] args) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        for (Object obj : args) {
            if (obj instanceof Map) {
                paramMap = (Map<String, Object>) obj;
            }
        }
        return paramMap;
    }
}
  1. 制作切面类
package com.xdf.zipperdye.aop;

import com.xdf.zipperdye.utils.MyBatisSqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * @author chanchaw
 * @create 2022-09-30 9:26
 */
@Component
@Aspect
@Slf4j
public class MybatisAspect {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
//    @Autowired
//    private ApplicationContext applicationContext;

    @Pointcut("execution(* com.xdf.zipperdye.dao.business.ContactMapper.*(..))")
    public void logDao(){}

    @Before("logDao()")
    public void logDaoBefore(JoinPoint joinPoint) throws Exception {
        log.info("RequestContextHolder.currentRequestAttributes:" + String.valueOf(RequestContextHolder.currentRequestAttributes()));
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        Assert.notNull(attributes,"通过上下文holder获取到了空的属性对象!");
        HttpServletRequest request = attributes.getRequest();
        log.info("dao请求地址:" + request.getRequestURL().toString());
        log.info("dao请求类型:" + request.getMethod());
//

//        SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory.class);

        String methodName = joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName();
        log.info("SqlSessionFactory:" + sqlSessionFactory);
        log.info("dao实现方法:" + methodName);
        log.info("dao参数:" + Arrays.toString(joinPoint.getArgs()));

        //仅获取无参数语句
        String sqlRaw =sqlSessionFactory.getConfiguration().getMappedStatement(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()).getBoundSql(null).getSql();
        //获取最终执行的sql语句
        log.info("joinPoint.getSignature().getDeclaringTypeName():" + joinPoint.getSignature().getDeclaringTypeName());
        log.info("joinPoint.getSignature().getDeclaringType():" + joinPoint.getSignature().getDeclaringType());
        log.info("joinPoint.getSignature().toString():" + joinPoint.getSignature().toString());


        String sql=new MyBatisSqlUtils().execute(methodName, joinPoint.getArgs(),sqlSessionFactory);
        log.info("无参SQL:" + sqlRaw);
        log.info("拼接后的SQL:" + sql);
    }
}