0%

使用 ThreadLocal 实现上下文

为了避免使用方法直接显示的参数传递,我们可以采用 ThreadLocal 实现上下文来存储和获取参数,假设我们现在需要实现提现上下文 WithdrawContext,首先定义一个上下文类 WithdrawContext,并定义对应上下文管理器 WithdrawManager

1
2
3
4
5
6
7
8
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WithdrawContext {
// eg
private String withdrawId;
}
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
public class WithdrawContextManager {

private static final ThreadLocal<WithdrawContext> THREAD_LOCAL = new InheritableThreadLocal<>();

/**
* init Context
*
* @param context WithdrawContext
*/
public static void setContext(WithdrawContext context) {
THREAD_LOCAL.set(context);
}

/**
* get current thread context
*
* @return WithdrawContext
*/
public static WithdrawContext getContext() {
return THREAD_LOCAL.get();
}

/**
* clear conext
*/
public static void removeContext() {
THREAD_LOCAL.remove();
}
}

为了简化开发,我们可以采用切面来初始化和销毁上下文,首先我们定义如下注解,并采用 AOP 切面来针对声明有对应注解的方法进行切面处理:

1
2
3
4
5
6
// 该注解可被声明在构造器方法和普通方法上
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WithdrawContextHolder {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
@Aspect
public class WithdrawContextAspect {

@Pointcut(value = "@annotation(com.shoto.threadlocal.WithdrawContextHolder)")
public void pointCut() {

}

@Around("pointCut()")
public Object invokeAround(ProceedingJoinPoint joinPoint) throws Throwable {
try {
WithdrawContextManager.removeContext();
WithdrawContextManager.setContext(new WithdrawContext());
return joinPoint.proceed();
} finally {
// 销毁方法是必须的,防止 OOM
WithdrawContextManager.removeContext();
}
}
}

至此,我们便可以在 Service 方法上进行使用,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class WithdrawServiceImpl implements WithdrawService {

@Override
@WithdrawContextHolder // 注解声明
public String getWithdrawId() {
WithdrawContext withdrawContext = WithdrawContextManager.getContext();
withdrawContext.setWithdrawId(UUID.randomUUID().toString());
// 这里我们无须在 queryWithdraw 上传递 WithdrawContext 参数即可获取上下文参数信息
return queryWithdraw();
}

private String queryWithdraw() {
WithdrawContext withdrawContext = WithdrawContextManager.getContext();
return withdrawContext.getWithdrawId();
}
}
------ 本文结束------