无法掌握JSF的CDI注入:会话作用域实例注入不一致问题
这种情况我在日常开发中碰到过好多次,大概率是会话作用域Bean的代理配置没做好,或者注入方式出了问题,咱们一步步来排查解决:
1. 先确认会话作用域的核心配置是否到位
你说AppLogin是会话作用域,但如果没有正确配置代理模式,当请求作用域的LoginLookup去依赖它时,容器没办法帮你绑定到当前会话的实例,反而会每次新建一个。以Spring为例,正确的AppLogin配置应该是这样的:
@SessionScope(proxyMode = ScopedProxyMode.TARGET_CLASS) public class AppLogin { private String token; // 这里添加getter和setter方法 }
重点就是proxyMode = ScopedProxyMode.TARGET_CLASS,它会让容器生成一个代理类,每次请求时都帮你拿到当前会话对应的AppLogin实例,而不是直接创建新对象。
2. 检查LoginLookup里的注入方式
绝对不能自己手动new AppLogin()!一定要通过容器的依赖注入来获取实例,推荐用构造器注入(最安全的方式):
@RequestScope public class LoginLookup { private final AppLogin appLogin; // 构造器注入,让容器自动传入当前会话的AppLogin代理实例 public LoginLookup(AppLogin appLogin) { this.appLogin = appLogin; } public void authenticate() { // 先判断token是否存在,不存在才生成 if (appLogin.getToken() == null) { String newToken = generateUniqueToken(); // 你的令牌生成逻辑 appLogin.setToken(newToken); } } // 模拟令牌生成方法 private String generateUniqueToken() { return UUID.randomUUID().toString(); } }
如果你用的是字段注入@Autowired也可以,但构造器注入更利于测试和避免空指针问题。
3. 验证是不是真的会话不一致
有时候你觉得实例不同,可能是测试时用了不同的会话!比如开了无痕窗口、每次请求清除了Cookie(JSESSIONID变了),这种情况下每个会话本来就会有自己的AppLogin实例。你可以加个日志验证:
在AppLogin里重写toString:
@Override public String toString() { return "AppLogin{hashCode=" + hashCode() + ", token='" + token + "'}"; }
然后在LoginLookup的authenticate方法里打印会话ID和AppLogin信息:
public void authenticate(HttpServletRequest request) { System.out.println("当前会话ID: " + request.getSession().getId()); System.out.println("AppLogin实例信息: " + appLogin); if (appLogin.getToken() == null) { String newToken = generateUniqueToken(); appLogin.setToken(newToken); System.out.println("生成新令牌: " + newToken); } else { System.out.println("复用已有令牌: " + appLogin.getToken()); } }
同一个会话里多次调用authenticate,应该会看到同一个hashCode的AppLogin实例,且只会生成一次令牌。
4. 排查容器环境配置
如果是Spring Boot项目,确保你引入了spring-boot-starter-web依赖,因为会话作用域需要Web环境支持;如果是普通Spring项目,要使用AnnotationConfigWebApplicationContext作为应用上下文,而不是普通的AnnotationConfigApplicationContext,不然容器识别不了会话作用域。
按照这几步排查下来,基本就能解决你的问题了——核心就是让会话作用域Bean生成代理,确保请求作用域的Bean能拿到当前会话的正确实例。
内容的提问来源于stack exchange,提问作者ScottFree

