【问题标题】:Spring MVC @Scope proxy bean & Jackson 2Spring MVC @Scope 代理 bean & Jackson 2
【发布时间】:2015-04-03 20:53:01
【问题描述】:

我正在尝试开发一个小型 Spring MVC 应用程序,我希望用户对象在每个会话开始时进行初始化。

我有用户类

@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 对象(顺便说一句,这是一个好习惯吗?)

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 对象获取器在 Controller 类中工作得很好,但 Jackson - 没有。

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

显然,作用域代理 bean 和单例控制器类是问题

但是我该如何解决呢?

--

更新

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

【问题讨论】:

    标签: json spring spring-mvc jackson


    【解决方案1】:

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

    @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())
    

    【讨论】:

      【解决方案2】:

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

      您可以通过使用writerFor 告诉ObjectMapper 在编写对象时使用哪些类签名来解决此问题:

      mapper.writerFor(User.class).writeValueAsString(user)
      

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

      writer.forClass(User.class).writeValueAsString(user)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-26
        • 2020-09-21
        • 2016-04-17
        • 2012-09-10
        • 2015-07-20
        • 2017-06-09
        相关资源
        最近更新 更多