前言
一直没机会做spring生态圈的框架,公司选择的是一些小众的微服务,鉴于此考虑,丰富自己的技术栈,花了两天时间从网上各网站上学习了springboot一些基础知识。 本章只介绍springboot微服务集成AOP,用于定义微服务中的切点,此处应用切面到记录操作日志的流程中,以记录操作日志为例。
环境准备
- IntelliJ IDEA
- 前一章中搭建的微服务框架
开始集成
- pom.xml中增加依赖包
依赖包.png
<dependency> | |
<groupId>org.springframework.boot</groupId> | |
<artifactId>spring-boot-starter-aop</artifactId> | |
</dependency> | |
<dependency> | |
<groupId>com.alibaba</groupId> | |
<artifactId>fastjson</artifactId> | |
<version>1.2.32</version> | |
</dependency> |
fastjson包为将对象转为json字符串使用。
- 定义操作日志实体类,在entity包下新建OperateLogBean类
实体类.png
package com.example.demo.entity; | |
import lombok.Data; | |
import lombok.Getter; | |
import lombok.Setter; | |
/** | |
* 类功能描述:<br> | |
* <ul> | |
* <li>类功能描述1<br> | |
* <li>类功能描述2<br> | |
* <li>类功能描述3<br> | |
* </ul> | |
* 修改记录:<br> | |
* <ul> | |
* <li>修改记录描述1<br> | |
* <li>修改记录描述2<br> | |
* <li>修改记录描述3<br> | |
* </ul> | |
* | |
* @author xuefl | |
* @version 5.0 since 2020-01-08 | |
*/ | |
@Getter | |
@Setter | |
@Data | |
public class OperateLogBean { | |
private String type; | |
private String operateObj; | |
private boolean result; | |
} |
- 定义注解,在demo下新建annotation包,用于存放注解,新建注解OperateLog
定义注解.png
package com.example.demo.annotation; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
/** | |
* 类功能描述:<br> | |
* <ul> | |
* <li>类功能描述1<br> | |
* <li>类功能描述2<br> | |
* <li>类功能描述3<br> | |
* </ul> | |
* 修改记录:<br> | |
* <ul> | |
* <li>修改记录描述1<br> | |
* <li>修改记录描述2<br> | |
* <li>修改记录描述3<br> | |
* </ul> | |
* | |
* @author xuefl | |
* @version 5.0 since 2020-01-07 | |
*/ | |
public OperateLog { | |
String type() default ""; | |
String operateObj() default ""; | |
} |
此注解近定义了操作类型,操作对象两个参数,请根据自己业务扩展
- 定义切面类,在demo包下新建aspect包,并在其下新建LogAspect类
切面类.png
package com.example.demo.aspect; | |
import com.alibaba.fastjson.JSONObject; | |
import com.example.demo.annotation.OperateLog; | |
import com.example.demo.entity.OperateLogBean; | |
import com.example.demo.rabbitmq.MsgProducer; | |
import com.example.demo.util.I18nProperties; | |
import lombok.extern.slf4j.Slf4j; | |
import org.aspectj.lang.ProceedingJoinPoint; | |
import org.aspectj.lang.annotation.Around; | |
import org.aspectj.lang.annotation.Aspect; | |
import org.aspectj.lang.annotation.Pointcut; | |
import org.aspectj.lang.reflect.MethodSignature; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.stereotype.Component; | |
import java.lang.reflect.Method; | |
/** | |
* 类功能描述:<br> | |
* <ul> | |
* <li>类功能描述1<br> | |
* <li>类功能描述2<br> | |
* <li>类功能描述3<br> | |
* </ul> | |
* 修改记录:<br> | |
* <ul> | |
* <li>修改记录描述1<br> | |
* <li>修改记录描述2<br> | |
* <li>修改记录描述3<br> | |
* </ul> | |
* | |
* @author xuefl | |
* @version 5.0 since 2020-01-06 | |
*/ | |
//标记类为Aspect类 | |
//引入到Spring容器中 | |
public class LogAspect { | |
private MsgProducer msgProducer; | |
public void operateLog(){ | |
} | |
//定义在http请求到方法之前将内容记录下来 | |
public Object doPointCut(ProceedingJoinPoint joinPoint) throws Throwable { | |
OperateLogBean operateLog = new OperateLogBean(); | |
buildOperateObj(joinPoint, operateLog); | |
boolean isCatchExcept = false; | |
Exception e = null; | |
Object result = null; | |
try { | |
result = joinPoint.proceed(); | |
} catch (Exception ex) { | |
log.error("response exception: {}", ex.getMessage()); | |
operateLog.setResult(false); | |
isCatchExcept = true; | |
e = ex; | |
} | |
operateLog.setResult(true); | |
// 用测试发送rabbitmq消息到日志微服务(此处为模拟) | |
String massage = JSONObject.toJSONString(operateLog); | |
msgProducer.send2DirectTestQueue(massage); | |
log.info(massage); | |
if (isCatchExcept) { | |
throw e; | |
} else { | |
return result; | |
} | |
} | |
//日志转换国际化 | |
private void buildOperateObj(ProceedingJoinPoint joinPoint, OperateLogBean operateLog) { | |
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); | |
Method method = signature.getMethod(); | |
OperateLog ol = method.getAnnotation(OperateLog.class); | |
if (ol != null) { | |
operateLog.setType(I18nProperties.getI18nString(ol.type())); | |
operateLog.setOperateObj(I18nProperties.getI18nString(ol.operateObj())); | |
} | |
} | |
} |
通过给类名增加@Aspect注解,标记本类是切面类,并用@Component将其引入spring容器中; 1). 定义切点,通过@Pointcut("@annotation(com.example.demo.annotation.OperateLog)")注解定义一个空方法为切点方法,参数值为前面定义的注解,表示将此注解作为一个切面,执行切点所定义的方法内容。 2). 实现切点执行方法,此处使用环绕模式@Around("operateLog()"),参数为定义切点的方法名,并在方法中将@OperateLog注解的参数通过国际化解析后发送到日志模块(此处为模拟)
- 国际化工具类引入,util下新建I18nProperties类,并在resources包下新建properties包,新建msg_zh_CN.properties和msg_en_US.properties,在国际化文件中增加所需要转化的字符串键值。
国际化工具类.png
properties.png
package com.example.demo.util; | |
import org.springframework.util.StringUtils; | |
import java.util.Locale; | |
import java.util.ResourceBundle; | |
/** | |
* 类功能描述:<br> | |
* <ul> | |
* <li>类功能描述1<br> | |
* <li>类功能描述2<br> | |
* <li>类功能描述3<br> | |
* </ul> | |
* 修改记录:<br> | |
* <ul> | |
* <li>修改记录描述1<br> | |
* <li>修改记录描述2<br> | |
* <li>修改记录描述3<br> | |
* </ul> | |
* | |
* @author xuefl | |
* @version 5.0 since 2020-01-08 | |
*/ | |
public class I18nProperties { | |
private static final String strPro = "properties.msg"; | |
/** | |
* 获取国际化字符串 | |
* @param strKey | |
* @return | |
*/ | |
public static String getI18nString(String strKey) | |
{ | |
Locale cn = Locale.CHINA;//中文 | |
Locale us = Locale.US;//英文 | |
String lang = "cn"; | |
try { | |
ResourceBundle strI18n = ResourceBundle.getBundle(strPro, cn); | |
if (!StringUtils.isEmpty(lang) && "English(en-US)".contains(lang)) { | |
strI18n = ResourceBundle.getBundle(strPro, us); | |
} | |
return strI18n.getString(strKey); | |
} catch (Exception e) { | |
return strKey; | |
} | |
} | |
} | |
select=查询 | |
delete=删除 | |
add=新增 | |
update=修改 | |
user_list=用户列表 | |
user=用户 |
- 自定义注解的使用
自定义注解使用.png
- 测试结果
测试结果.png 结果显示已经通过消息队列发送出去,并且模拟的日志模块已收到该消息。