gpt4 book ai didi

Spring AOP + 注解实现统一注解功能

转载 作者:qq735679552 更新时间:2022-09-27 22:32:09 27 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章Spring AOP + 注解实现统一注解功能由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

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 。

最后此篇关于Spring AOP + 注解实现统一注解功能的文章就讲到这里了,如果你想了解更多关于Spring AOP + 注解实现统一注解功能的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com