我试图在 Spring MVC 应用程序中跨不同层(控制器、服务、存储库)访问参数的两种方法之间做出决定。例如:
public class UserContext {
private static final ThreadLocal<Long> userIdHolder = new ThreadLocal<>();
public static void setUserId(Long userId) {
userIdHolder.set(userId);
}
public static Long getUserId() {
return userIdHolder.get();
}
public static void clear() {
userIdHolder.remove();
}
}
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Long userId = // get userId from token or somewhere
UserContext.setUserId(userId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
UserContext.clear();
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/info")
public String getUserInfo() {
return userService.getUserInfo();
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public String getUserInfo() {
Long userId = UserContext.getUserId(); // Can access userId directly
return userRepository.findUserInfo(userId);
}
}
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Long userId = // get userId from token or somewhere
request.setAttribute("userId", userId);
return true;
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/info")
public String getUserInfo(@RequestAttribute("userId") Long userId) {
return userService.getUserInfo(userId);
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public String getUserInfo(Long userId) { // Need to pass userId as parameter
return userRepository.findUserInfo(userId);
}
}
哪种方法被认为是 Spring 应用程序中的最佳实践?我特别感兴趣的是:
这个问题更侧重于寻求最佳实践和建议,而不仅仅是比较技术差异。
我建议第三种方法,它适用于 Spring MVC(不适用于反应式堆栈),但以更干净的方式包装第一个
ThreadLocal
。
您可以定义一个holder
UserIdHolder
类以及将其定义为请求范围bean的配置,如下所示:
public class UserIdHolder {
private Long userId;
// Getters, setters
}
@Configuration
public class UserIdHolderConfiguration {
@Bean
@Scope("request")
UserIdHolder userIdHolder() {
return new UserIdHolder;
}
}
然后你可以通过
@Autowired
注解将其注入到拦截器和你需要的每个 bean 中; Spring 将管理此 bean 的实例,使其每次为任何请求创建,并在运行下游控制器/服务/存储库 bean 时引用正确的实例。
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private UserIdHolder userIdHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
Long userId = // get userId from token or somewhere
userIdHolder.setUserId(userId);
return true;
}
}
等等。
使用这个,你不必在每个请求结束时手动清理本地线程,因为它都是由 Spring 管理的,并且你不必手动将此属性作为参数传递给每个方法或在控制器签名。