通过spring的AOP的切面,拦截到持久层执行的sql及参数

Posted Peter-OK

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过spring的AOP的切面,拦截到持久层执行的sql及参数相关的知识,希望对你有一定的参考价值。

声明:文章内容是 自己使用后整理,大部分工具代码出自大牛,但因无法确认出处,故仅在此处由衷的对无私分享源代码的作者表示感谢与致敬!

使用该方式需要按规范书写所有的mapper方法,否则会报错

方式一:形参为自定义实体类
void insert(User user);

方式二:形参为Map
void insert(Map<String,Object> map);

方式三:形参为list

void batchInsertUser(@Param(value = "list") List<UserDTO> list);

方式四:形参为单个或多个参数,需要使用@Param注解
void insert(@Param("userName") String userName, @Param("age")Integer age);

LogSqlAnnotation.java

package com.xxx.xxx.xxx.aspect;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogSqlAnnotation
   
    String value() default "";

LogSqlAspect.java

package com.xxx.xxx.xxx.aspect;

import com.xxx.xxx.xxx.util.Func;
import com.xxx.xxx.xxx.util.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Slf4j
//@Aspect
//@Component
public class LogSqlAspect 


    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 切点 扫描整个mapper
     *
     * @Pointcut("execution(* com.xxx.xxx.xxx.controller.*.*(..)) && @annotation(com.xxx.xxx.xxx.aspect.LogSqlAnnotation)")
     * @Pointcut("execution(* com.xxx.xxx.xxx.mappers.*.*(..))")
     */
    @Pointcut("execution(* com.xxx.xxx.xxx.mapper.*.*(..)) && !execution(* com.xxx.xxx.xxx.mapper.JobLogSqlMapper.*(..))")
    public void logRecord() 
        // 定义
    

    /**
     * 前置通知
     *
     * @param jp
     */
    @Before(value = "logRecord()")
    public void before(JoinPoint jp) 
        String name = jp.getSignature().getName();
        log.info(name + "方法开始执行");
    

    /**
     * 后置通知
     *
     * @param jp
     */
    @After(value = "logRecord()")
    public void after(JoinPoint jp) 
        String name = jp.getSignature().getName();
        log.info(name + "方法执行结束...");
    

    /**
     * 返回通知
     *
     * @param jp
     * @param result
     */
    @AfterReturning(value = "logRecord()", returning = "result")
    public void afterReturning(JoinPoint jp, Object result) 
        String name = jp.getSignature().getName();
        log.info(name + "方法返回值为:" + result);
    

    /**
     * 异常通知
     *
     * @param jp
     * @param e
     */
    @AfterThrowing(value = "logRecord()", throwing = "e")
    public void afterThrowing(JoinPoint jp, Exception e) 
        String name = jp.getSignature().getName();
        log.info(name + "方法异常:" + e.getMessage());
    

    //    @Around(value = "logRecord()")
//    public Object around(ProceedingJoinPoint pjp) throws Throwable 
//        return pjp.proceed();
//    


    @Around(value = "logRecord()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable 

        String sql = SqlUtils.getMybatisSql(pjp, sqlSessionFactory);
        String dml= Func.getSqlDML(sql);
        String remark= Func.analyzeSqlWhere(sql);

        // 获取请求信息
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String requestUrl=null;
        if(requestAttributes!=null)
            HttpServletRequest request = requestAttributes.getRequest();
            requestUrl = request.getRequestURL().toString();
        
        Signature signature = pjp.getSignature();
        String methodName = signature.getName();
        String methodUrl = signature.toShortString();

        log.info("记录执行sql--->dml:",dml);
        log.info("记录执行sql--->remark:",remark);
        if(remark!=null || (remark==null && dml != null && !"SELECT".equals(dml) ))
            log.info("记录执行sql--->sql:",sql);
            log.info("记录执行sql--->methodName:",methodName);
            log.info("记录执行sql--->methodUrl:",methodUrl);
            log.info("记录执行sql--->requestUrl:",requestUrl);
        
        /*//<editor-fold desc="1、jdbc 保存日志">
        Connection connection = JdbcUtils.getConnection();
        Statement statement = connection.createStatement();
        String insertSql = "insert into logsql(log_id, sql_str, method_name, method_url, request_url, create_date) " +
                "values (" + "\\'" + logId + "\\'" + ","
                + "\\"" + sql + "\\"" + ","
                + "\\"" + methodName + "\\"" + ","
                + "\\'" + methodUrl + "\\'" + ","
                + "\\'" + requestUrl + "\\'" + ","
                + "NOW()" + ")";
        statement.executeUpdate(insertSql);
        //</editor-fold>*/

        /*//<editor-fold desc="2、mybatis 通过model方式保存">
        LogSql logSql = new LogSql();
        logSql.setLogId(logId);
        logSql.setMethodName(methodName);
        logSql.setMethodUrl(methodUrl);
        logSql.setSqlStr(sql);
        logSql.setRequestUrl(requestUrl);
        logSqlMapper.saveLogByModel(logSql);
        //</editor-fold>*/

        // 3、mybatis 通过 map方式保存"
//        Map<String, String> map = new HashMap<>(16);
//        map.put("sql_str", sql);
//        map.put("method_name", methodName);
//        map.put("method_url", methodUrl);
//        map.put("request_url", requestUrl);
//    logSqlMapper.saveLogByMap(map);

        return pjp.proceed();
    


SqlUtils.java

package com.xxx.xxx.xxx.util;

import com.google.common.collect.Lists;
import com.xxx.xxx.xxx.model.log.LogSqlConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.DateFormat;
import java.time.Instant;
import java.util.*;


/**
 * @date 2021/9/20 10:16
 * @description  获取aop中的SQL语句,借鉴来源https://blog.csdn.net/sdzhangshulong/article/details/104393244
 */
@Slf4j
public class SqlUtils 

    /**
     * 获取aop中的SQL语句
     *
     * @param pjp
     * @param sqlSessionFactory
     * @return
     * @throws IllegalAccessException
     */
    public static String getMybatisSql(ProceedingJoinPoint pjp, SqlSessionFactory sqlSessionFactory) throws IllegalAccessException 
        Map<String, Object> map = new HashMap<>(16);
        //1.获取namespace+methdoName
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        String namespace = method.getDeclaringClass().getName();
        String methodName = method.getName();
        //2.根据namespace+methdoName获取相对应的MappedStatement
        Configuration configuration = sqlSessionFactory.getConfiguration();
        MappedStatement mappedStatement = configuration.getMappedStatement(namespace + "." + methodName);
        //3.获取方法参数列表名
        Parameter[] parameters = method.getParameters();
        //4.形参和实参的映射,获取实参
        Object[] objects = pjp.getArgs();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++) 
            Object object = objects[i];
            //说明该参数没有注解,此时该参数可能是实体类,也可能是Map,也可能只是单参数
            if (parameterAnnotations[i].length == 0) 
                if (object.getClass().getClassLoader() == null && object instanceof Map) 
                    map.putAll((Map<? extends String, ?>) object);
                    log.info("该对象为Map");
                 else //形参为自定义实体类
                    map.putAll(objectToMap(object));
                    log.info("该对象为用户自定义的对象");
                
             else //说明该参数有注解,且必须为@Param
                for (Annotation annotation : parameterAnnotations[i]) 
                    if (annotation instanceof Param) 
                        map.put(((Param) annotation).value(), object);
                    
                
            
        
        //5.获取boundSql
        BoundSql boundSql = mappedStatement.getBoundSql(map);
//        BoundSql boundSql = mappedStatement.getBoundSql();
        return showSql(configuration, boundSql);
    

    /**
     * 解析BoundSql,生成不含占位符的SQL语句
     *
     * @param configuration
     * @param boundSql
     * @return
     */
    private static String showSql(Configuration configuration, BoundSql boundSql) 
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) 
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) 
                sql = sql.replaceFirst("\\\\?", getParameterValue(parameterObject));
             else 
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) 
                    String propertyName = parameterMapping.getProperty();
                    String[] s = metaObject.getObjectWrapper().getGetterNames();
                    s.toString();
                    if (metaObject.hasGetter(propertyName)) 
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\\\?", getParameterValue(obj));
                     else if (boundSql.hasAdditionalParameter(propertyName)) 
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\\\?", getParameterValue(obj));
                    
                
            
        
        return sql;
    

    /**
     * 若为字符串或者日期类型,则在参数两边添加''
     *
     * @param obj
     * @return
     */
    private static String getParameterValue(Object obj) 
        String value = null;
        if (obj instanceof String) 
            value = "'" + obj.toString() + "'";
         else if (obj instanceof Date) 
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
         else 
            if (obj != null) 
                value = obj.toString();
             else 
                value = "";
            
        
        return value;
    

    /**
     * 获取利用反射获取类里面的值和名称
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    private static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException 
        Map<String, Object> map = new HashMap<>(16);
        Class<?> clazz = obj.getClass();
        log.info("Class<?>=",clazz);
        // 获取本类及其父类的属性,↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) 
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        
        // 获取本类及其父类的属性,↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
        for (Field field : fieldList) 
            field.setAccessible(true);
            String fieldName = field.getName();
            Object value = field.get(obj);
            map.put(fieldName, value);
        
        return map;
    

    /**
     * 获取 DML 的 方式(insert、delete、update、select)
     * @param sql
     * @return
     */
    public static String getSqlDML(String sql) 

        if(Func.isEmpty(sql))
            return null;
        
        try 
            sql=sql.trim();
            int endIndex = sql.indexOf(" ")!=-1?sql.indexOf(" "):sql.length()>=6?6:sql.length();
            return sql.substring(0, endIndex).toUpperCase();
         catch (Exception e) 
            log.error("SqlUtils- 获取sql的DML 异常",e);
            return null;
        
    

    /**
     * 获取表名
     * @param sql
     * @return
     */
    public static String getTableName(String sql) 

        String dml = getSqlDML(sql);

        if(Func.isEmpty(dml))
            return null;
        
        // LogSqlEnum.DmlEnum.UPDATE.equals(dml) || LogSqlEnum.DmlEnum.DELETE.equals(dml) || LogSqlEnum.DmlEnum.SELECT.equals(dml)
        try 
            sql = sql.substring(6).trim().toUpperCase();
            if((LogSqlConstant.DmlEnum.DELETE.equals(dml) || LogSqlConstant.DmlEnum.SELECT.equals(dml))
                    && sql.contains("FROM"))
                sql=sql.substring(sql.indexOf("FROM")+4).trim();
                String[] s = sql.split(" ");
                if(s.length>1)
                    return s[0].trim();
                
            
            if(LogSqlConstant.DmlEnum.UPDATE.equals(dml) && sql.contains("SET"))
                return sql.substring(6,sql.indexOf("SET")).trim();
            
            return null;
         catch (Exception e) 
            log.error("SqlUtils- 获取表名 异常",e);
            return null;
        
    

    /**
     * 分析sql的where条件
     * @param sql
     * @return
     */
    public static String analyzeSqlWhere(String sql) 
        try 
            String dml = getSqlDML(sql);
            // 忽略枚举表
            if(LogSqlConstant.DmlEnum.SELECT.equals(dml)
                    && LogSqlConstant.getIgnoreTable().contains(getTableName(sql)))
                return null;
            

            if(LogSqlConstant.DmlEnum.UPDATE.equals(dml) || LogSqlConstant.DmlEnum.DELETE.equals(dml) || LogSqlConstant.DmlEnum.SELECT.equals(dml))
                String sqlUpperCase = sql.toUpperCase();

                if(!sqlUpperCase.contains("WHERE"))
                    return "【高风险sql】"+dml+"无WHERE条件";
                else 
                    String whereStr = sqlUpperCase.substring(sqlUpperCase.indexOf("WHERE")+5);
                    if(whereStr.contains("GROUP BY"))
                        whereStr = whereStr.substring(0,whereStr.indexOf("GROUP BY"));
                    else if(whereStr.contains("ORDER BY"))
                        whereStr = whereStr.substring(0,whereStr.indexOf("ORDER BY"));
                    
                    whereStr = whereStr.trim();
                    List<String> blList = Lists.newArrayList();
                    if(whereStr.contains("("))
                        String[] bl = whereStr.split("\\\\(");
                        for (String b : bl) 
                            if(Func.isNotEmpty(b))
                                blList.add(b);
                            
                        
                    
                    List<String> brList = Lists.newArrayList();
                    for (String bl : blList) 
                        String[] br = bl.split("\\\\)");
                        for (String b : br) 
                            if(Func.isNotEmpty(b))
                                brList.add(b);
                            
                        
                    
                    List<String> andList = Lists.newArrayList();
                    for (String br : brList) 

                        String[] ands = br.split("AND");
                        for (String s : ands) 
                            if(Func.isNotEmpty(s))
                                andList.add(s);
                            
                        
                    

                    List<String> orList = Lists.newArrayList();
                    for (String an : andList) 
                        String[] ors = an.split("OR");
                        for (String s : ors) 
                            if(Func.isNotEmpty(s))
                                orList.add(s);
                            
                        
                    
                    StringBuilder nullSB = new StringBuilder();
                    for (String or : orList) 
                        String[] eqs = or.split("=");
                        if(eqs.length==2
                                && (Func.isEmpty(eqs[1]) || "null".equalsIgnoreCase(eqs[1].trim()) ))
                            nullSB.append(or).append(";");
                        
                    

                    return nullSB.length()>0?nullSB.insert(0,"【中风险sql】条件值为空:").toString():null;
                
            
            return null;
         catch (Exception e) 
            log.error("SqlUtils-分析sql的where条件异常",e);
            return "SqlUtils-分析sql的where条件异常";
        
    


    public static void main(String[] args) 
//        System.out.println(getSqlDML("insertaass"));

        String sql0="select * from t_test where (sex='男' or sex='女' or id in (1) ) and name=null and id=2 and age in (1,2,3) group by dept order by id desc";
        String sql2="select day as day, code as code from t_day where day >= '2021-09-08' and day <= '2021-09-08' and (code = '0' or code = '2')";
        
//        String analyzeSqlWhere = analyzeSqlWhere(sql);
//        System.out.println(analyzeSqlWhere);

    


LogSqlConstant.java
package com.xxx.xxx.xxx.model.log;

import com.google.common.collect.Sets;

import java.util.Set;

public class LogSqlConstant 

    public enum DmlEnum 
        SELECT,INSERT,UPDATE,DELETE;
    

    public static Set<String> getIgnoreTable()
        return Sets.newHashSet("T_PRIORITY","T_STATUS_TYPE","T_B_TYPE","T_A_TYPE");
    

Func.java

package com.xxx.xxx.xxx.util;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 高频方法集合类
 */
public class Func 

    /**
     * 两个字符串数值 乘法
     *
     * @return
     */
    public static String multiply(String arg1, String arg2) 
        BigDecimal bg1 = new BigDecimal(arg1);
        BigDecimal bg2 = new BigDecimal(arg2);
        return bg1.multiply(bg2).toString();
    

    /**
     * 比较两个对象是否相等。<br>
     * 相同的条件有两个,满足其一即可:<br>
     * 1. obj1 == null && obj2 == null; 2. obj1.equals(obj2)
     *
     * @param obj1 对象1
     * @param obj2 对象2
     * @return 是否相等
     */
    public static boolean equals(Object obj1, Object obj2) 
        return (obj1 != null) ? (obj1.equals(obj2)) : (obj2 == null);
    

    /**
     * 计算对象长度,如果是字符串调用其length函数,集合类调用其size函数,数组调用其length属性,其他可遍历对象遍历计算长度
     *
     * @param obj 被计算长度的对象
     * @return 长度
     */
//    public static int length(Object obj) 
//        return ObjectKit.length(obj);
//    

    /**
     * 对象中是否包含元素
     *
     * @param obj     对象
     * @return 是否包含
     */
//    public static boolean contains(Object obj, Object element) 
//        return ObjectKit.contains(obj, element);
//    

    public static boolean isNotEmpty(Object obj) 
        return !isEmpty(obj);
    

    /**
     * 对象是否为空
     *
     * @param obj String,List,Map,Object[],int[],long[]
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static boolean isEmpty(Object obj) 
        if (obj == null) 
            return true;
        
        if (obj instanceof String) 
            if (obj.toString().trim().equals("")) 
                return true;
            
         else if (obj instanceof List) 
            if (((List) obj).size() == 0) 
                return true;
            
         else if (obj instanceof Map) 
            if (((Map) obj).size() == 0) 
                return true;
            
         else if (obj instanceof Set) 
            if (((Set) obj).size() == 0) 
                return true;
            
         else if (obj instanceof Object[]) 
            if (((Object[]) obj).length == 0) 
                return true;
            
         else if (obj instanceof int[]) 
            if (((int[]) obj).length == 0) 
                return true;
            
         else if (obj instanceof long[]) 
            if (((long[]) obj).length == 0) 
                return true;
            
        
        return false;
    

    /**
     * 对象组中是否存在 Empty Object
     *
     * @param os 对象组
     * @return
     */
    public static boolean isOneEmpty(Object... os) 
        for (Object o : os) 
            if (isEmpty(o)) 
                return true;
            
        
        return false;
    

    /**
     * 对象组中是否全是 Empty Object
     *
     * @param os
     * @return
     */
    public static boolean isAllEmpty(Object... os) 
        for (Object o : os) 
            if (!isEmpty(o)) 
                return false;
            
        
        return true;
    

    /**
     * 是否为数字
     *
     * @param obj
     * @return
     */
    public static boolean isNum(Object obj) 
        try 
            Integer.parseInt(obj.toString());
         catch (Exception e) 
            return false;
        
        return true;
    

    /**
     * 数字是否为Null或Zero
     *
     * @param obj
     * @return
     */
    public static boolean isNullOrZero(Object obj) 
        if (obj == null) 
            return true;
        

        if (obj instanceof Integer) 
            try 
                return Integer.parseInt(obj.toString()) == 0;
             catch (Exception e) 
                return false;
            
        

        if (obj instanceof Long) 
            try 
                return Long.parseLong(obj.toString()) == 0L;
             catch (Exception e) 
                return false;
            
        
        return false;
    

    /**
     * 如果为空, 则调用默认值
     *
     * @param str
     * @return
     */
    public static Object getValue(Object str, Object defaultValue) 
        if (isEmpty(str)) 
            return defaultValue;
        
        return str;
    

    /**
     * map的key转为小写
     *
     * @param map
     * @return Map<String   ,   Object>
     */
    public static Map<String, Object> caseInsensitiveMap(Map<String, Object> map) 
        Map<String, Object> tempMap = new HashMap<>();
        for (String key : map.keySet()) 
            tempMap.put(key.toLowerCase(), map.get(key));
        
        return tempMap;
    

    /**
     * 获取map中第一个数据值
     *
     * @param <K> Key的类型
     * @param <V> Value的类型
     * @param map 数据源
     * @return 返回的值
     */
    public static <K, V> V getFirstOrNull(Map<K, V> map) 
        V obj = null;
        for (Entry<K, V> entry : map.entrySet()) 
            obj = entry.getValue();
            if (obj != null) 
                break;
            
        
        return obj;
    

    /**
     * 创建StringBuilder对象
     *
     * @return StringBuilder对象
     */
    public static StringBuilder builder(String... strs) 
        final StringBuilder sb = new StringBuilder();
        for (String str : strs) 
            sb.append(str);
        
        return sb;
    

    /**
     * 创建StringBuilder对象
     *
     * @return StringBuilder对象
     */
    public static void builder(StringBuilder sb, String... strs) 
        for (String str : strs) 
            sb.append(str);
        
    


    /**
     *  清除两边的空格
     * @param sql " aa bb cc "
     * @return "aa bb cc"
     */
    public static String clearSideSpaces(String sql) 
        if(Func.isEmpty(sql))
            return "";
        
        return sql.trim();
    
    /**
     *  清除中间的空格
     * @param sql " aa bb
                    cc "

     * @return " aa bb cc "
     */
    public static String clearMiddleSpaces(String sql) 
        if(Func.isEmpty(sql))
            return "";
        
        return sql.replaceAll("[\\\\s]+", " ");
    
    /**
     *  清除(两边+中间)的空格
     * @param sql " aa bb
                    cc "

     * @return "aa bb cc"
     */
    public static String clearExtraSpaces(String sql) 
        if(Func.isEmpty(sql))
            return "";
        
        return sql.replaceAll("[\\\\s]+", " ").trim();
    

    public static void main(String[] args) 
        String sql = " aa bb cc ";
        System.out.println("--"+clearMiddleSpaces(sql)+"--");
    

以上是关于通过spring的AOP的切面,拦截到持久层执行的sql及参数的主要内容,如果未能解决你的问题,请参考以下文章

通过spring的AOP的切面,拦截到持久层执行的sql及参数

通过spring的AOP的切面,拦截到持久层执行的sql及参数

过滤器拦截器AOP切面执行顺序的比较

Spring aop学习整理(spring in action):spring AOP

jeecg系统日志管理

Spring Aop对Controller层拦截失效问题