我正在尝试开发一个小的Spring MVC应用程序,我希望User对象在每个会话开始时初始化。
我有用户类
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyUser implements User {
// private fields
// getters and setters
public void fillByName(String username) {
userDao.select(username);
}
}
我想在Spring Security识别用户时初始化MyUser对象,在Interceptor Class 中(顺便说一下,这是一个好习惯吗?)
public class AppInterceptor extends HandlerInterceptorAdapter {
@Autowired
MyUser user;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
user.fillByName(auth.getName());
}
return true;
}
}
因此,当Controller处理请求时,已经初始化了会话范围的User类。但是当我尝试用Jackson序列化MyUser对象时,它只是不起作用:
@RequestMapping("/")
public String launchApp(ModelMap model) {
ObjectMapper mapper = new ObjectMapper();
try {
System.out.println(user.getUsername()); // Works good!
model.addAttribute("user", mapper.writeValueAsString(user)); // Doesn't work
} catch (JsonProcessingException e) {
// @todo log an error
}
return "app/base";
}
正如您所看到的,MyUser对象getter在Controller类中运行良好,但Jackson - 却没有。
当我从User对象中删除@Scope注释时,Jackson序列化开始工作。
显然,作用域代理bean和单例Controller类是问题
但我怎么能解决它?
-
更新
看起来我是第一个遇到这个的人:) 也许这是糟糕的建筑?我应该在Controller中创建一个新的MyUser类实例吗?通常的做法是什么?
答案 0 :(得分:2)
我能想到的另一种方法是添加另一个包装器:
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyScopedUser implements User {
private MyUser myUser;
// private fields
// getters and setters
public void fillByName(String username) {
userDao.select(username);
}
public MyUser getMyUser() {
return this.myUser;
}
}
所以现在你的MyScopedUser
是一个范围内的代理,但核心用户是一个普通的类。您可以稍后让用户离开并编组:
mapper.writeValueAsString(scopeUser.getMyUser())
答案 1 :(得分:1)
原因是你的proxyMode
设置为TARGET_CLASS
,它指示Spring为你的类创建一个基于类的代理(实质上是一个实现原始类接口的新类) ,但可能与ObjectMapper
将对象转换为字符串的方式不兼容。
您可以通过告诉ObjectMapper
使用writerFor
编写对象时使用哪些类签名来解决此问题:
mapper.writerFor(User.class).writeValueAsString(user)
或者,如果您已有自定义编写器,请使用forClass
,如下所示:
writer.forClass(User.class).writeValueAsString(user)