spring 自定义注解实现日志统一处理

需求:

通过注解的方式 统一处理controller和service的日志(实现上可能不太严谨,主要是实现流程)

原理:

先自定义注解。用aop切面拦截方法的使用,看是否有对应的自定义的注解,如果有,在切面中进行日志的统一打印,可以获取到加了注解方法的类名、方法名、参数。

如果想每个方法传进来不同信息,可以在自定义注解里写上参数,这样在使用时就可以带进来不同信息。例如,spring自带的注解@Resource(name=“”)。

1.controller、service自定义注解

package com.qunar.fresh.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * ControllerLog
 *
 * @author chenliclchen
 * @date 17-10-12 上午11:35
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ControllerLog {
    String description() default "";
}
package com.qunar.fresh.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * ServiceLog
 *
 * @author chenliclchen
 * @date 17-10-12 上午11:34
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceLog {
    String description() default "";
}
可以通过需求加多个如同description的注解。
2.写aop进行切面
package com.qunar.fresh.aop;
 
import com.qunar.fresh.annotation.ControllerLog;
import com.qunar.fresh.annotation.ServiceLog;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;
 
/**
 * LogAop
 *
 * @author chenliclchen
 * @date 17-10-12 上午11:40
 */
@Slf4j
@Component
@Aspect
public class LogAop {
 
 @Pointcut("@annotation(com.qunar.fresh.annotation.ServiceLog)")
 public void serviceAspect(){
    log.info("service 日志");
 }
 
 @Pointcut("@annotation(com.qunar.fresh.annotation.ControllerLog)") //
 public void controllerAspect(){
    log.info("controller 日志");
 }
 
 @Before("controllerAspect() || serviceAspect()")
 public void doBefore(JoinPoint joinPoint){
    String className = joinPoint.getTarget().getClass().getName();
    String methodName = joinPoint.getSignature().getName();
    String description = getDescription(joinPoint);
    Object[] args = joinPoint.getArgs();
    String logResult = new StringBuilder().append(description).append("操作: ")
    .append(className).append("类的").append(methodName).append("()方法被请求,参数为: ")
    .append(Arrays.toString(args)).toString();
 
    log.info(logResult);
 }
 
// @AfterThrowing(pointcut = "controllerAspect() || serviceAspect()", throwing = "e")
// public void doAfterThrowing(JoinPoint joinPoint, Throwable e){
//      log.error("统一处理异常日志 {}", e);
// }
 
 private String getDescription(JoinPoint joinPoint){
    Class<?> className = joinPoint.getTarget().getClass();
    //得到类的所有方法
    Method[] methods = className.getMethods();
    String methodName = joinPoint.getSignature().getName();
    Object[] args = joinPoint.getArgs();
    //在类所有方法中找到对应的被拦截到的方法,通过方法的名字和参数的个数。
    for(Method method: methods){
        if(method.getName().equals(methodName)){
            Class<?>[] parameterTypes = method.getParameterTypes();
            //参数个数是否相等。
            if(parameterTypes.length == args.length){
                ControllerLog controllerLog = method.getAnnotation(ControllerLog.class);
                if(controllerLog != null){
                    return controllerLog.description();
                }else{
                    ServiceLog serviceLog = method.getAnnotation(ServiceLog.class);
                    return serviceLog != null? serviceLog.description(): "";
                }
            }
        }
    }
    return "";
 }
}
只写了before,如果有需求还可以写上after等。
3.xml配置
<aop:aspectj-autoproxy proxy-target-class="true"/>

需要注意的是,如果项目的spring-mvc.xml和applicationContext.xml是两个文件,而你的切面是同时面向controller和service的,

两个配置文件里都需要加上这行配置。

4.使用
package com.qunar.fresh.controller;
 
import com.qunar.fresh.annotation.ControllerLog;
import com.qunar.fresh.annotation.LoginRequired;
import com.qunar.fresh.service.TestService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
 
import javax.annotation.Resource;
 
/**
 * TestController
 *
 * @author chenliclchen
 * @date 17-10-11 下午9:05
 */
//@LoginRequired
@Controller
@RequestMapping("/test")
public class TestController {
 
 @Resource
 TestService testService;
 
 @RequestMapping("/one/{name}")
 @ControllerLog(description="测试用aop统一处理日志")
 public String test(@PathVariable String name){
    testService.printHello(name);
    return "test";
 }
}
package com.qunar.fresh.service;
 
/**
 * TestService
 *
 * @author chenliclchen
 * @date 17-10-12 下午6:32
 */
public interface TestService {
 void printHello( String name);
}
 
package com.qunar.fresh.service.impl;
 
import com.qunar.fresh.annotation.ServiceLog;
import com.qunar.fresh.service.TestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
 
/**
 * TestServiceImpl
 *
 * @author chenliclchen
 * @date 17-10-12 下午6:33
 */
@Slf4j
@Service
public class TestServiceImpl implements TestService {
 
 @Override
 @ServiceLog(description = "测试aop统一处理service日志输出")
 public void printHello(String name) {
    log.info("hello {}", name);
 }
}
这样,当浏览器输入: http://localhost:8080/test/one/who  时,会有如下输出日志:
INFO com.qunar.fresh.aop.LogAop - 测试用aop统一处理日志操作: com.qunar.fresh.controller.TestController类的test()方法被请求,参数为: [who]
INFO com.qunar.fresh.aop.LogAop - 测试aop统一处理service日志输出操作: com.qunar.fresh.service.impl.TestServiceImpl类的printHello()方法被请求,参数为: [who]
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值