时间:2021-05-02
1. 概述
在一般系统中,当我们做了一些重要的操作时,如登陆系统,添加用户,删除用户等操作时,我们需要将这些行为持久化。本文我们通过spring aop和java的自定义注解来实现日志的插入。此方案对原有业务入侵较低,实现较灵活
2. 日志的相关类定义
我们将日志抽象为以下两个类:功能模块和操作类型
使用枚举类定义功能模块类型moduletype,如学生、用户模块
? 1 2 3 4 5 6 7 8 9 10 11 12 public enum moduletype { default("1"), // 默认值 student("2"),// 学生模块 teacher("3"); // 用户模块 private moduletype(string index){ this.module = index; } private string module; public string getmodule(){ return this.module; } }使用枚举类定义操作的类型:eventtype。如登陆、添加、删除、更新、删除等
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public enum eventtype { default("1", "default"), add("2", "add"), update("3", "update"), delete_single("4", "delete-single"), login("10","login"),login_out("11","login_out"); private eventtype(string index, string name){ this.name = name; this.event = index; } private string event; private string name; public string getevent(){ return this.event; } public string getname() { return name; } }3. 定义日志相关的注解
3.1. @logenable
这里我们定义日志的开关量,类上只有这个值为true,这个类中日志功能才开启
? 1 2 3 4 5 6 7 8 9 10 @documented @retention(retentionpolicy.runtime) @target({elementtype.type}) public @interface logenable { /** * 如果为true,则类下面的logevent启作用,否则忽略 * @return */ boolean logenable() default true; }3.2. @logevent
这里定义日志的详细内容。如果此注解注解在类上,则这个参数做为类全部方法的默认值。如果注解在方法上,则只对这个方法启作用
? 1 2 3 4 5 6 7 8 @documented @retention(retentionpolicy.runtime) @target({java.lang.annotation.elementtype.method, elementtype.type}) public @interface logevent { moduletype module() default moduletype.default; // 日志所属的模块 eventtype event() default eventtype.default; // 日志事件类型 string desc() default ""; // 描述信息 }3.3. @logkey
此注解如果注解在方法上,则整个方法的参数以json的格式保存到日志中。如果此注解同时注解在方法和类上,则方法上的注解会覆盖类上的值。
? 1 2 3 4 5 6 7 8 @target({elementtype.field,elementtype.parameter}) @retention(retentionpolicy.runtime) @documented public @interface logkey { string keyname() default ""; // key的名称 boolean isuserid() default false; // 此字段是否是本次操作的userid,这里略 boolean islog() default true; // 是否加入到日志中 }4. 定义日志处理类
4.1. logadmmodel
定义保存日志信息的类
? 1 2 3 4 5 6 7 8 9 10 11 public class logadmmodel { private long id; private string userid; // 操作用户 private string username; private string admmodel; // 模块 private string admevent; // 操作 private date createdate; // 操作内容 private string admoptcontent; // 操作内容 private string desc; // 备注 set/get略 }4.2. ilogmanager
定义日志处理的接口类ilogmanager
我们可以将日志存入数据库,也可以将日志发送到开中间件,如果redis, mq等等。每一种日志处理类都是此接口的实现类
? 1 2 3 4 5 6 7 public interface ilogmanager { /** * 日志处理模块 * @param paramlogadmbean */ void deallog(logadmmodel paramlogadmbean); }4.3. dblogmanager
ilogmanager实现类,将日志入库。这里只模拟入库
? 1 2 3 4 5 6 7 @service public class dblogmanager implements ilogmanager { @override public void deallog(logadmmodel paramlogadmbean) { system.out.println("将日志存入数据库,日志内容如下: " + json.tojsonstring(paramlogadmbean)); } }5. aop的配置
5.1. logaspect定义aop类
使用@aspect注解此类
使用@pointcut定义要拦截的包及类方法
我们使用@around定义方法
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @component @aspect public class logaspect { @autowired private loginfogeneration loginfogeneration; @autowired private ilogmanager logmanager; @pointcut("execution(* com.hry.spring.mvc.aop.log.service..*.*(..))") public void managerlogpoint() { } @around("managerlogpoint()") public object aroundmanagerlogpoint(proceedingjoinpoint jp) throws throwable { …. } }aroundmanagerlogpoint:主方法的主要业务流程
1. 检查拦截方法的类是否被@logenable注解,如果是,则走日志逻辑,否则执行正常的逻辑
2. 检查拦截方法是否被@logevent,如果是,则走日志逻辑,否则执行正常的逻辑
3. 根据获取方法上获取@logevent 中值,生成日志的部分参数。其中定义在类上@logevent 的值做为默认值
4. 调用loginfogeneration的processingmanagerlogmessage填充日志中其它的参数,做个方法我们后面再讲
5. 执行正常的业务调用
6. 如果执行成功,则logmanager执行日志的处理(我们这里只记录执行成功的日志,你也可以定义记录失败的日志)
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 @around("managerlogpoint()") public object aroundmanagerlogpoint(proceedingjoinpoint jp) throws throwable { class target = jp.gettarget().getclass(); // 获取logenable logenable logenable = (logenable) target.getannotation(logenable.class); if(logenable == null || !logenable.logenable()){ return jp.proceed(); } // 获取类上的logevent做为默认值 logevent logeventclass = (logevent) target.getannotation(logevent.class); method method = getinvokedmethod(jp); if(method == null){ return jp.proceed(); } // 获取方法上的logevent logevent logeventmethod = method.getannotation(logevent.class); if(logeventmethod == null){ return jp.proceed(); } string optevent = logeventmethod.event().getevent(); string optmodel = logeventmethod.module().getmodule(); string desc = logeventmethod.desc(); if(logeventclass != null){ // 如果方法上的值为默认值,则使用全局的值进行替换 optevent = optevent.equals(eventtype.default) ? logeventclass.event().getevent() : optevent; optmodel = optmodel.equals(moduletype.default) ? logeventclass.module().getmodule() : optmodel; } logadmmodel logbean = new logadmmodel(); logbean.setadmmodel(optmodel); logbean.setadmevent(optevent); logbean.setdesc(desc); logbean.setcreatedate(new date()); loginfogeneration.processingmanagerlogmessage(jp, logbean, method); object returnobj = jp.proceed(); if(optevent.equals(eventtype.login)){ //todo 如果是登录,还需要根据返回值进行判断是不是成功了,如果成功了,则执行添加日志。这里判断比较简单 if(returnobj != null) { this.logmanager.deallog(logbean); } }else { this.logmanager.deallog(logbean); } return returnobj; } /** * 获取请求方法 * * @param jp * @return */ public method getinvokedmethod(joinpoint jp) { // 调用方法的参数 list classlist = new arraylist(); for (object obj : jp.getargs()) { classlist.add(obj.getclass()); } class[] argscls = (class[]) classlist.toarray(new class[0]); // 被调用方法名称 string methodname = jp.getsignature().getname(); method method = null; try { method = jp.gettarget().getclass().getmethod(methodname, argscls); } catch (nosuchmethodexception e) { e.printstacktrace(); } return method; } }6. 将以上的方案在实际中应用的方案
这里我们模拟学生操作的业务,并使用上文注解应用到上面并拦截日志
6.1. istudentservice
业务接口类,执行一般的crud
? 1 2 3 4 5 6 public interface istudentservice { void deletebyid(string id, string a); int save(studentmodel studentmodel); void update(studentmodel studentmodel); void querybyid(string id); }6.2. studentserviceimpl:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @logenable : 启动日志拦截 类上@logevent定义所有的模块 方法上@logeven定义日志的其它的信息 @service @logenable // 启动日志拦截 @logevent(module = moduletype.student) public class studentserviceimpl implements istudentservice { @override @logevent(event = eventtype.delete_single, desc = "删除记录") // 添加日志标识 public void deletebyid(@logkey(keyname = "id") string id, string a) { system.out.printf(this.getclass() + "deletebyid id = " + id); } @override @logevent(event = eventtype.add, desc = "保存记录") // 添加日志标识 public int save(studentmodel studentmodel) { system.out.printf(this.getclass() + "save save = " + json.tojsonstring(studentmodel)); return 1; } @override @logevent(event = eventtype.update, desc = "更新记录") // 添加日志标识 public void update(studentmodel studentmodel) { system.out.printf(this.getclass() + "save update = " + json.tojsonstring(studentmodel)); } // 没有日志标识 @override public void querybyid(string id) { system.out.printf(this.getclass() + "querybyid id = " + id); } }执行测试类,打印如下信息,说明我们日志注解配置启作用了:
将日志存入数据库,日志内容如下:
? 1 {"admevent":"4","admmodel":"1","admoptcontent":"{\"id\":\"1\"}","createdate":1525779738111,"desc":"删除记录"}7. 代码
以上的详细的代码见下面
github代码,请尽量使用tag v0.21,不要使用master,因为我不能保证master代码一直不变
原文链接:https://blog.csdn.net/hry2015/article/details/80244765
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
spring对AOP的实现提供了很好的支持。下面我们就使用Spring的注解来完成AOP做一个例子。首先,为了使用Spring的AOP注解功能,必须导入如下几个
本文演示的是Spring中使用AspectJ注解和XML配置两种方式实现AOP下面是使用AspectJ注解实现AOP的JavaProject首先是位于class
配置注解的支持:在spring4之后,想要使用注解形式,必须得要引入aop的包org.springframeworkspring-aop5.2.8.RELEAS
Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的jar包aopal
这篇文章给大家介绍了springAOP的实现方式,三种分别是纯XML方式,XML+注解,纯注解方式。Spring实现AOP思想使⽤的是动态代理技术