Spring MVC @Scope代理bean和Jackson 2

sil*_*box 5 spring json spring-mvc jackson

我正在尝试开发一个小的Spring MVC应用程序,我希望User对象从每个会话开始初始化.

我有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);
    }

}
Run Code Online (Sandbox Code Playgroud)

我想在Spring Security识别用户时初始化MyUser对象,在拦截器类中(顺便说一句,这是一个好习惯吗?)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,当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";
    }
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,MyUser对象getter在Controller类中运行良好,但Jackson - 却没有.

当我从User对象中删除@Scope注释时,Jackson序列化开始工作.

显然,scoped proxy bean和singleton Controller类是问题所在

但我怎么能解决它?

-

UPDATE

看起来我是第一个碰到这个的人:)也许这是糟糕的建筑?我应该在Controller中创建一个新的MyUser类实例吗?常见的做法是什么?

ggr*_*nig 5

原因是 yourproxyMode设置为TARGET_CLASS,这指示 Spring 为你的类创建一个基于类的代理(本质上是一个实现原始类接口的新类,但可能与将ObjectMapper对象转换为字符串的方式不兼容) )。

您可以通过告诉ObjectMapper在编写对象时使用哪些类签名来解决这个问题writerFor,如下所示:

mapper.writerFor(User.class).writeValueAsString(user)
Run Code Online (Sandbox Code Playgroud)

或者,如果您已经有自定义编写器,请使用forClass,如下所示:

writer.forClass(User.class).writeValueAsString(user)
Run Code Online (Sandbox Code Playgroud)


Bij*_*men 4

我能想到的一种方法是添加另一个包装器:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以现在你MyScopedUser是一个作用域代理,但核心用户是一个普通类。您可以稍后让用户退出并编组:

mapper.writeValueAsString(scopeUser.getMyUser())
Run Code Online (Sandbox Code Playgroud)