2

我在 2 个控制器上使用 @SessionAttributes 并且遇到了一些非常奇怪的行为。我的第一个控制器 (ViewController) 只是一个显示 JSP 页面的视图控制器。另一个是处理 Ajax 请求的控制器(AjaxController)。我有一个会话属性,它只是一个以 HashMap 作为成员的对象。该对象是地图的包装器。地图从数据库中填充并放入会话中,通过 ViewController 显示良好。但是,当我通过 ajax 请求 (AjaxController) 从地图中删除并刷新页面时,ViewController 有时会显示该元素已被删除,而其他时候该元素仍然存在。这是代码片段:

ViewController(首页简单显示userSettings所包含的地图内容

@Controller
@SessionAttributes({"userSettings"})
public class ViewController {

@RequestMapping(value="/", method=RequestMethod.GET)
    public String home(ModelMap model) {
        UserSettings userSettings = (UserSettings) model.get("userSettings");
        String userListenersJson = userSettings.toJson();  // for bootsrtapping the js on the front end

        return "views/home";
    }
}

Ajax控制器:

@Controller
@SessionAttributes({"userSettings"})
public class AjaxController {

@RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public @ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
        @PathVariable long externalId) {

            UserSettings userSettings = (UserSettings) model.get("userSettings");
            userSettings.removeSetting(externalId);
            return new AjaxResponse<String>(null, true);    
}
}

我在这里使用@SessionAttributes 错了吗?为什么这有时会起作用,而其他时候却不起作用?我还尝试将所有视图和 ajax 功能放在同一个控制器中并体验到相同的行为。

谢谢你的帮助!

编辑:

我已经重构了我的代码以通过 springsecurity 使用 UserPrincipal。我的理解是这个对象存储在会话中。无论如何,我看到的行为完全相同。

这是填充用户设置映射的 UserPrincipal 构造函数。我在这里设置了断点以确保设置了正确的 listenerDBO——它们每次都设置。这是唯一一次将侦听器从 db 设置到 CustomUserPrincipal 中的 UserSettings 对象中。所有其他添加/删除都是通过控制器完成的(快速旁白:添加永远不会失败......仅删除):

public CustomUserPrincipal(UserDBO userDBO) {
    // set UserSettings obj
    UserSettingsAdapter.addListeners(userDBO.getUserListenerDBOs(), userSettings);
}

UserSettings 对象本身:

public class UserSettings implements Serializable {

    private static final long serialVersionUID = -1882864351438544088L;
    private static final Logger log = Logger.getLogger(UserSettings.class);

    private Map<Long, Listener> userListeners = Collections.synchronizedMap(new HashMap<Long, Listener>(1));

    // get the listeners as an arraylist
    public List<Listener> userListeners() {
        return new ArrayList<Listener>(userListeners.values());
    }

    public Map<Long, Listener> getUserListeners() {
        return userListeners;
    }

    public Listener addListener(Listener listener) {
        userListeners.put(listener.getId(), listener);
        return listener;
    }

    // I'm logging here to try and debug the issue. I do see the success
    // message each time this function is called
    public Listener removeListener(Long id) {
        Listener l = userListeners.remove(id);
        if (l == null) {
            log.info("failed to remove listener with id " + id);
        } else {
            log.info("successfully removed listener with id " + id);
        }

        log.info("Resulting map: " + userListeners.toString());
        log.info("Map hashcode: " + userListeners.hashCode());

        return l;
    }


    public Listener getListener(long id) {
        return userListeners.get(id);
    }
  }

这是 UserSettingsAdapter 类中添加到 UserSettings 对象的辅助函数,从 CustomUserDetails 构造函数调用:

public static void addListeners(Set<UserListenerDBO> userListeners, UserSettings userSettings) {
    for (UserListenerDBO userListenerDBO : userListeners) {
        if (userListenerDBO.isActive()) {
            addListener(userListenerDBO, userSettings);
        }
    }
}

我还将控制器代码更改为使用 CustomUserPrincipal 对象而不是 @SessionAttributes:

在视图控制器中:

@RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
   CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
   UserSettings userSettings = userPrincipal.getUserSettings();
   String userListenersJson = userSettings.toJson();
   return "views/home";
}

在 AjaxController 中:

@RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public @ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
        @PathVariable long externalId) {
   CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
   UserSettings userSettings = userPrincipal.getUserSettings();
   userSettings.removeListener(externalId);

   return new AjaxResponse<String>(null, true); 
}

我希望这有助于阐明这个问题!

4

2 回答 2

0

我遇到了@SessionAttributes 的类似问题。控制器在类级别具有 @SessionAttributes 注释,其中一种方法处理 POST 请求,并包含会话管理对象的实例作为参数。此实例已保存到数据库中,但被后续请求重新使用,导致某些数据损坏。我们必须添加另一个 SessionStatus 类型的方法参数,然后调用SessionStatus.setComplete(). 这会导致实例从会话中删除,并防止重用和损坏。因此,尝试将 SessionStatus 实例添加到控制器的处理程序方法中,并在适当的情况下调用 setComplete()。

isComplete()编辑:我在最初的回答中不小心引用了吸气剂;我的意思是引用 setter setComplete()

于 2012-06-05T19:16:03.937 回答
0

@SessionAttributes 特定于一个控制器,而不是在多个控制器之间共享。相反,请考虑手动使用 session.setAttribute(类 HttpSession)。

你应该看看这里:http ://beholdtheapocalypse.blogspot.fr/2013/01/spring-mvc-framework-sessionattributes.html

于 2013-09-19T07:18:40.513 回答