JAVA语言之Spring AOP + 注解实现统一注解功能[Java代码]
龚超 2018-07-17 来源 : 阅读 2981 评论 0

摘要:本文主要向大家介绍了JAVA语言的Spring AOP + 注解实现统一注解功能,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言的Spring AOP + 注解实现统一注解功能,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

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":"删除记录"}

复制代码

希望对JAVA有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言JAVA频道!

本文由 @职坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 1
看完这篇文章有何感觉?已经有2人表态,50%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
本文作者 联系TA

擅长针对企业软件开发的产品设计及开发的细节与流程设计课程内容。座右铭:大道至简!

  • 370
    文章
  • 22914
    人气
  • 87%
    受欢迎度

已有23人表明态度,87%喜欢该老师!

进入TA的空间
求职秘籍 直通车
  • 索取资料 索取资料 索取资料
  • 答疑解惑 答疑解惑 答疑解惑
  • 技术交流 技术交流 技术交流
  • 职业测评 职业测评 职业测评
  • 面试技巧 面试技巧 面试技巧
  • 高薪秘笈 高薪秘笈 高薪秘笈
TA的其他文章 更多>>
WEB前端必须会的基本知识题目
经验技巧 93% 的用户喜欢
Java语言中四种遍历List的方法总结(推荐)
经验技巧 91% 的用户喜欢
Java语言之SHA-256加密的两种实现方法详解
经验技巧 75% 的用户喜欢
java语言实现把两个有序数组合并到一个数组的实例
经验技巧 75% 的用户喜欢
通过Java语言代码来创建view的方法
经验技巧 80% 的用户喜欢
其他海同师资 更多>>
吕益平
吕益平 联系TA
熟悉企业软件开发的产品设计及开发
孔庆琦
孔庆琦 联系TA
对MVC模式和三层架构有深入的研究
周鸣君
周鸣君 联系TA
擅长Hadoop/Spark大数据技术
范佺菁
范佺菁 联系TA
擅长Java语言,只有合理的安排和管理时间你才能做得更多,行得更远!
金延鑫
金延鑫 联系TA
擅长与学生或家长及时有效沟通
经验技巧30天热搜词 更多>>

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程