一:

spring-mvc.xml:

<!--配置日志切面 start,必须与mvc配置在同一个配置文件,否则无法切入Controller层-->
<!-- 声明自动为spring容器中配置@aspectj切面的bean创建代理 ,织入切面 -->
<context:component-scan base-package="org.jeecgframework.core.aop" />
<aop:aspectj-autoproxy />
<aop:config proxy-target-class="true"></aop:config>
<!--配置日志切面 end-->

 二:
LoginLog.java:
import java.lang.annotation.*;

/**
 * <ul>
 *                <li>使用运行时参数值(用法:1-->#p{下标} 2-->#{形参名称.属性名} 3-->${形参名称.属性名})</li>
 *                <li>第一种适合参数为基本类型或String</li>
 *                <li>第二种适合参数为实体类</li>
 *                <li>第三种适合参数为实体类List集合,会将属性按照","隔开再返回</li>
 *                <li>备注:</li>
 *                <li>需要多个值用","隔开,前后可加说明性文字,举例:</li>
 *                <li>@AdminLog(module = "更新#p{0}模块",content = "更新地址:#{bcContact.address}完毕,电话:${bcContactSubList.phone}完毕")
 *                </li>
 *                <li>
 public void updateMain(String test,BcContactEntity bcContact,
 List<BcContactSubEntity> bcContactSubList)</li>
 <li>module结果-->更新测试模块,content结果-->更新地址:****完毕,电话:***,***完毕</li>
 *            <ul/>
 */
@Retention(RetentionPolicy.RUNTIME)  //注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD) //注解到方法
@Documented //注解包含在javadoc中
@Inherited //注解可以被继承
public @interface LoginLog {
    /** 登录时长 */
    String time() default "";

    /** 登录类型 */
    String type() default "";

}

三:
LoginLogAspect.java:
import com.aiitec.log.entity.BcLoginLogEntity;
import com.aiitec.log.service.BcLoginLogServiceI;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecgframework.core.annotation.LoginLog;
import org.jeecgframework.core.util.AspectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class LoginLogAspect {
    @Autowired
    private BcLoginLogServiceI bcLoginLogServiceI;
    private static final Logger log = LoggerFactory.getLogger(LoginLogAspect.class);

    // 配置织入点
    @Pointcut("@annotation(org.jeecgframework.core.annotation.LoginLog)")
    public void logPointCut() {
    }

    /**
     * 前置通知 用于拦截操作,在方法返回后执行
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()")
    public void doAfter(JoinPoint joinPoint) {
        handleLog(joinPoint,null);
    }

    /**
     * 拦截异常操作,有异常时执行
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e);
    }

    private void handleLog(JoinPoint joinPoint,Exception e) {
        try {
            // 获得注解
            LoginLog controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }
            // 获得方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            String type = controllerLog.type();
            String time = controllerLog.time();
            //打印日志,如有需要还可以存入数据库
            log.info(">>>>>>>>>>>>>模块名称:{}",time);
            log.info(">>>>>>>>>>>>>操作名称:{}",type);
            log.info(">>>>>>>>>>>>>类名:{}",className);
            log.info(">>>>>>>>>>>>>方法名:{}",methodName);
            //获取所有的参数
            Object[] args = joinPoint.getArgs();
            String resultTime = AspectUtil.getAttributeValue(className,methodName,args,time);
            String resultType = AspectUtil.getAttributeValue(className,methodName,args,type);


            log.info(">>>>>>>>>>>>>属性值:{}",resultTime);
            //保存登录日志信息到数据库
            BcLoginLogEntity bcLoginLogEntity=new BcLoginLogEntity();
            bcLoginLogEntity.setContent(resultType+","+resultTime);
            bcLoginLogEntity.setLoginType(0);
            bcLoginLogEntity.setLoginTime(3600);
            bcLoginLogServiceI.save(bcLoginLogEntity);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==登录日志后置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private static LoginLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(LoginLog.class);
        }
        return null;
    }

}

四:
AspectUtil.java
import org.apache.commons.lang.StringUtils;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

public class AspectUtil {
    /**
     * 判断是否为实体类使用
     */
    public final static HashMap<String, Class> IS_ENTITY_MAP = new HashMap<String, Class>() {
        {
            put("java.lang.Integer", int.class);
            put("java.lang.Double", double.class);
            put("java.lang.Float", float.class);
            put("java.lang.Long", long.class);
            put("java.lang.Short", short.class);
            put("java.lang.Boolean", boolean.class);
            put("java.lang.Char", char.class);
        }
    };
    /**
     * 解析实体类,获取实体类中的属性
     * @param obj
     * @param arg
     * @return
     */
    public static String getFieldsValue(Object obj,String arg) {
        //通过反射获取所有的字段,getFileds()获取public的修饰的字段
        //getDeclaredFields获取private protected public修饰的字段
        Field[] fields = obj.getClass().getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        for (Field f : fields) {
            //在反射时能访问私有变量
            f.setAccessible(true);
            try {
                if (f.get(obj)!=null){
                    if(arg.equals(f.getName())){//找到对应属性
                        sb.append(f.get(obj) + "");//取值
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    /**
     * 获取指定参数的值
     * @param className 类名
     * @param methodName 方法名
     * @param args 参数数组
     * @param key 自定义字段(用法:1-->#p{下标} 2-->#{形参名称.属性名} 3-->${形参名称.属性名})
     *            <ul>
     *                <li>第一种适合参数为基本类型或String</li>
     *                <li>第二种适合参数为实体类</li>
     *                <li>第三种适合参数为实体类List集合,会将属性按照","隔开再返回</li>
     *            <ul/>
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     */
    public static String getAttributeValue(String className,String methodName,Object[] args,String key) throws ClassNotFoundException, NoSuchMethodException {
        Class<?>[] classes = new Class[args.length];
        for (int k = 0; k < args.length; k++) {
            if (!args[k].getClass().isPrimitive()) {
                //获取的是封装类型而不是基础类型
                String result = args[k].getClass().getName();
                Class s = IS_ENTITY_MAP.get(result);
                if (result.equals("java.util.ArrayList")){//如果是ArrayList替换为List,否则找不到该方法(因为方法声明为List,传入的是ArrayList)
                    classes[k] = Class.forName("java.util.List");
                }else{
                    classes[k] = s == null ? args[k].getClass() : s;
                }
            }
        }
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        //获取指定的方法,第二个参数可以不传,但是为了防止有重载的现象,还是需要传入参数的类型
        Method method = Class.forName(className).getMethod(methodName, classes);
        String[] parameterNames = pnd.getParameterNames(method);


        StringBuffer sb=new StringBuffer();
        if (StringUtils.isNotBlank(key)){
            String[] vars=key.split(",");
            for(String var:vars){
                if(var.indexOf("#p")>-1){//包含#p
                    String index = var.substring(var.indexOf("{") + 1,var.indexOf("}"));
                    sb.append(var.replaceAll("#p\\{.*\\}",args[Integer.valueOf(index)]+"")+",");//通过注解属性值中的"#p[n]"n的值,确定参数下标,获取参数值

                }else if(var.indexOf("#")>-1){//包含#

                    String paramName= var.substring(var.indexOf("{")+1,var.indexOf("."));//参数名
                    int index= Arrays.asList(parameterNames).indexOf(paramName);//参数下标
                    String temp=getFieldsValue(args[index],var.substring(var.indexOf(".")+1,var.indexOf("}")));//传入参数对象和属性名
                    sb.append(var.replaceAll("#\\{.*\\}",temp)+",");
                }else if(var.indexOf("$")>-1){//包含$
                    String paramName= var.substring(var.indexOf("{")+1,var.indexOf("."));//参数名
                    String attrName=var.substring(var.indexOf(".")+1,var.indexOf("}"));//属性名
                    int index=Arrays.asList(parameterNames).indexOf(paramName);//参数下标
                    final ArrayList argList = (ArrayList<?>) args[index];
                    StringBuffer temp=new StringBuffer();
                    for (Object o : argList) {
                        String keys=getFieldsValue(o,attrName);
                        temp.append(keys+",");
                    }
                    if(temp.length()>0){
                        if (',' == temp.charAt(temp.length() - 1)){
                            temp.deleteCharAt(temp.length() - 1);//去掉最后一个","
                        }
                    }
                    sb.append(var.replaceAll("\\$\\{.*\\}",temp.toString())+",");
                }else{//普通字符串
                    sb.append(var+",");
                }
            }
            if(sb.length()>0){
                if (',' == sb.charAt(sb.length() - 1)){
                    sb.deleteCharAt(sb.length() - 1);//去掉最后一个","
                }
            }
        }

        return sb.toString();
    }
}