zoukankan      html  css  js  c++  java
  • Spring自定义注解配置切面实现日志记录

      一:

    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();
    }
    }
     
  • 相关阅读:
    Supervisor 管理进程,Cloud Insight 监控进程,完美!
    【灵魂拷问】你为什么要来学习Node.js呢?
    Web数据交互技术
    请求与上传文件,Session简介,Restful API,Nodemon
    Express服务器开发
    HTTP协议,到底是什么鬼?
    大学我都是自学走来的,这些私藏的实用工具/学习网站我贡献出来了,建议收藏精品推荐
    Node.js安装使用-VueCLI安装使用-工程化的Vue.js开发
    React开发环境准备
    【可视化】Vue基础
  • 原文地址:https://www.cnblogs.com/zou-rong/p/10366736.html
Copyright © 2011-2022 走看看