springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)
程序员文章站
2022-07-10 11:14:47
本文主要记录如何使用aop切面的方式来实现日志记录功能。 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型(增删改查),详细描述,返回值。 ......
版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接
一:功能简介
本文主要记录如何使用aop切面的方式来实现日志记录功能。
主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型(增删改查),详细描述,返回值。
二:项目结构图
三:代码实现
1.配置文件
这里只有两个配置:
1)server.port=11000,设置项目启动的端口号,防止被其他服务占用;
2)spring.aop.auto=true,开启spring的aop配置,简单明了,不需要多配置其他的配置或注解。
application.yml文件
server: port: 11000 spring: aop: auto: true #启动aop配置
2.aop切点类
这个是最主要的类,可以使用自定义注解或针对包名实现aop增强。
1)这里实现了对自定义注解的环绕增强切点,对使用了自定义注解的方法进行aop切面处理;
2)对方法运行时间进行监控;
3)对方法名,参数名,参数值,对日志描述的优化处理;
在方法上增加@aspect 注解声明切面,使用@pointcut 注解定义切点,标记方法。
使用切点增强的时机注解:@before,@around,@afterreturning,@afterthrowing,@after
package com.wwj.springboot.aop; import com.alibaba.fastjson.json; import com.wwj.springboot.annotation.operationlogdetail; import com.wwj.springboot.model.operationlog; import org.aspectj.lang.joinpoint; import org.aspectj.lang.proceedingjoinpoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.methodsignature; import org.springframework.stereotype.component; import java.util.date; import java.util.hashmap; import java.util.map; import java.util.uuid; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/12 */ @aspect @component public class logaspect { /** * 此处的切点是注解的方式,也可以用包名的方式达到相同的效果 * '@pointcut("execution(* com.wwj.springboot.service.impl.*.*(..))")' */ @pointcut("@annotation(com.wwj.springboot.annotation.operationlogdetail)") public void operationlog(){} /** * 环绕增强,相当于methodinterceptor */ @around("operationlog()") public object doaround(proceedingjoinpoint joinpoint) throws throwable { object res = null; long time = system.currenttimemillis(); try { res = joinpoint.proceed(); time = system.currenttimemillis() - time; return res; } finally { try { //方法执行完成后增加日志 addoperationlog(joinpoint,res,time); }catch (exception e){ system.out.println("logaspect 操作失败:" + e.getmessage()); e.printstacktrace(); } } } private void addoperationlog(joinpoint joinpoint, object res, long time){ methodsignature signature = (methodsignature)joinpoint.getsignature(); operationlog operationlog = new operationlog(); operationlog.setruntime(time); operationlog.setreturnvalue(json.tojsonstring(res)); operationlog.setid(uuid.randomuuid().tostring()); operationlog.setargs(json.tojsonstring(joinpoint.getargs())); operationlog.setcreatetime(new date()); operationlog.setmethod(signature.getdeclaringtypename() + "." + signature.getname()); operationlog.setuserid("#{currentuserid}"); operationlog.setusername("#{currentusername}"); operationlogdetail annotation = signature.getmethod().getannotation(operationlogdetail.class); if(annotation != null){ operationlog.setlevel(annotation.level()); operationlog.setdescribe(getdetail(((methodsignature)joinpoint.getsignature()).getparameternames(),joinpoint.getargs(),annotation)); operationlog.setoperationtype(annotation.operationtype().getvalue()); operationlog.setoperationunit(annotation.operationunit().getvalue()); } //todo 这里保存日志 system.out.println("记录日志:" + operationlog.tostring()); // operationlogservice.insert(operationlog); } /** * 对当前登录用户和占位符处理 * @param argnames 方法参数名称数组 * @param args 方法参数数组 * @param annotation 注解信息 * @return 返回处理后的描述 */ private string getdetail(string[] argnames, object[] args, operationlogdetail annotation){ map<object, object> map = new hashmap<>(4); for(int i = 0;i < argnames.length;i++){ map.put(argnames[i],args[i]); } string detail = annotation.detail(); try { detail = "'" + "#{currentusername}" + "'=》" + annotation.detail(); for (map.entry<object, object> entry : map.entryset()) { object k = entry.getkey(); object v = entry.getvalue(); detail = detail.replace("{{" + k + "}}", json.tojsonstring(v)); } }catch (exception e){ e.printstacktrace(); } return detail; } @before("operationlog()") public void dobeforeadvice(joinpoint joinpoint){ system.out.println("进入方法前执行....."); } /** * 处理完请求,返回内容 * @param ret */ @afterreturning(returning = "ret", pointcut = "operationlog()") public void doafterreturning(object ret) { system.out.println("方法的返回值 : " + ret); } /** * 后置异常通知 */ @afterthrowing("operationlog()") public void throwss(joinpoint jp){ system.out.println("方法异常时执行....."); } /** * 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行 */ @after("operationlog()") public void after(joinpoint jp){ system.out.println("方法最后执行....."); } }
3.自定义注解
package com.wwj.springboot.annotation; import com.wwj.springboot.enums.operationtype; import com.wwj.springboot.enums.operationunit; import java.lang.annotation.*; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/12 */ //@operationlogdetail(detail = "通过手机号[{{tel}}]获取用户名",level = 3,operationunit = operationunit.user,operationtype = operationtype.select) @documented @target({elementtype.method}) @retention(retentionpolicy.runtime) public @interface operationlogdetail { /** * 方法描述,可使用占位符获取参数:{{tel}} */ string detail() default ""; /** * 日志等级:自己定,此处分为1-9 */ int level() default 0; /** * 操作类型(enum):主要是select,insert,update,delete */ operationtype operationtype() default operationtype.unknown; /** * 被操作的对象(此处使用enum):可以是任何对象,如表名(user),或者是工具(redis) */ operationunit operationunit() default operationunit.unknown; }
4.注解用到的枚举类型
package com.wwj.springboot.enums; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/12 */ public enum operationtype { /** * 操作类型 */ unknown("unknown"), delete("delete"), select("select"), update("update"), insert("insert"); private string value; public string getvalue() { return value; } public void setvalue(string value) { this.value = value; } operationtype(string s) { this.value = s; } }
package com.wwj.springboot.enums; /** * created by intellij idea * 被操作的单元 * @author weiwenjun * @date 2018/9/12 */ public enum operationunit { /** * 被操作的单元 */ unknown("unknown"), user("user"), employee("employee"), redis("redis"); private string value; operationunit(string value) { this.value = value; } public string getvalue() { return value; } public void setvalue(string value) { this.value = value; } }
5.日志记录对象
package com.wwj.springboot.model; import java.util.date; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/12 */ public class operationlog { private string id; private date createtime; /** * 日志等级 */ private integer level; /** * 被操作的对象 */ private string operationunit; /** * 方法名 */ private string method; /** * 参数 */ private string args; /** * 操作人id */ private string userid; /** * 操作人 */ private string username; /** * 日志描述 */ private string describe; /** * 操作类型 */ private string operationtype; /** * 方法运行时间 */ private long runtime; /** * 方法返回值 */ private string returnvalue; @override public string tostring() { return "operationlog{" + "id='" + id + '\'' + ", createtime=" + createtime + ", level=" + level + ", operationunit='" + operationunit + '\'' + ", method='" + method + '\'' + ", args='" + args + '\'' + ", userid='" + userid + '\'' + ", username='" + username + '\'' + ", describe='" + describe + '\'' + ", operationtype='" + operationtype + '\'' + ", runtime=" + runtime + ", returnvalue='" + returnvalue + '\'' + '}'; } public long getruntime() { return runtime; } public void setruntime(long runtime) { this.runtime = runtime; } public string getreturnvalue() { return returnvalue; } public void setreturnvalue(string returnvalue) { this.returnvalue = returnvalue; } public string getid() { return id; } public void setid(string id) { this.id = id; } public date getcreatetime() { return createtime; } public void setcreatetime(date createtime) { this.createtime = createtime; } public integer getlevel() { return level; } public void setlevel(integer level) { this.level = level; } public string getoperationunit() { return operationunit; } public void setoperationunit(string operationunit) { this.operationunit = operationunit; } public string getmethod() { return method; } public void setmethod(string method) { this.method = method; } public string getargs() { return args; } public void setargs(string args) { this.args = args; } public string getuserid() { return userid; } public void setuserid(string userid) { this.userid = userid; } public string getusername() { return username; } public void setusername(string username) { this.username = username; } public string getdescribe() { return describe; } public void setdescribe(string describe) { this.describe = describe; } public string getoperationtype() { return operationtype; } public void setoperationtype(string operationtype) { this.operationtype = operationtype; } }
6.springboot启动类
package com.wwj.springboot; import org.springframework.boot.springapplication; import org.springframework.boot.autoconfigure.springbootapplication; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/12 */ @springbootapplication public class aopapplication { public static void main(string[] args) { springapplication.run(aopapplication.class); } }
7.controller类
package com.wwj.springboot.controller; import com.wwj.springboot.service.userservice; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.controller; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.requestparam; import org.springframework.web.bind.annotation.responsebody; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/12 */ @controller @requestmapping("user") public class usercontroller { @autowired private userservice userservice; /** * 访问路径 http://localhost:11000/user/findusernamebytel?tel=1234567 * @param tel 手机号 * @return username */ @responsebody @requestmapping("/findusernamebytel") public string findusernamebytel(@requestparam("tel") string tel){ return userservice.findusername(tel); } }
8.service类
package com.wwj.springboot.service; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/13 */ public interface userservice { /** * 获取用户信息 * @return * @param tel */ string findusername(string tel); }
9.serviceimpl类
package com.wwj.springboot.service.impl; import com.wwj.springboot.annotation.operationlogdetail; import com.wwj.springboot.enums.operationtype; import com.wwj.springboot.enums.operationunit; import com.wwj.springboot.service.userservice; import org.springframework.stereotype.service; /** * created by intellij idea * * @author weiwenjun * @date 2018/9/13 */ @service public class userserviceimpl implements userservice { @operationlogdetail(detail = "通过手机号[{{tel}}]获取用户名",level = 3,operationunit = operationunit.user,operationtype = operationtype.select) @override public string findusername(string tel) { system.out.println("tel:" + tel); return "zhangsan"; } }
四:mavem依赖
本项目有两个pom文件,父类的pom文件主要作用是对子类pom文件依赖的版本号进行统一管理。
1.最外层的pom文件配置如下
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <groupid>com.wwj.springboot</groupid> <artifactid>springboot</artifactid> <packaging>pom</packaging> <version>1.0-snapshot</version> <modules> <module>springboot-aop</module> </modules> <!-- inherit defaults from spring boot --> <parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>2.0.4.release</version> </parent> <properties> <fastjson.version>1.2.49</fastjson.version> </properties> <dependencymanagement> <dependencies> <dependency> <!-- import dependency management from spring boot --> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-dependencies</artifactid> <version>2.0.4.release</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> <version>${fastjson.version}</version> </dependency> </dependencies> </dependencymanagement> </project>
2.子pom文件配置如下
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactid>springboot</artifactid> <groupid>com.wwj.springboot</groupid> <version>1.0-snapshot</version> </parent> <modelversion>4.0.0</modelversion> <artifactid>springboot-aop</artifactid> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <!-- spring-boot aop依赖配置引入 --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-aop</artifactid> </dependency> <dependency> <groupid>com.alibaba</groupid> <artifactid>fastjson</artifactid> </dependency> </dependencies> </project>
五:运行结果
进入方法前执行..... tel:1234567 记录日志:operationlog{id='cd4b5ba7-7580-4989-a75e-51703f0dfbfc', createtime=fri sep 14 08:54:55 cst 2018, level=3, operationunit='user', method='com.wwj.springboot.service.impl.userserviceimpl.findusername', args='["1234567"]', userid='#{currentuserid}', username='#{currentusername}', describe=''#{currentusername}'=》通过手机号["1234567"]获取用户名', operationtype='select', runtime=4, returnvalue='"zhangsan"'} 方法最后执行..... 方法的返回值 : zhangsan
项目源码github地址:
感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。本文欢迎各位转载,但是转载文章之后必须在文章页面中给出作者和原文连接。
上一篇: flash怎么制作飞舞的萤火虫动画?
下一篇: 虎年伊始super相关域名价值近7万出售