温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

使用Spring AOP怎么实现统一注解功能

发布时间:2021-05-24 17:59:51 来源:亿速云 阅读:208 作者:Leah 栏目:编程语言

本篇文章为大家展示了使用Spring AOP怎么实现统一注解功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

1. 概述

在一般系统中,当我们做了一些重要的操作时,如登陆系统,添加用户,删除用户等操作时,我们需要将这些行为持久化。本文我们通过Spring AOP和Java的自定义注解来实现日志的插入。此方案对原有业务入侵较低,实现较灵活

2. 日志的相关类定义

我们将日志抽象为以下两个类:功能模块和操作类型

使用枚举类定义功能模块类型ModuleType,如学生、用户模块

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。如登陆、添加、删除、更新、删除等

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,这个类中日志功能才开启

@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface LogEnable {   /**    * 如果为true,则类下面的LogEvent启作用,否则忽略    * @return    */   boolean logEnable() default true; }

3.2. @LogEvent

这里定义日志的详细内容。如果此注解注解在类上,则这个参数做为类全部方法的默认值。如果注解在方法上,则只对这个方法启作用

@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的格式保存到日志中。如果此注解同时注解在方法和类上,则方法上的注解会覆盖类上的值。

@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

定义保存日志信息的类

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等等。每一种日志处理类都是此接口的实现类

public interface ILogManager {   /**    * 日志处理模块    * @param paramLogAdmBean    */   void dealLog(LogAdmModel paramLogAdmBean); }

4.3. DBLogManager

ILogManager实现类,将日志入库。这里只模拟入库

@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定义方法

@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执行日志的处理(我们这里只记录执行成功的日志,你也可以定义记录失败的日志)

@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

public interface IStudentService {   void deleteById(String id, String a);   int save(StudentModel studentModel);   void update(StudentModel studentModel);   void queryById(String id); }

6.2. StudentServiceImpl:

@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);   } }

执行测试类,打印如下信息,说明我们日志注解配置启作用了:

将日志存入数据库,日志内容如下:

{"admEvent":"4","admModel":"1","admOptContent":"{\"id\":\"1\"}","createDate":1525779738111,"desc":"删除记录"}

上述内容就是使用Spring AOP怎么实现统一注解功能,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI