`
chenruieye
  • 浏览: 38128 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

记我的第一次spring aop项目实践

    博客分类:
  • Java
阅读更多

一、需求背景。

 

1、刚来公司1个多月,接到一个新的项目需求,需要开发喜力啤酒的app相关接口,因为相对比较独立,具体功能领导安排我一个人完成。

 

然后就愉快的撸起了代码,撸啊撸,撸啊撸,时间过得真快,过了1周,就撸完了.屁颠屁颠跑过去给领导说撸完了,可以上线了。

 

领导思考了片刻,为保障上线需要,需要监控喜力所有接口的日志部分,包括请求参数和返回参数的情况。让我把喜力所有接口的相关调用日志也加上!

 

Oh,我滴个乖乖,这个咋整啊,难道要去每个接口加参数,那么多接口,都猴年马月去了。后来请教了一下公司的前辈,问他们怎么做,他们让我用spring aop来弄,只需要在需要监控的方法加上相关注解就可以了。于是开始了我的spring aop之旅。

 

 

二、希望实现的效果。

 

1、哪个接口需要记录日志,就在哪个日志前加一个注解,这样能做到比较灵活。毕竟不是所有接口都要加。

 

2、不改变已经写好了的接口的任何业务逻辑,不穿插代码。

 

3、对调用者调用服务端的接口依然和修改前调用方式一样,让client无感监控动作。

 

三、结论:

1、能满足第二点提到的要求,用spring aop再合适不过了。

 

2、首先我们为什么需要做日志管理,在现实的上线中我们经常会遇到系统出现异常或者问题。这个时候就马上打开CRT或者SSH连上服务器拿日子来分析。受网络的各种限制。于是我们就想为什么不能直接在管理后台查看报错的信息呢。于是日志管理就出现了。

         其次个人觉得做日志管理最好的是Aop,有的人也喜欢用拦截器。都可以,在此我重点介绍我的实现方式。

         Aop有的人说拦截不到Controller。有的人说想拦AnnotationMethodHandlerAdapter截到Controller必须得拦截org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。

首先Aop可以拦截到Controller的,这个是毋容置疑的其次须拦截AnnotationMethodHandlerAdapter也不是必须的。最起码我没有验证成功过这个。我的Spring版本是4.0.3。

         Aop之所以有的人说拦截不到Controller是因为Controller被jdk代理了。我们只要把它交给cglib代理就可以了。

 

 

四、操作步骤。

 

第一步:定义注解:

 

package com.biz.core.util.annotation;

import java.lang.annotation.*;

//@Documented
//@Retention(RetentionPolicy.RUNTIME)
//@Target({ElementType.METHOD})

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebMapControllerLog {
    String module()  default "";
    String methods()  default "";
}

 

 

第二步:定义一个切点类

 

package com.biz.web.controller.util;


import com.alibaba.fastjson.JSONObject;
import com.biz.core.bean.MessageObject;
import com.biz.core.bean.enums.MessageType;
import com.biz.core.entity.WebMapLog;
import com.biz.core.util.annotation.HeineKenControllerLog;
import com.biz.core.util.annotation.WebMapControllerLog;
import com.biz.core.util.mq.MqUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@Aspect
@Component
public class WebMapControllerLogAspect {

    //本地异常日志记录对象
    private static final Logger logger = LoggerFactory.getLogger(WebMapControllerLogAspect.class);


    /**
     * 切入点,加了注解SfaMethod
     */
    // @Pointcut(value = "@annotation(com.biz.core.util.gatewaySupport.annotation.SfaMethod)")
    //@Pointcut(value = "@annotation(com.biz.core.util.annotation.HenineKenAnnotation)")
    @Pointcut("@annotation(com.biz.core.util.annotation.WebMapControllerLog)")
    public void serviceAspect() {
        System.out.println("我是一个切入点");
    }


    /**
     *     * 前置通知:目标方法执行之前执行以下方法体的内容      * @param jp    
     */
//    @Before("execution(* com.biz.service.rest.henineken.impl.*.*(..))")
// @Before(value = "serviceAspect()")
//    public void beforeMethod(JoinPoint joinPoint) {
//        String methodName = joinPoint.getSignature().getName();
//    begintime = System.currentTimeMillis();
//
//        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        HttpSession session = request.getSession();
//
//        String url = request.getRequestURL().toString();
//        String method = request.getMethod();
//        String uri = request.getRequestURI();
//        String a = request.getQueryString();
////        request.
//
//        Map<String,String[]> ss = request.getParameterMap();
//
//        Enumeration<String> s1 = request.getParameterNames();
//
//                //Map<String,Object> pramMap = request.getParameterMap();
//
//        logger.info(a);
//
//
//
////        //读取session中的用户 等其他和业务相关的信息,比如当前用户所在应用,以及其他信息, 例如ip
////        String ip = request.getRemoteAddr();
////
////
////        JSONObject jdata = JSONObject.parseObject(data);
////        try {
////            logger.info("doBefore enter。 任何时候进入连接点都调用");
////            logger.info("method requested:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
////            logger.info("method description:" + getServiceMethodDescription(joinPoint));
////            logger.info("remote ip:" + ip);
////            //日志存入数据库
////
////            logger.info("doBefore end");
////        }  catch (Exception e) {
////            logger.info("doBefore exception");
////            logger.info("exceptionMsg={}", e.getMessage());
////        }
//
//        System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + JSONObject.toJSONString(joinPoint.getArgs()));
//    }
//
//    /**
//     *     * 返回通知:目标方法正常执行完毕时执行以下代码     * @param jp     * @param result    
//     */
////    @AfterReturning( value = "execution(* com.biz.service.rest.henineken.impl.*.*(..))", returning = "result")
// @AfterReturning(pointcut = "serviceAspect()", returning = "result")
//    public void afterReturningMethod(JoinPoint jp, Object result) {
//        String methodName = jp.getSignature().getName();
//
//        System.out.println("【返回通知1】the method 【" + methodName + "】 ends with 【" + result + "】");
//
//    CrmReqLog crmReqLog=new CrmReqLog();
//    crmReqLog.setContent(JSONObject.toJSONString(result));
//    crmReqLog.setInfo("格式化请求内容");
//    crmReqLog.setUri("workCircleSummaryLikeService.1111");
//    crmReqLog.setTimes(System.currentTimeMillis()-begintime);
//
//    crmReqLog.setSuccess(true);
//    MqUtil.sendMessage(MessageObject.getLogType(MessageType.crm_req, crmReqLog));
// }
    @Around("serviceAspect()   && @annotation(oauth)")
    public Object around(ProceedingJoinPoint pjp, WebMapControllerLog oauth) throws Throwable {


        logger.info("进入环绕通知");
        Object[] args = pjp.getArgs(); //7

        String uri = getRequestURI();





        HttpServletRequest request = (HttpServletRequest) args[0];
        Map<String, Object> param = getParameterMap(request);


        long beginTime = System.currentTimeMillis();
        Object objectResult = pjp.proceed();
        logger.info("执行完方法调用");
        WebMapLog webMapLog = new WebMapLog();
        webMapLog.setUri(uri);
        webMapLog.setReq_param(JSONObject.toJSONString(param));
        String content = JSONObject.toJSONString(objectResult);
        //当前报文长度
        int length = content.length();
        webMapLog.setLengths(length);
        webMapLog.setReturn_param(content);
        webMapLog.setTimes(System.currentTimeMillis() - beginTime);
        webMapLog.setMethod(oauth.methods());
        webMapLog.setModule(oauth.module());
        //日志
        webMapLog.setSuccess(true);
        webMapLog.setSno("sno");
        MqUtil.sendMessage(MessageObject.getLogType(MessageType.web_map, webMapLog));
        logger.info("this is after around advice normal end");


//        String sno = (String) param.get("sno");
//        String lockName = RedisLockUtil.getLockName(uri, sno);
//
//        long beginTime = System.currentTimeMillis();
//        Object objectResult = null;
//        boolean isok = false;
//        try {
//            if (StringUtils.isNotEmpty(lockName) && StringUtils.isNotEmpty(sno)) {
//                //判断是否有处理成功的
//                LockStatus lockStatus = RedisLockUtil.getLockStatus(lockName);
//                //处理成功的直接返回
//                if (LockStatus.success.equals(lockStatus)) {
//                    objectResult = JSONResult.successByMsg("该接口已成功处理过");
//                    isok = true;
//                } else if (LockStatus.loading.equals(lockStatus)) {
//                    //处理中
//                    objectResult = JSONResult.error(Exceptions.Global.SERVICE_LOADING.getCode(), Exceptions.Global.SERVICE_LOADING.getDescription());
//                } else {
//                    //获取锁,开始处理
//                    boolean successLock = RedisLockUtil.getLock(lockName, 2, 24 * 60 * 60);
//                    //获得锁,处理
//                    if (successLock) {
//                        //成功的不再处理(必须二次判断)
//                        if (LockStatus.success.equals(RedisLockUtil.getLockStatus(lockName))) {
//                            objectResult = JSONResult.successByMsg("该接口已成功处理过");
//                            isok = true;
//                        }else{
//                            objectResult = pjp.proceed();
//                            isok = true;
//                            //解锁
//                            RedisLockUtil.setLockStatus(lockName, LockStatus.success);
//                            //同时关闭同步锁
//                            RedisLockUtil.unLock(lockName);
//                        }
//                    } else {
//                        //没有获得锁也是处理中
//                        objectResult = JSONResult.error(Exceptions.Global.SERVICE_LOADING.getCode(), Exceptions.Global.SERVICE_LOADING.getDescription());
//                    }
//                }
//            } else {
//                //sno不存在
//                objectResult = pjp.proceed();
//                isok = true;
//            }
//        } catch (Throwable t) {
//            t.printStackTrace();
//            logger.error(t.getMessage());
//            //失败了
//            isok = false;
//            JSONResult jerror=null;
//            if (null != t.getCause()) {
//                if (t.getCause().getClass().equals(CRMException.class)) {
//                    CRMException crmException = (CRMException) t.getCause();
//                    jerror = crmException.getResult();
//                } else {
//                    jerror = JSONResult.error(Exceptions.Global.ERROR.getCode(), "服务器正忙,请稍后再试");
//                }
//            }
//            //错误配置
//            if (null == jerror || StringUtils.isEmpty(jerror.getMsg())) {
//                jerror = JSONResult.error(Exceptions.Global.INTERFACE_SERVER_EXCEPTION.getCode(), Exceptions.Global.INTERFACE_SERVER_EXCEPTION.getDescription());
//            }
//
//            objectResult = jerror;
//            logger.error(":调用接口出错:" + jerror.toString() + ",接口编号:" + sno);
//
//            if (StringUtils.isNotEmpty(lockName)) {
//                //解锁
//                RedisLockUtil.setLockStatus(lockName, LockStatus.fail);
//                //同时关闭同步锁
//                RedisLockUtil.unLock(lockName);
//            }
//        } finally {
//            //关闭crm日志 20200323
//
//
//            WebMapLog webMapLog=new WebMapLog();
//            webMapLog.setUri(uri);
//            webMapLog.setReq_param(JSONObject.toJSONString(param));
//            String content = JSONObject.toJSONString(objectResult);
//            //当前报文长度
//            int length = content.length();
//            webMapLog.setLengths(length);
//            webMapLog.setReturn_param(content);
//            webMapLog.setTimes(System.currentTimeMillis()-beginTime);
//            //日志
//            webMapLog.setStatus(isok);
//            webMapLog.setSno(sno);
//            MqUtil.sendMessage(MessageObject.getLogType(MessageType.web_map, webMapLog));
//            logger.info("this is after around advice normal end");
//        }

        return objectResult;
    }


    private String getRequestURI() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String uri = request.getRequestURI();
        return uri;
    }


    public Map<String, Object> getParameterMap(HttpServletRequest request) {
        // 参数Map
        Map<?, ?> properties = request.getParameterMap();
        // 返回值Map
        Map<String, Object> returnMap = new HashMap<String, Object>();
        Iterator<?> entries = properties.entrySet().iterator();

        Map.Entry<String, Object> entry;
        String name = "";
        String value = "";
        Object valueObj = null;
        while (entries.hasNext()) {
            entry = (Map.Entry<String, Object>) entries.next();
            name = (String) entry.getKey();
            valueObj = entry.getValue();
            if (null == valueObj) {
                value = "";
            } else if (valueObj instanceof String[]) {
                String[] values = (String[]) valueObj;
                for (int i = 0; i < values.length; i++) {
                    value = values[i] + ",";
                }
                value = value.substring(0, value.length() - 1);
            } else {
                value = valueObj.toString();
            }
            returnMap.put(name, value);
        }
        return returnMap;
    }


//    /**
//     *     * 异常通知:目标方法发生异常的时候执行以下代码    
//     */
//    @AfterThrowing(value = "execution(* com.biz.service.rest.henineken.impl.*.*(..))", throwing = "e")
//    public void afterThorwingMethod(JoinPoint jp, NullPointerException e) {
//        String methodName = jp.getSignature().getName();
//        System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);
//
//    }


//    //异常通知
//    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
//    public void handleThrowing(JoinPoint joinPoint, Exception e) {
//
//        Signature signature = joinPoint.getSignature();
//        MethodSignature methodSignature = (MethodSignature) signature;
//        IntfaceException intfaceException = methodSignature.getMethod().getAnnotation(IntfaceException.class);
//
//        //连接异常进行短信通知
//        if (e instanceof CRMException) {
//
//            CRMException crmExcep = (CRMException) e;
//            JSONResult result = crmExcep.getResult();
//
//            TxySmsSetting txySmsSetting = SettingUtil.getTxySmsSetting();
//
//
//            if (result.getErrcode() == 1107) {
//
//                Object notify = RedisUtil.get("sys_excep_notify" + intfaceException.system());
//
//                if (notify != null) {
//                    return;
//                }
//
//                String[] content = new String[3];
//
//                content[0] = SystemConstants.profileName + "" + intfaceException.system();
//                content[1] = result.getMsg();
//                content[2] = intfaceException.module();
//
//                String[] phones = txySmsSetting.getExcepPhones().split(",");
//
//                SmsMessage smsMessage = new SmsMessage();
//                smsMessage.setAppuser("admin");
//                smsMessage.setContent(content);
//                smsMessage.setPhones(phones);
//                smsMessage.setTemplateId(txySmsSetting.getIntfacExcepTemplateId());
//                //12小时之内不重复发送短信
//                RedisUtil.set("sys_excep_notify" + intfaceException.system(), "Y", 43200L);
//                MqUtil.sendMessage(MessageObject.getType(null, smsMessage, MqConstant.mq_sms_send));
//
//            }
//
//        }
//
//
//    }


    /**
     * 获取注解中对方法的描述信息
     *
     * @param joinPoint 切点
     * @return 方法描述
     * @throws Exception
     */
    private static String getServiceMethodDescription(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(HeineKenControllerLog.class).module();
                    break;
                }
            }
        }
        return description;
    }


}

 

 

 

第三步:把Controller的代理权交给cglib

在实例化ApplicationContext的时候需要加上

 

Xml代码 复制代码
  1. <!-- 启动对@AspectJ注解的支持 -->  
  2. <aop:aspectj-autoproxy/>  
  3. <aop:aspectj-autoproxy proxy-target-class="true" />

 在调用Controller的时候AOP发挥作用所以在SpringMVC的配置文件里加上

 

因为 SpringMVC的Contrller的容器和 spring 的容器不是同一个,必须再spring mvc的配置文件添加<aop:aspectj-autoproxy proxy-target-class="true" />

 这个语句,才能实现对controller的注解支持

 

第四步使用

 

Controller层的使用

 

@RequestMapping(method = RequestMethod.GET)
@WebMapControllerLog(methods = "goLogin",module = "跳转登录界面")
public String goLogin(HttpServletRequest request, HttpServletResponse response) {
    logger.debug("LoginController  goLogin");
    return BaseUntil.webPath + "/login/loginMain";
}

 

 

 

 

分享到:
评论

相关推荐

    spring第一次课笔记

    Spring框架是Java开发中广泛应用的一个开源框架,以其强大的依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect-Oriented Programming,简称AOP)功能而著名。本笔记将详细解析Spring框架的基础知识,...

    shop第一次做的项目.zip_spring_网上商城 spring_购物_购物SpringMVC_购物商城项目

    标题中的"shop第一次做的项目.zip_spring_网上...总之,"shop第一次做的项目"是一个涵盖Spring、SpringMVC和MyBatis的网上购物商城实践,涉及了Web应用开发的多个方面,对提升开发者在Java Web领域的技能具有重要价值。

    第一次搭建spring3.x需要的jar和搭建源码

    Spring是一个开源的Java框架,主要用于简化企业级应用的开发,通过依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)提供了强大的功能。在3.x版本中,Spring引入了许多改进...

    Spring5.pdf

    Spring Cloud是基于Spring Boot的一系列工具集,旨在快速建立分布式系统中的一些常见模式(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话和集群状态)。...

    spring攻略(第一版)源码

    《Spring攻略(第一版)源码》是一份珍贵的学习资源,包含了Spring框架的早期版本的源代码,旨在帮助开发者深入理解Spring的工作原理和设计模式。这份资料的核心价值在于其实践性和教育性,鼓励开发者通过阅读和分析...

    第一次ssm项目.zip

    这个“第一次SSM项目”显然旨在帮助初学者理解并实践这三大框架的协作方式。让我们深入探讨一下SSM项目的核心概念、它们各自的职责以及如何将它们整合到一个实际的应用中。 **1. Spring框架** Spring是一个开源的...

    Spring in Action 中文版 pdf

    《Spring in Action》是Spring框架领域的一本经典著作,它以深入浅出的方式介绍了Spring框架的核心概念和技术。这本书的中文版对于中国的Java开发者来说是一份非常宝贵的...这是一次对Spring框架全面而深入的学习之旅。

    Spring笔记(第七次)1

    在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许程序员定义横切关注点,并将这些关注点与应用程序的业务逻辑分离。在本篇笔记中,我们将深入探讨Spring AOP的核心流程,特别是在目标方法执行时的拦截...

    SSM项目整合第一次修改

    在这个"SSM项目整合第一次修改"中,我们主要探讨的是如何将这三个框架进行有效的集成,以实现高效、灵活的后端业务逻辑处理。 首先,Spring框架作为基础,它提供了依赖注入(Dependency Injection,DI)和面向切面...

    spring第二次课测试

    Spring框架是Java开发中广泛使用的轻量级框架,它的核心特性包括依赖注入...在“spring第二次课测试”中,通过实践和代码示例,你将有机会深入了解和应用这些Spring框架的关键知识点,提升你的Java开发技能。

    【BAT必备】spring面试题,spring MVC面试题,spring boot面试题,spring cloud面试题

    它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)中的快速解决方案。 #### 2. Eureka服务发现 Eureka是Netflix...

    Spring五次课的截图详细笔记

    在Spring第一次课中,可能涉及了IoC容器的概念,它是Spring的核心,负责管理对象的生命周期和依赖关系。 2. **依赖注入**:DI是Spring的核心特性,通过反转控制权,使得对象之间的依赖关系不再硬编码,而是由框架...

    spring课件,ppt版

    例如,第01章可能介绍Spring的基本概念和IoC,第02章可能涉及依赖注入的实践,第03章可能是关于AOP的深入探讨,第04章可能讨论Spring MVC的使用,第05章和第06章可能涵盖数据访问和事务管理,第07章可能涉及Spring ...

    spring Framework 中文参考开发手册

    Spring Cloud 是一组工具,用于快速构建分布式系统中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等功能。它使开发人员能够轻松地创建具有云...

    Spring基本知识点教程+案例演示

    - **BeanFactory**:这是Spring最基础的容器接口,它负责生产任意bean,采用延迟加载的方式,在第一次调用`getBean`方法时才会初始化bean。 - **ApplicationContext**:它是`BeanFactory`的子接口,提供了更多高级...

    OA项目框架(SpringMvc+Mybatis3+Spring Security3)

    【OA项目框架(SpringMvc+Mybatis3+Spring Security3)】是一个基于Java技术的办公自动化(OA)系统开发框架,适用于快速构建企业级的办公应用。该框架整合了几个关键的技术组件,包括SpringMVC、Spring、Mybatis...

    spring 教程,培训资料很详细

    这个"spring 教程,培训资料很详细"的资源包含了三次课程的内容,分别命名为"spring第三次课"、"spring第一次课"和"spring第二次课",这些课程可能涵盖从基础到进阶的Spring知识点。 在Spring框架中,IoC容器是核心...

    spring学习课件

    - **轻量级**:Spring框架体积小、资源占用低,可以作为一个独立的组件运行于不同的服务器环境,体现了“一次编写,到处运行”的理念。 - **控制反转(IoC)**:IoC是Spring的核心特性之一,通过让对象的依赖关系由...

    spring mvc + spring + hibernate 全注解整合开发视频教程 09

    【标题】"Spring MVC + Spring + Hibernate 全注解整合开发视频教程 09" 提供了一次深入了解Java企业级应用开发的机会,特别是在使用Spring MVC、Spring和Hibernate这三大核心框架进行全注解配置的场景下。...

    spring ppt

    标题中的“spring ppt”指的是关于Spring框架的一系列PowerPoint演示文稿,这通常是一次讲座或教程的材料。从描述中的博文链接来看,这可能是一个技术博主分享的关于Spring框架的深入学习资源。标签“源码”和“工具...

Global site tag (gtag.js) - Google Analytics