我在 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);
}
我希望这有助于阐明这个问题!