2012-05-25 3 views
2

Я использую @SessionAttributes на 2 контроллерах и испытываю некоторые очень странные действия. Мой первый контроллер (ViewController) - это просто контроллер представления, который отображает страницы JSP. Другой - контроллер, который обрабатывает запросы Ajax (AjaxController). У меня есть атрибут сеанса, который является просто объектом, который имеет HashMap в качестве члена. Объект является оберткой вокруг карты. Карта заполняется из базы данных и помещается в сеанс, который отлично отображается через ViewController. Однако, когда я делаю удаление с карты с помощью ajax-запроса (AjaxController) и обновляю страницу, ViewController SOMETIMES показывает, что элемент удален, но в остальное время элемент все еще существует. Вот фрагменты кода:Strange Spring @SessionAttributes Behavior

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="https://stackoverflow.com/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 в один и тот же контроллер и испытал такое же поведение.

Спасибо за помощь!

EDIT:

Я переработан мой код немного, чтобы использовать UserPrincipal через springsecurity. Я понимаю, что этот объект хранится в сеансе. Несмотря ни на что, я вижу точно такое же поведение.

Вот конструктор UserPrincipal, который заполняет карту пользовательских настроек. Я установил контрольные точки здесь, чтобы убедиться, что установлены правильные listenerDBOs - они есть, каждый раз. Это единственный раз, когда слушатели получают из db в объект UserSettings в CustomUserPrincipal. Все остальные добавляет/удаляет выполняются с помощью контроллеров (быстро в сторону: не прибавляет не подведет ... только удаляет):

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="https://stackoverflow.com/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); 
} 

Я надеюсь, что это помогает пролить свет на вопрос!

+0

[@SessionAttributes Проблема] (http://forum.springsource.org/showthread.php?49396-SessionAttribute-problems) Вы можете посмотреть этот –

+0

Спасибо за связь. Дело в том, что этот поток говорит, что работы @SessionAttributes отличаются от поведения, которое я вижу. Объект присутствует в обоих контроллерах, но когда я удаляю параметр через userSettings.removeSetting (..), он иногда удаляет, а иногда нет. – threejeez

+0

«У каждого контроллера есть собственный ModelMap, поэтому что-то, помеченное как @SessionAttributes в контроллере1, недоступно в контроллере2 и наоборот. Для этого вам придется вручную вводить материал в сеанс». -из ссылки выше –

ответ

0

У меня возникла аналогичная проблема с @SessionAttributes. Контроллер имел аннотацию @SessionAttributes на уровне класса, а один из методов обрабатывал запросы POST и включал экземпляр объекта, управляемого сеансом, в качестве аргумента. Этот экземпляр был сохранен в базе данных, но был повторно использован последующими запросами, что привело к некоторому повреждению данных.Нам пришлось добавить еще один аргумент метода типа SessionStatus и вызвать SessionStatus.setComplete(). Это вызвало удаление экземпляра из сеанса и предотвращение повторного использования и коррупции. Поэтому попробуйте добавить экземпляр SessionStatus к методам обработчика вашего контроллера и при необходимости вызвать setComplete().

РЕДАКТИРОВАТЬ: Я случайно ссылался на геттер isComplete() в своем первоначальном ответе; Я имел в виду ссылку установщика setComplete().

+0

Я пробовал как setComplete(), так и isComplete(), и поведение не менялось. Если я удалю с карты в сеансе, это займет от 1 до n попыток «вставить». – threejeez

0

@SessionAttributes специфичен для контроллера и не используется совместно несколькими контроллерами. Вместо этого рассмотрите возможность использования вручную session.setAttribute (класс HttpSession).

Вы должны посмотреть здесь: http://beholdtheapocalypse.blogspot.fr/2013/01/spring-mvc-framework-sessionattributes.html