以下内容来自公众号逆锋起笔,关注每日干货及时送达

1.简介

在使用spring完成项目的时候需要完成记录日志,开始以为Spring 的AOP功能,就可以轻松解决,半个小时都不用,可是经过一番了解过后,发现一般的日志记录,只能记录一些简单的操作,例如表名、表名称等记录不到。

这个时侯就用到了自定义注解,把想要记录的内容放在注解中,通过切入点来获取到注解参数,然后将参数插入数据库记录

2.Spring AOP

2.1.关于Spring AOP的一些术语

  • 切面(Aspect):在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现

  • 连接点(Joinpoint):在Spring AOP中一个连接点代表一个方法的执行

  • 通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种类型,其中包括"around"、"before”和"after"等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链

  • 切入点(Pointcut):定义出一个或一组方法,当执行这些方法时可产生通知,Spring缺省使用AspectJ切入点语法。

通知类型
  • 前置通知(@Before):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)

  • 返回后通知(@AfterReturning):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回

  • 抛出异常后通知(@AfterThrowing):方法抛出异常退出时执行的通知

  • 后通知(@After):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)

  • 环绕通知(@Around):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型,环绕通知可以在方法调用前后完成自定义的行为,它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。

2.2.Spring AOP配置有两种风格:

  • XML风格 = 采用声明形式实现Spring AOP

  • AspectJ风格 = 采用注解形式实现Spring AOP

3.首先自定义注解

定义一个日志描述和一个表名这里根据需要自定义注解

package com.ywj.log;import java.lang.annotation.*;/*** ClassName Crmlog* AOP日志记录 自定义注解类*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemCrmlog {/*** 日志描述* 对于什么表格进行了什么操作*/String description()  default "";/*** 操作了的表名* @return*/String  tableName() default "";
}

3.1.定义切面类,从切入点获取注解信息保存到数据库

对于一些可能碰到的问题我在方法的注释里都有解决办法,大家注意一下,这里我对于方法报错也有处理方法

这里是对于切面类里使用到的两个类解释:

AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。任何一个增强方法都可以通过将第一个入参声明为JoinPoint访问到连接点上下文的信息。我们先来了解一下这两个接口的主要方法:

1)JoinPoint
  • java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;

  • Signature getSignature() :获取连接点的方法签名对象;

  • java.lang.Object getTarget() :获取连接点所在的目标对象;

  • java.lang.Object getThis() :获取代理对象本身;

2)ProceedingJoinPoint

ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:

  • java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;

  • java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。因为看了这些资料,我就成了别人眼中的大佬!

package com.ywj.log;import com.fasterxml.jackson.databind.ObjectMapper;
import com.ywj.log.biz.Sys_logBiz;
import com.ywj.log.dao.Sys_logDao;
import com.ywj.login.biz.Sys_UserBiz;
import com.ywj.login.dao.Sys_UserDao;
import com.ywj.login.dao.Sys_righDao;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
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.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;/*** @ClassName SystemLogAspect* @Author Administrator* @Describe  定义切入面类*/
@Aspect
@Component
public class SystemLogAspect {/*** 注解Pointcut切入点* 定义出一个或一组方法,当执行这些方法时可产生通知* 指向你的切面类方法* 由于这里使用了自定义注解所以指向你的自定义注解*/@Pointcut("@annotation(com.ywj.log.SystemCrmlog)")public void crmAspect() {}/***抛出异常后通知(@AfterThrowing):方法抛出异常退出时执行的通知* 注意在这里不能使用ProceedingJoinPoint* 不然会报错ProceedingJoinPoint is only supported for around advice* throwing注解为错误信息* @param joinPoint* @param ex*/@AfterThrowing(value="crmAspect()", throwing="ex")public void afterThrowingMethod(JoinPoint joinPoint, Exception ex) throws Exception {HttpServletRequest httpServletRequest = getHttpServletRequest();//获取管理员用户信息\WebUtil webUtil = new WebUtil();Map<String, Object> user = webUtil.getUser(httpServletRequest);CrmLogMessage log=new CrmLogMessage();//获取需要的信息String context=getServiceMthodDescription(joinPoint);String usr_name="";String rolename="";if(user!=null){usr_name = user.get("usr_name").toString();rolename=user.get("rolename").toString();}//管理员姓名log.setUserName(usr_name);//角色名log.setUserRole(rolename);//日志信息log.setContent(usr_name+context);//设置参数集合log.setRemarks(getServiceMthodParams(joinPoint));//设置表名log.setTableName(getServiceMthodTableName(joinPoint));//操作时间SimpleDateFormat sif=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");log.setDateTime(sif.format(new Date()));//设置ip地址log.setIp(httpServletRequest.getRemoteAddr());//设置请求地址log.setRequestUrl(httpServletRequest.getRequestURI());//执行结果log.setResult("执行失败");//错误信息log.setExString(ex.getMessage());//将数据保存到数据库Sys_logDao sysLogDao=new Sys_logDao();sysLogDao.addSys_log(log);}/*** 返回后通知(@AfterReturning):在某连接点(joinpoint)* 正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回* 方法执行完毕之后* 注意在这里不能使用ProceedingJoinPoint* 不然会报错ProceedingJoinPoint is only supported for around advice* crmAspect()指向需要控制的方法*  returning  注解返回值* @param joinPoint* @param returnValue  返回值* @throws Exception*/@AfterReturning(value = "crmAspect()",returning = "returnValue")public  void doCrmLog(JoinPoint joinPoint,Object returnValue) throws Exception {HttpServletRequest httpServletRequest = getHttpServletRequest();//获取管理员用户信息WebUtil webUtil = new WebUtil();Map<String, Object> user = webUtil.getUser(httpServletRequest);CrmLogMessage log=new CrmLogMessage();String context=getServiceMthodDescription(joinPoint);String usr_name="";String rolename="";if(user!=null){usr_name = user.get("usr_name").toString();rolename=user.get("rolename").toString();}//管理员姓名log.setUserName(usr_name);//角色名log.setUserRole(rolename);//日志信息log.setContent(usr_name+context);//设置参数集合log.setRemarks(getServiceMthodParams(joinPoint));//设置表名log.setTableName(getServiceMthodTableName(joinPoint));//操作时间SimpleDateFormat sif=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");log.setDateTime(sif.format(new Date()));//设置ip地址log.setIp(httpServletRequest.getRemoteAddr());//设置请求地址log.setRequestUrl(httpServletRequest.getRequestURI());if(returnValue!=null){if(returnValue instanceof List){List ls= (List) returnValue;if(ls.size()>0){log.setResult("执行成功");}else{log.setResult("执行成功");}}else if(returnValue instanceof Boolean){Boolean falg= (Boolean) returnValue;if(falg){log.setResult("执行成功");}else{log.setResult("执行失败");}}else if(returnValue instanceof Integer){Integer i= (Integer) returnValue;if(i>0){log.setResult("执行成功");}else{log.setResult("执行失败");}}else{log.setResult("执行成功");}}//将数据保存到数据库Sys_logDao sysLogDao=new Sys_logDao();sysLogDao.addSys_log(log);}/***获取自定义注解里的日志描述* @param joinPoint* @return 返回注解里面的日志描述* @throws Exception*/private String getServiceMthodDescription(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(SystemCrmlog.class).description();break;}}}return description;}/***获取自定义注解里的表名* @param joinPoint* @return 返回注解里的表名字* @throws Exception*/private String getServiceMthodTableName(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 tableName = "";for (Method method : methods) {//判断方法名是不是一样if (method.getName().equals(methodName)) {//对比参数数组的长度Class[] clazzs = method.getParameterTypes();if (clazzs.length == arguments.length) {//获取注解里的表名tableName = method.getAnnotation(SystemCrmlog.class).tableName();break;}}}return tableName;}/*** 获取json格式的参数用于存储到数据库中* @param joinPoint* @return* @throws Exception*/private String getServiceMthodParams(JoinPoint joinPoint)throws Exception {Object[] arguments = joinPoint.getArgs();ObjectMapper om=new ObjectMapper();return om.writeValueAsString(arguments);}/*** 获取当前的request* 这里如果报空指针异常是因为单独使用spring获取request* 需要在配置文件里添加监听* <listener>* <listener-class>* org.springframework.web.context.request.RequestContextListener* </listener-class>* </listener>* @return*/public HttpServletRequest getHttpServletRequest(){RequestAttributes ra = RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra = (ServletRequestAttributes)ra;HttpServletRequest request = sra.getRequest();return request;}}

每个切面传递的数据的都不一样,最终决定,获取切面的所有参数,转成json字符串,保存到数据库中。

相关类:

日志信息类

package com.ywj.log;/*** @ClassName CrmLogMessage* @Author Administrator* @Describe 数据库日志类*/
public class CrmLogMessage {private Integer logid;//日志idprivate String UserName;//管理员姓名private String UserRole;//管理员角色private String Content;//日志描述private String Remarks;//参数集合private String TableName;//表格名称private String DateTime;//操作时间private String resultValue;//返回值private String ip;//ip地址private String  requestUrl;//请求地址private String result;//操作结果private  String ExString;//错误信息public CrmLogMessage() {}@Overridepublic String toString() {return "CrmLogMessage{" +"logid=" + logid +", UserName='" + UserName + '\'' +", UserRole='" + UserRole + '\'' +", Content='" + Content + '\'' +", Remarks='" + Remarks + '\'' +", TableName='" + TableName + '\'' +", DateTime='" + DateTime + '\'' +", resultValue='" + resultValue + '\'' +", ip='" + ip + '\'' +", requestUrl='" + requestUrl + '\'' +", result='" + result + '\'' +", ExString='" + ExString + '\'' +'}';}public CrmLogMessage(Integer logid, String userName, String userRole, String content, String remarks, String tableName, String dateTime, String resultValue, String ip, String requestUrl, String result, String exString) {this.logid = logid;UserName = userName;UserRole = userRole;Content = content;Remarks = remarks;TableName = tableName;DateTime = dateTime;this.resultValue = resultValue;this.ip = ip;this.requestUrl = requestUrl;this.result = result;ExString = exString;}public String getExString() {return ExString;}public void setExString(String exString) {ExString = exString;}public Integer getLogid() {return logid;}public void setLogid(Integer logid) {this.logid = logid;}public String getUserName() {return UserName;}public void setUserName(String userName) {UserName = userName;}public String getUserRole() {return UserRole;}public void setUserRole(String userRole) {UserRole = userRole;}public String getContent() {return Content;}public void setContent(String content) {Content = content;}public String getRemarks() {return Remarks;}public void setRemarks(String remarks) {Remarks = remarks;}public String getTableName() {return TableName;}public void setTableName(String tableName) {TableName = tableName;}public String getDateTime() {return DateTime;}public void setDateTime(String dateTime) {DateTime = dateTime;}public String getResultValue() {return resultValue;}public void setResultValue(String resultValue) {this.resultValue = resultValue;}public String getIp() {return ip;}public void setIp(String ip) {this.ip = ip;}public String getRequestUrl() {return requestUrl;}public void setRequestUrl(String requestUrl) {this.requestUrl = requestUrl;}public String getResult() {return result;}public void setResult(String result) {this.result = result;}
}

用来获取登录用户信息的帮助类:

package com.ywj.log;import com.base.web.BaseAction;import javax.servlet.http.HttpServletRequest;
import java.util.Map;/*** @ClassName WebUtil* @Author Administrator* @Describe  日志帮助类 用来获取session中的用户信息来存入数据库*/
public class WebUtil  {/*** 从session中获取到用户对象* @return*/public Map<String, Object>   getUser(HttpServletRequest request){Map<String, Object> attribute=null;if(request!=null){Object user = request.getSession().getAttribute(Constans.USER_KEY);attribute = (Map<String, Object>) user;}return attribute;
}}

在你的spring-context.xml中配置

<!-- 启动对@AspectJ注解的支持  --><aop:aspectj-autoproxy proxy-target-class="true" /><!-- 自动扫描包路径  --><!--你需要刚才的切面类的包路径--><context:component-scan base-package="com.ywj.log" /><!--你需要注解方法的包路径--><context:component-scan base-package="com.*.*.biz.impl" />

然后在你需要记录的方法上加上注解

@SystemCrmlog(description = "进行了登录操作",tableName =Constans.USER_TABLENAME)

效果这里表名使用了常量类

对于一些表的信息可以写一个常量类

然后执行登录操作数据库记录为:

来源:blog.csdn.net/yjt520557/article/details/85099115

成为一名优秀Java开发人员的7个步骤

40 个 SpringBoot 常用注解

泛型会让你的 Go 代码运行变慢

今日福利
赠送如下图书籍:

赠送规则:
1、给本文点赞、在看
2、给本文留言(留言内容与本书有关)
3、中奖后我会回复你的留言,请注意公众号消息。上次中奖者:

妙用自定义注解,一行代码搞定大功能(文末赠书)相关推荐

  1. 开源作品ThinkJDBC—一行代码搞定数据库操作

    1 简介 ThinkJD,又名ThinkJDBC,一个简洁而强大的开源JDBC操作库.你可以使用Java像ThinkPHP框架的M方法一样,一行代码搞定数据库操作.ThinkJD会自动管理数据库连接, ...

  2. 一行代码搞定 Scrapy 随机 User-Agent 设置

    摘要:爬虫过程中的反爬措施非常重要,其中设置随机 User-Agent 是一项重要的反爬措施,Scrapy 中设置随机 UA 的方式有很多种,有的复杂有的简单,本文就对这些方法进行汇总,提供一种只需要 ...

  3. thinkjdbc 关闭_ThinkJD: ThinkJD,又名ThinkJDBC,一个强大的开源JDBC/ORM操作库,让你尽可能简洁地用一行代码搞定数据库操作。...

    1 简介 ThinkJD,又名ThinkJDBC,一个简洁而强大的开源JDBC操作库.你可以使用Java像ThinkPHP框架的M方法一样,一行代码搞定数据库操作.ThinkJD会自动管理数据库连接, ...

  4. Android 一行代码搞定将错误日志放入到sd卡中且不需要任何权限,适配到android7.0

    Android 一行代码搞定将错误日志放入到sd卡中且不需要任何权限,适配到android7.0 之前所有的项目都有一个将崩溃日志写入到sd卡的工具类,然后每次项目新建都从老项目copy过来,后来慢慢 ...

  5. Python 数据可视化:Stack Graph 堆叠图,标准化堆叠柱形图,标准化的同时还能反应数据量大小的堆叠图(放入自写库,一行代码搞定复杂细节绘图)

    本文已在公众号 " 数据分析与商业实践 " 首发.关注一下~,更多商业数据分析案例源码等你来撩.后台回复 "堆叠图" ,即可获取本文的案例示范与包含详细注释的源 ...

  6. 一行代码搞定android全屏适配

    一行代码搞定安卓全屏幕适配--简单粗暴-低入侵,无继承,简单高效 话不多说,先上解决方案 方案一(推荐) ##### 1.引用工具类 DensityHelper.java ##### 2.在自定义的 ...

  7. table中加表单元素每行怎么验证_Validform 一行代码搞定整站的表单验证 - 文章

    Validform 一行代码搞定整站的表单验证,为什么能如此方便?插件的核心思想就是把所有的验证条件及验证提示信息绑定到每个表单元素,让验证代码在执行时只是核对表单下各元素的值是否跟绑定的验证条件相符 ...

  8. WPF使用Linq 一行代码搞定数据绑定

    首先设置好DataGrid控件的相关属性,注意XAML代码文件中的列绑定要和源数据的列名一致,如: Binding="{Binding No}" 详细设置如下: this.data ...

  9. 成功解决numpy.ndarray格式类型转数据为list格式数据带有中括号(一行代码搞定!)

    成功解决numpy.ndarray格式类型转数据为list格式数据带有中括号(一行代码搞定!) 目录 解决问题 解决思路 解决方法 解决问题 numpy.ndarray格式类型转数据为list格式数据 ...

最新文章

  1. 【The final】软件工程实践总结
  2. linux c warning: implicit declaration of function 'strtok_r'解决方案
  3. 淮海工学院软件测试技术实验三功能测试,软件工程-实验3软件测试技术实验(7页)-原创力文档...
  4. 网页制作中的背景处理
  5. Cisco 综合交换实验Dynamips.Net实现方法
  6. ROS入门笔记(十二):动作编程 (C++)
  7. 光储充一体化充电站_忙时给车充电 闲时上网供电 多能电动车充电站在乐清投用...
  8. linux 设置tomcat快捷启动方式
  9. Android Studio新建工程syncing失败;Android studio Connection timed out: connect
  10. Python之ASCII码
  11. Python语法基础
  12. java 获得pdf 目录的方法
  13. 1991年首批全国500位名老中医
  14. Factory method ‘sqlSessionFactory‘ threw exception; nested exception is java.lang.NoSuchMethodError:
  15. 如何科学地蹭热点:用python爬虫获取热门微博评论并进行情感分析
  16. Neo4j入门实战之红楼梦知识图谱
  17. 基于Cocos的猫狗大战
  18. Python编程遇问题,文科生怎么办?
  19. python整段代码注释-Python中注释(多行注释和单行注释)的用法实例
  20. 河南省高清地形图-大图

热门文章

  1. 解决selenium实例化时警告问题
  2. Python根据字幕文件自动给视频添加字幕(通用版)
  3. uniapp团购商城源码在哪里下载?
  4. 【springboot】5、lombok
  5. 用户画像标签维度_用户画像详解:关于用户画像的What、Why、How
  6. AGA 2019电竞狂欢盛宴开启:为全国校园电竞玩家“圆梦”
  7. OpenHarmony分布式软总线与设备认证模块总结
  8. 认证授权那点事儿 —— OAuth 2.0
  9. Xposed框架动态调试第三方APP—实战演示
  10. 2022-2023年度江苏省职业院校技能大赛“网络安全”赛项中职组圆满成功