一、需求背景。
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的时候需要加上
- <!-- 启动对@AspectJ注解的支持 -->
- <aop:aspectj-autoproxy/>
-
<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框架是Java开发中广泛应用的一个开源框架,以其强大的依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect-Oriented Programming,简称AOP)功能而著名。本笔记将详细解析Spring框架的基础知识,...
标题中的"shop第一次做的项目.zip_spring_网上...总之,"shop第一次做的项目"是一个涵盖Spring、SpringMVC和MyBatis的网上购物商城实践,涉及了Web应用开发的多个方面,对提升开发者在Java Web领域的技能具有重要价值。
Spring是一个开源的Java框架,主要用于简化企业级应用的开发,通过依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)提供了强大的功能。在3.x版本中,Spring引入了许多改进...
Spring Cloud是基于Spring Boot的一系列工具集,旨在快速建立分布式系统中的一些常见模式(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话和集群状态)。...
《Spring攻略(第一版)源码》是一份珍贵的学习资源,包含了Spring框架的早期版本的源代码,旨在帮助开发者深入理解Spring的工作原理和设计模式。这份资料的核心价值在于其实践性和教育性,鼓励开发者通过阅读和分析...
这个“第一次SSM项目”显然旨在帮助初学者理解并实践这三大框架的协作方式。让我们深入探讨一下SSM项目的核心概念、它们各自的职责以及如何将它们整合到一个实际的应用中。 **1. Spring框架** Spring是一个开源的...
《Spring in Action》是Spring框架领域的一本经典著作,它以深入浅出的方式介绍了Spring框架的核心概念和技术。这本书的中文版对于中国的Java开发者来说是一份非常宝贵的...这是一次对Spring框架全面而深入的学习之旅。
在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许程序员定义横切关注点,并将这些关注点与应用程序的业务逻辑分离。在本篇笔记中,我们将深入探讨Spring AOP的核心流程,特别是在目标方法执行时的拦截...
在这个"SSM项目整合第一次修改"中,我们主要探讨的是如何将这三个框架进行有效的集成,以实现高效、灵活的后端业务逻辑处理。 首先,Spring框架作为基础,它提供了依赖注入(Dependency Injection,DI)和面向切面...
Spring框架是Java开发中广泛使用的轻量级框架,它的核心特性包括依赖注入...在“spring第二次课测试”中,通过实践和代码示例,你将有机会深入了解和应用这些Spring框架的关键知识点,提升你的Java开发技能。
它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)中的快速解决方案。 #### 2. Eureka服务发现 Eureka是Netflix...
在Spring第一次课中,可能涉及了IoC容器的概念,它是Spring的核心,负责管理对象的生命周期和依赖关系。 2. **依赖注入**:DI是Spring的核心特性,通过反转控制权,使得对象之间的依赖关系不再硬编码,而是由框架...
例如,第01章可能介绍Spring的基本概念和IoC,第02章可能涉及依赖注入的实践,第03章可能是关于AOP的深入探讨,第04章可能讨论Spring MVC的使用,第05章和第06章可能涵盖数据访问和事务管理,第07章可能涉及Spring ...
Spring Cloud 是一组工具,用于快速构建分布式系统中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等功能。它使开发人员能够轻松地创建具有云...
- **BeanFactory**:这是Spring最基础的容器接口,它负责生产任意bean,采用延迟加载的方式,在第一次调用`getBean`方法时才会初始化bean。 - **ApplicationContext**:它是`BeanFactory`的子接口,提供了更多高级...
【OA项目框架(SpringMvc+Mybatis3+Spring Security3)】是一个基于Java技术的办公自动化(OA)系统开发框架,适用于快速构建企业级的办公应用。该框架整合了几个关键的技术组件,包括SpringMVC、Spring、Mybatis...
这个"spring 教程,培训资料很详细"的资源包含了三次课程的内容,分别命名为"spring第三次课"、"spring第一次课"和"spring第二次课",这些课程可能涵盖从基础到进阶的Spring知识点。 在Spring框架中,IoC容器是核心...
- **轻量级**:Spring框架体积小、资源占用低,可以作为一个独立的组件运行于不同的服务器环境,体现了“一次编写,到处运行”的理念。 - **控制反转(IoC)**:IoC是Spring的核心特性之一,通过让对象的依赖关系由...
【标题】"Spring MVC + Spring + Hibernate 全注解整合开发视频教程 09" 提供了一次深入了解Java企业级应用开发的机会,特别是在使用Spring MVC、Spring和Hibernate这三大核心框架进行全注解配置的场景下。...
标题中的“spring ppt”指的是关于Spring框架的一系列PowerPoint演示文稿,这通常是一次讲座或教程的材料。从描述中的博文链接来看,这可能是一个技术博主分享的关于Spring框架的深入学习资源。标签“源码”和“工具...