注解结合AOP在方法前后打印日志

亮点:直接在方法上添加一行注解,就可以实现统计方法的执行时间,另外根据注解参数中属性来控制是否进行方法入参的校验

知识点:java注解+AOP+java 反射机制

特别注意点:如果要进行方法入参的校验,返回参数类定义中必须要有  A(Stirng a,String b)的构造方法,否则会报错,如果方法没有返回参数 请修改红色部分,为防止隐私我已经将所有类中包路径去除,使用时请自行修改

步骤一:创建注解类

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 计算方法调用时间注解
 * name是业务名称描述,比如:调用下单接口
 * isCheckParams 是否校验接口入参,如果是会调用BeanValidator.validate(obj);进行参数的校验
 * Created by yunpeng.zhao on 2017/8/15.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TimeDiff {
    /**
     * 业务名称描述
     * @return
     */
    String name() default "";

    /**
     * 是否对请求参数进行校验
     * @return
     */
    boolean isCheckParams() default false;
}

步骤二:定义aop实现类

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;

/**
 * 计算方法调用时间切面
 * @author yunpeng.zhao
 * @version $Id TimeDiffAop.java, v 0.1 2017-08-15 上午11:25 yp-tc-m-2651 Exp $$
 */
@Aspect
@Component
public class TimeDiffAop {
    /**
     * 记录耗时变量
     */
    ThreadLocal<Long> time = new ThreadLocal<Long>();

    private static final Logger LOGGER = LoggerFactory.getLogger(TimeDiffAop.class);


    /**
     * 在方法前记录时间并根据注解,是否校验参数
     * @param joinPoint
     * @return
     */
    @Before("@annotation(com.yeepay.g3.core.bc.trade.aop.TimeDiff)")
    public void beforeMethod(JoinPoint joinPoint){

    }

    /**
     * 方法执行前后拦截
     * @param pjp
     * @return
     */
    @Around("@annotation(TimeDiff)")
    public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
        time.set(System.currentTimeMillis());
        //获取TimeDiff注解中是否需要校验参数
        try {
            Method method = getObjMethod(pjp);
            String name = method.getAnnotation(TimeDiff.class).name();
            LOGGER.info("{}开始执行,开始时间:{}",name,DateUtils.getTimeStampStr(new Date(time.get())));
            boolean isCheckParams = method.getAnnotation(TimeDiff.class).isCheckParams();
            if (isCheckParams){
                Type type = method.getGenericReturnType();
                Object[] args = pjp.getArgs();
                LOGGER.info("{}请求参数:{}",name, JSONUtils.toJsonString(args));
                for (Object o : args) {
                    try {

                        BeanValidator.validate(o);
                    } catch (IllegalArgumentException e) {
                        LOGGER.error("参数验证错误", e);
                        return getResponseObj(type, BCErrorInfoConstants.PARAM_VALIDATE_ERROR, e.getMessage());

                    }
                }
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return pjp.proceed();
    }

    private Method getObjMethod(JoinPoint joinPoint) throws ClassNotFoundException, NoSuchMethodException {
        String targetName = joinPoint.getTarget().getClass().getName();
        Class targetClass = Class.forName(targetName);
        MethodSignature ms= (MethodSignature)joinPoint.getSignature();
        String methodName = ms.getMethod().getName();
        Class<?>[] par=ms.getParameterTypes();
        return targetClass.getMethod(methodName,par);
    }
    /*此处根据方法的返回类型进行拼装返回,所以在返回参数中必须有构造函数 	
    public Object getResponseObj(Type type,String retCode,String retMsg){

        try {
            if (type != null){
                String returnName = type.toString().replace("class ","");
                LOGGER.info("返回类型是:{}",returnName);
                Class cls = Class.forName(returnName);
                Constructor con = cls.getConstructor(String.class, String.class);
                Object responseObj = con.newInstance(retCode, retMsg);
                return responseObj;
            }
            return null;

        } catch (Exception e) {
            throw BCException.PAY_PARAM_CVRT_ERROR;
        }
    }

    /**
     * 在方法后打印总耗时
     * @param joinPoint
     */
    @After("@annotation(com.yeepay.g3.core.bc.trade.aop.TimeDiff)")
    public void afterMethod(JoinPoint joinPoint){
        try {
            Method method = getObjMethod(joinPoint);
            String name = method.getAnnotation(TimeDiff.class).name();
            LOGGER.info("{}执行结束,结束时间:{},总耗时:{}ms",name, DateUtils.getLongDateStr(),System.currentTimeMillis()-time.get());
        }  catch (Exception e) {
            LOGGER.error("打印总耗时出现异常:{}",e);
        }
    }

}

步骤三 在spring配置文件中开启aop 并能扫描到切面类

<aop:aspectj-autoproxy proxy-target-class="true"/>
<context:component-scan	base-package="com.yeepay.g3.core.bc.trade" />

步骤四:在方法上添加这个牛逼的注解就可以了

@Service
public class CompensateOrderFacadeImpl implements CompensateOrderFacade{

    private static final Logger LOGGER = LoggerFactory.getLogger(CompensateOrderFacadeImpl.class);
    @Autowired
    private CompensateOrderBiz compensateOrderBiz;

    @TimeDiff(name = "赔付反查接口",isCheckParams = true)
    @Override
    public CompensateQueryResponseDTO queryOrgOrder(CompensateQueryRequestDTO query) {
        CompensateQueryResponseDTO response = new CompensateQueryResponseDTO(BCException.FAIL.getDefineCode(),BCException.FAIL.getMessage());

        try {
            //1.反查流程
            response = compensateOrderBiz.queryOrgOrder(query);

        } catch (BCException e) {
            LOGGER.error("赔付反查接口参数校验异常:{}",e);
            response.setRetCode(e.getDefineCode());
            response.setRetMsg(e.getMessage());
        } catch (Exception e) {
            LOGGER.error("赔付反查接口出现系统异常:{}",e);
            response.setRetCode(BCErrorInfoConstants.TRADE_COMPENSATE_QUERY_ERROR);
            response.setRetMsg(e.getMessage());
        }

        return response;
    }

    @TimeDiff(name = "赔付接口",isCheckParams = true)
    @Override
    public CompensateOrderResponseDTO createCompensateOrder(CompensateOrderDTO order) {
        CompensateOrderResponseDTO responseDTO = new CompensateOrderResponseDTO();

        return null;
    }
}

步骤五;参数校验类

import java.util.Locale;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.springframework.context.i18n.LocaleContextHolder;

public class BeanValidator {

	private static Validator validator;
	static {
		ValidatorFactory validatorFactory = Validation
				.buildDefaultValidatorFactory();
		validator = validatorFactory.getValidator();
	}
	public static String getMergedMessage(Set set) {
		StringBuilder sb = new StringBuilder("");
		for (Object obj : set) {
			if (obj instanceof ConstraintViolation) {
				ConstraintViolation constraintViolation = (ConstraintViolation) obj;
				sb.append(constraintViolation.getPropertyPath());
				sb.append(" ");
				sb.append(constraintViolation.getMessage());
				sb.append("; ");
			}
		}
		return sb.toString();
	}

	/**
	 * 根据Bean中的注解配置验证Bean的参数合法性
	 * 
	 * @param <E>
	 * @param obj
	 *            待验证对象
	 */
	@SuppressWarnings("unchecked")
	public static <E> void validate(Object obj) {
		Set<ConstraintViolation<E>> set = validator.validate((E) obj);
		if (set.size() != 0) {
			throw new IllegalArgumentException("验证参数合法性时出现异常["
					+ getMergedMessage(set)
					+ "]");
		}
	}

}


大功告成:看效果图

2017-08-16 09:28:52,768 - com.yeepay.g3.core.bc.trade.aop.TimeDiffAop -10764 [main] INFO   - 赔付反查接口开始执行,开始时间:2017-08-16 09:28:52
2017-08-16 09:28:52,775 - com.yeepay.g3.core.bc.trade.aop.TimeDiffAop -10771 [main] INFO   - 赔付反查接口请求参数:[{"customerRequestId":"2017081000004","customerNumber":"10040007799"}]
2017-08-16 09:28:52,795 - org.hibernate.validator.util.Version -10791 [main] INFO   - Hibernate Validator 4.2.0.Final
2017-08-16 09:28:53,005 - com.yeepay.g3.core.bc.trade.aop.TimeDiffAop -11001 [main] INFO   - 赔付反查接口执行结束,结束时间:2017-08-16 09:28:53,总耗时:244ms

相关参考文章:

http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html    注解相关

http://www.xdemo.org/springmvc-aop-annotation/   AOP相关

http://blog.csdn.net/meiyang1990/article/details/50562046  获取method方法

http://blog.csdn.net/shenyunsese/article/details/51133065  获取注解属性,此处主要是直接通过 下面代码 无法获取到annotation,怀疑跟JDK版本有关

  MethodSignature ms=(MethodSignature) joinPoint.getSignature();
  Method method=ms.getMethod();
   method.getAnnotation(Log. class ).name()






  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值