奇怪的春天@SessionAttributes行为

时间:2012-05-25 04:53:07

标签: spring spring-mvc session-state tomcat7

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

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";
    }
}

AjaxController:

@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:

在ViewController中:

@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); 
}

我希望这有助于解决这个问题!

2 个答案:

答案 0 :(得分:0)

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

编辑:我在最初的答案中偶然引用了getter isComplete();我的意思是引用setter setComplete()

答案 1 :(得分:0)

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

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