springMVC自定义注解,用AOP来实现日志记录的方法

时间:2022-09-18 18:16:16

需求背景

最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中。

为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求。

准备工作

自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号。

自定义注解

在项目下单独建立了一个log包,来存放日志相关的内容

?
1
2
**.common.log.annotation //自定义注解存放位置
**.common.log.aop     //aop工具类存放位置

在annotation包下面新建自定义注解类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package **.common.log.annotation;
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 XXXOperateLog {
  /**
   * 操作类型描述
   * @return
   */
  String operateTypeDesc() default "";
  /**
   * 操作类型
   * @return
   */
  long operateType() default -1;
  /**
   * 模块编码
   * @return
   */
  String moudleCode() default "M30";
  /**
   * 模块名称
   * @return
   */
  String moudleName() default "XX模块";
  /**
   * 业务类型
   * @return
   */
  String bussType() default "";
  /**
   * 业务类型描述
   * @return
   */
  String bussTypeDesc() default "";
}

在aop包下新建XXXOperateLogAop

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package **.common.log.aop;
import ** ;//省略
@Aspect
@Component
public class XXXOperateLogAop{
  @Autowired
  SystemLogService systemLogService;
   HttpServletRequest request = null;
   Logger logger = LoggerFactory.getLogger(XXXOperateLogAop.class);
  ThreadLocal<Long> time = new ThreadLocal<Long>();
  //用于生成操作日志的唯一标识,用于业务流程审计日志调用
  public static ThreadLocal<String> tag = new ThreadLocal<String>();
  //声明AOP切入点,凡是使用了XXXOperateLog的方法均被拦截
  @Pointcut("@annotation(**.common.log.annotation.XXXOperateLog)")
  public void log() {
    System.out.println("我是一个切入点");
  }
  /**
   * 在所有标注@Log的地方切入
   * @param joinPoint
   */
  @Before("log()")
  public void beforeExec(JoinPoint joinPoint) {
    time.set(System.currentTimeMillis()); 
    info(joinPoint);
    //设置日志记录的唯一标识号
    tag.set(UUID.randomUUID().toString());
    request= ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  }
  @After("log()")
  public void afterExec(JoinPoint joinPoint) {
    MethodSignature ms = (MethodSignature) joinPoint.getSignature();
    Method method = ms.getMethod();
    logger.debug("标记为" + tag.get() + "的方法" + method.getName()
        + "运行消耗" + (System.currentTimeMillis() - time.get()) + "ms"); 
  }
  //在执行目标方法的过程中,会执行这个方法,可以在这里实现日志的记录
  @Around("log()")
  public Object aroundExec(ProceedingJoinPoint pjp) throws Throwable {
    Object ret = pjp.proceed();
    try {
      Object[] orgs = pjp.getArgs();
      SystemLog valueReturn = null;
      for (int i = 0; i < orgs.length; i++) {
        if(orgs[i] instanceof SystemLog){
          valueReturn = (SystemLog) orgs[i];
        
      
      if(valueReturn==null){
        valueReturn = new SystemLog();
      }
      if(valueReturn!=null&&request!=null){
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        //获取注解的操作日志信息
        XXXOperateLog log = method.getAnnotation(XXXOperateLog.class);
        String businessType = log.bussType();
        String businessDesc = log.bussTypeDesc();
        HashMap requestMap = ServletUtils.getParametersToHashMap(request) ;
        //从参数中寻找业务类型
        if(businessType.equals(""))
        {
          Object objBusinessType = requestMap.get("business_type");
          businessType = objBusinessType == null ? "" : objBusinessType.toString();
        }
        //从执行结果的申请单中找业务类型
        Object apply = request.getAttribute("apply") ;
        if(apply != null){
          JSONObject obj = JSONFactory.toJSONAbstractEntity(apply);
          if(obj != null)
          {
            valueReturn.setOtherDesc("申请单号:"+obj.getString("apply_no"));
            if(businessType.equals(""))
            {
              businessType = obj.getString("business_type");
            }
          }
        }
        //从方法的执行过程参数中找业务类型(一般是手动设置)
        if(businessType.equals(""))
        {
          businessType = (String) request.getAttribute("business_type");
          businessType = businessType == null ? "" : businessType;
        }
        if(!businessType.equals("") && businessDesc.equals(""))
        {
          businessDesc = XXXSysConstant.BUSINESS_TYPE.getName(businessType);
        }
        valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType));
        valueReturn.setBussTypeDesc(businessDesc);
        valueReturn.setMoudleCode(log.moudleCode());
        valueReturn.setMoudleName(log.moudleName());
        valueReturn.setOperateResult(XXXSysConstant.YesOrNo.YES);
        valueReturn.setOperateType(log.operateType());
        valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId());
        valueReturn.setOperateTypeDesc(log.operateTypeDesc());
        valueReturn.setRequestIp(getRemoteHost(request));
        valueReturn.setRequestUrl(request.getRequestURI());
        valueReturn.setServerIp(request.getLocalAddr());
        valueReturn.setUids(tag.get());
        //保存操作日志
        systemLogService.saveSystemLog(valueReturn);
      }else{
        logger.info("不记录日志信息");
      }
      //保存操作结果 
    } catch (Exception e) {
      e.printStackTrace();
    }
    return ret;
  }
  //记录异常日志
  @AfterThrowing(pointcut = "log()",throwing="e")
  public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
    try {
      info(joinPoint);
      Object[] orgs = joinPoint.getArgs();
      SystemLog valueReturn = null;
      for (int i = 0; i < orgs.length; i++) {
        if(orgs[i] instanceof SystemLog){
          valueReturn = (SystemLog) orgs[i];
        }     
      }
      if(valueReturn==null){
        valueReturn = new SystemLog();
      }
      if(valueReturn!=null&&request!=null){
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        XXXOperateLog log = method.getAnnotation(XXXOperateLog.class);
        String businessType = log.bussType();
        String businessDesc = log.bussTypeDesc();
        if(businessType.equals(""))
        {
          Object objBusinessType = ServletUtils.getParametersToHashMap(request).get("business_type");
          businessType = objBusinessType == null ? "" : objBusinessType.toString();
          businessDesc = XXXSysConstant.BUSINESS_TYPE.getName(businessType);
        }
        valueReturn.setBussType(XXXSysConstant.BUSINESS_TYPE.getNumber(businessType));
        valueReturn.setBussTypeDesc(businessDesc);
        valueReturn.setMoudleCode(log.moudleCode());
        valueReturn.setMoudleName(log.moudleName());
        valueReturn.setOperateType(log.operateType());
        valueReturn.setOperateTypeDesc(log.operateTypeDesc());
        valueReturn.setInputUserId(((UserContext)WebUtils.getSessionAttribute(request, "XXXuserContext")).getSysUser().getId());
        valueReturn.setOperateResult(XXXSysConstant.YesOrNo.NO);
        String errMes = e.getMessage();
        if(errMes!=null && errMes.length()>800){
          errMes = errMes.substring(0, 800);
        }
        valueReturn.setErrorMessage(errMes);
        valueReturn.setRequestIp(getRemoteHost(request));
        valueReturn.setRequestUrl(request.getRequestURI());
        valueReturn.setServerIp(request.getLocalAddr());
        valueReturn.setUids(tag.get());
        systemLogService.saveSystemLog(valueReturn);
      }else{
        logger.info("不记录日志信息");
      }
    } catch (Exception e1) {
      e1.printStackTrace();
    }
  }
  private void info(JoinPoint joinPoint) {
    logger.debug("--------------------------------------------------");
    logger.debug("King:\t" + joinPoint.getKind());
    logger.debug("Target:\t" + joinPoint.getTarget().toString());
    Object[] os = joinPoint.getArgs();
    logger.debug("Args:");
    for (int i = 0; i < os.length; i++) {
      logger.debug("\t==>参数[" + i + "]:\t" + os[i].toString());
    }
    logger.debug("Signature:\t" + joinPoint.getSignature());
    logger.debug("SourceLocation:\t" + joinPoint.getSourceLocation());
    logger.debug("StaticPart:\t" + joinPoint.getStaticPart());
    logger.debug("--------------------------------------------------");
  }
  /**
   * 获取远程客户端Ip
   * @param request
   * @return
   */
  private String getRemoteHost(javax.servlet.http.HttpServletRequest request){
    String ip = request.getHeader("x-forwarded-for");
    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
      ip = request.getHeader("Proxy-Client-IP");
    }
    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
      ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
      ip = request.getRemoteAddr();
    }
    return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
  
}

修改配置文件spring-mvc.xml,添加如下配置

?
1
2
3
4
5
6
7
<!-- 开启AOP拦截 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<mvc:annotation-driven />
<!-- 定义Spring描述Bean的范围 -->
<context:component-scan base-package="**.common.log" >
   <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

需要注意的是,上述配置必须放在同一个xml文件里面,要么spring-mvc.xml,要么spring-context.xml,否则可能不生效,暂时还未查明是为什么。

注解的使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@XXXOperateLog(
      bussType=XXXSysConstant.BUSINESS_TYPE.YYYY
      ,bussTypeDesc="业务类型描述"
      ,operateType = XXXSysConstant.LogOperateType.QUERY
      ,operateTypeDesc = "操作描述"
  )
  @RequestMapping(value = "/**/**/queryXXXXX4DataGrid.json", method = RequestMethod.POST)
  public void queryXXXXX4DataGrid(HttpServletRequest request, HttpServletResponse arg1, Model model, Writer writer)
  {
    logger.info("==========验票查询(出库)交易信息 开始=====================");
    try {
      //do something for business
    } catch (SystemException se) {
      throw se;
    } catch (BusinessException be) {
      throw be;
    } catch (Exception e) {
      throw new SystemException(e);
    }
  }

以上这篇springMVC自定义注解,用AOP来实现日志记录的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/yang_lover/article/details/53037323