int period() default 5000
}
定义注解处理器 - 切面
@Aspect
@Component
public class RetryAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@AfterThrowing(pointcut=(“execution(* com.liziba.web.service.impl.RetryHttpRequestClient.*(…)) && @annotation(com.liziba.web.annotation.Retry)”))
public void tryAgain(JoinPoint point) {
logger.info("------------开始重试------------");
try {
// 获取方法上定义的Retry注解
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Retry retry = methodSignature.getMethod().getAnnotation(Retry.class);
// ThreadLocal做线程隔离 times记录已经重试的次数
Object object = point.getTarget();
Field field = object.getClass().getDeclaredField(“threadLocal”);
field.setAccessible(true);
ThreadLocal> threadLocal = (ThreadLocal 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》 【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享 tring, Object>>) field.get(object); Map threadLocalMap = threadLocal.get(); AtomicInteger times = (AtomicInteger)threadLocalMap.get(“times”); // 重试 -1一直循环 if (retry.value() == -1 || (times.intValue() < retry.value())) { logger.info(“开始重试第”+ index + “次”); int index = times.incrementAndGet(); MethodInvocationProceedingJoinPoint methodPoint = ((MethodInvocationProceedingJoinPoint) point); methodPoint.proceed(); } else { // 数据库服务异常捕获,防止异常中异常导致StackOverFlowError try { // ToDo // 此处可以保存到数据库,做周期性失败重试,重试次数达到阈值后通过消息平台通知到运维及时处理异常任务 // 移除threadLocal,防止线程生命周期过长导致内存泄露 threadLocal.remove(); } catch (Exception e) { e.printStackTrace(); } } } catch (Throwable throwable) { logger.error(throwable.getMessage(),throwable); // 失败后继续重试 tryAgain(point); } } } 4.2 日志记录注解 定义注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default “”; boolean enable() default true; LogActionType type() default LogActionType.SELECT; } 日志类型 public enum LogActionType { ADD(“新增”), SELECT(“查询”), UPDATE(“更新”), DELETE(“删除”); private String value; LogActionType(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } 定义注解处理器 - 切面 @Component @Aspect @Slf4j public class LogAspect { // 保存日志信息服务 private final LogService logService; // 线程隔离,用于计算每个方法的执行时间 ThreadLocal currentTime = new ThreadLocal<>(); public LogAspect(LogService logService) { this.logService = logService; } @Pointcut("@annotation(com.liziba.annotation.Log)") public void logPointcut() { } @Around(“logPointcut()”) public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { Object result; // 计算方法操作时间 currentTime.set(System.currentTimeMillis()); result = joinPoint.proceed(); Log log = new Log(“INFO”,System.currentTimeMillis() - currentTime.get()); currentTime.remove(); HttpServletRequest request = RequestHolder.getHttpServletRequest(); // 记录用户名、浏览器信息、ip地址、切入点、日志信息 logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, log); return result; } @AfterThrowing(pointcut = “logPointcut()”, throwing = “e”) public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { Log log = new Log(“ERROR”,System.currentTimeMillis() - currentTime.get()); currentTime.remove(); // 获取日志堆栈信息,并设值 log.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes()); HttpServletRequest request = RequestHolder.getHttpServletRequest(); // 记录用户名、浏览器信息、ip地址、切入点、日志信息 logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log); } public String getUsername() { try { return SecurityUtils.getCurrentUsername(); }catch (Exception e){ return “”; } } } 4.3 限流注解 - redis+lua限流 注解定义 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Limit { // 资源名称,用于描述接口功能 String name() default “”; // 资源 key String key() default “”; // key prefix String prefix() default “”; // 时间的,单位秒 int period(); // 限制访问次数 int count(); // 限制类型 LimitType limitType() default LimitType.CUSTOMER; } 注解类型 public enum LimitType { // 默认 CUSTOMER, // IP限流 IP } 定义注解处理器 - 切面 @Aspect @Component public class LimitAspect { // redis用于执行lua脚本 private final RedisTemplate redisTemplate; private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class); public LimitAspect(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Pointcut("@annotation(com.liziba.annotation.Limit)") public void pointcut() { } @Around(“pointcut()”) public Object around(ProceedingJoinPoint joinPoint) throws Throwable { HttpServletRequest request = RequestHolder.getHttpServletRequest(); // 获取方法的Limit注解 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method signatureMethod = signature.getMethod(); Limit limit = signatureMethod.getAnnotation(Limit.class); LimitType limitType = limit.limitType(); String key = limit.key(); if (StringUtils.isEmpty(key)) { if (limitType == LimitType.IP) { key = StringUtils.getIp(request); } else { key = signatureMethod.getName(); } } // 通过方法或者ip 结合资源请求路径定义限流key ImmutableList keys = ImmutableList.of(StringUtils.join(limit.prefix(), “", key, "”, request.getRequestURI().replaceAll("/","_"))); // Lua限流脚本 String luascript = buildLuascript(); Redisscript redisscript = new DefaultRedisscript<>(luascript, Number.class);
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》 【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
tring, Object>>) field.get(object);
Map threadLocalMap = threadLocal.get();
AtomicInteger times = (AtomicInteger)threadLocalMap.get(“times”);
// 重试 -1一直循环
if (retry.value() == -1 || (times.intValue() < retry.value())) {
logger.info(“开始重试第”+ index + “次”);
int index = times.incrementAndGet();
MethodInvocationProceedingJoinPoint methodPoint = ((MethodInvocationProceedingJoinPoint) point);
methodPoint.proceed();
} else {
// 数据库服务异常捕获,防止异常中异常导致StackOverFlowError
// ToDo
// 此处可以保存到数据库,做周期性失败重试,重试次数达到阈值后通过消息平台通知到运维及时处理异常任务
// 移除threadLocal,防止线程生命周期过长导致内存泄露
threadLocal.remove();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
logger.error(throwable.getMessage(),throwable);
// 失败后继续重试
tryAgain(point);
定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default “”;
boolean enable() default true;
LogActionType type() default LogActionType.SELECT;
日志类型
public enum LogActionType {
ADD(“新增”),
SELECT(“查询”),
UPDATE(“更新”),
DELETE(“删除”);
private String value;
LogActionType(String value) {
this.value = value;
public String getValue() {
return value;
public void setValue(String value) {
@Slf4j
public class LogAspect {
// 保存日志信息服务
private final LogService logService;
// 线程隔离,用于计算每个方法的执行时间
ThreadLocal currentTime = new ThreadLocal<>();
public LogAspect(LogService logService) {
this.logService = logService;
@Pointcut("@annotation(com.liziba.annotation.Log)")
public void logPointcut() {
@Around(“logPointcut()”)
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object result;
// 计算方法操作时间
currentTime.set(System.currentTimeMillis());
result = joinPoint.proceed();
Log log = new Log(“INFO”,System.currentTimeMillis() - currentTime.get());
currentTime.remove();
HttpServletRequest request = RequestHolder.getHttpServletRequest();
// 记录用户名、浏览器信息、ip地址、切入点、日志信息
logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, log);
return result;
@AfterThrowing(pointcut = “logPointcut()”, throwing = “e”)
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
Log log = new Log(“ERROR”,System.currentTimeMillis() - currentTime.get());
// 获取日志堆栈信息,并设值
log.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes());
logService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log);
public String getUsername() {
return SecurityUtils.getCurrentUsername();
}catch (Exception e){
return “”;
注解定义
public @interface Limit {
// 资源名称,用于描述接口功能
String name() default “”;
// 资源 key
String key() default “”;
// key prefix
String prefix() default “”;
// 时间的,单位秒
int period();
// 限制访问次数
int count();
// 限制类型
LimitType limitType() default LimitType.CUSTOMER;
注解类型
public enum LimitType {
// 默认
CUSTOMER,
// IP限流
IP
public class LimitAspect {
// redis用于执行lua脚本
private final RedisTemplate redisTemplate;
private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class);
public LimitAspect(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
@Pointcut("@annotation(com.liziba.annotation.Limit)")
public void pointcut() {
@Around(“pointcut()”)
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法的Limit注解
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method signatureMethod = signature.getMethod();
Limit limit = signatureMethod.getAnnotation(Limit.class);
LimitType limitType = limit.limitType();
String key = limit.key();
if (StringUtils.isEmpty(key)) {
if (limitType == LimitType.IP) {
key = StringUtils.getIp(request);
key = signatureMethod.getName();
// 通过方法或者ip 结合资源请求路径定义限流key
ImmutableList keys = ImmutableList.of(StringUtils.join(limit.prefix(), “", key, "”, request.getRequestURI().replaceAll("/","_")));
// Lua限流脚本
String luascript = buildLuascript();
Redisscript redisscript = new DefaultRedisscript<>(luascript, Number.class);
上一篇 RocketMQ
下一篇 Netty基于Http协议的服务端入门开发,2021年Java笔试题总
版权所有 (c)2021-2022 MSHXW.COM
ICP备案号:晋ICP备2021003244-6号