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

来源:互联网 发布:大学网络课程怎么快进 编辑:程序博客网 时间:2024/06/06 00:25

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

知识点: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@Componentpublic 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-scanbase-package="com.yeepay.g3.core.bc.trade" />

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

@Servicepublic 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:522017-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.Final2017-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()






阅读全文
0 0