Spring MVC - разница между HttpSession.setAttribute и model.addObject

Недавно я пытаюсь изучить Spring MVC. Кажется, я плохо понял функциональные возможности аннотации @ModelAttribute и HttpSession.

@SessionAttributes({"shoppingCart", "count"})
public class ItemController {

@ModelAttribute("shoppingCart")
public List<Item> createShoppingCart() {
    return new ArrayList<Item>();
}

@ModelAttribute("count")
public Integer createCount() {
    return 0;
}

@RequestMapping(value="/addToCart/{itemId}", method=RequestMethod.GET)
public ModelAndView addToCart(@PathVariable("itemId") Item item, 
        @ModelAttribute("shoppingCart") List<Item> shoppingCart, @ModelAttribute("count") Integer count) {

    if(item != null) {
        shoppingCart.add(item);
        count = count + 1;
    }

    return new ModelAndView(new RedirectView("showAllItems")).addObject("count", count);
}

@RequestMapping(value="/deleteFromCart/{itemId}", method=RequestMethod.GET)
public ModelAndView deleteFromCart(@PathVariable("itemId") Item item, 
        HttpSession session) {

    List<Item> list = (List<Item>) session.getAttribute("shoppingCart");
    list.remove(item);
    //session.setAttribute("shoppingCart", list);

    Integer count = (Integer) session.getAttribute("count");
    count = count - 1;
    session.setAttribute("count", count);

    return new ModelAndView(new RedirectView("showAllItems"));
}

ShoppingCart и count являются атрибутами сеанса.

Проблема в методе deleteFromCart. Я получаю счетчик из сеанса, переназначаю его и переписываю в сеансе. Но я не вижу обновленного значения count на jsp. Однако обновленный объект shoppingCart можно увидеть обновленным, хотя я не перезаписываю объект сеанса (поскольку объект является тем же объектом, который уже находится в сеансе).

Но почему счетчик не обновляется, хотя я перезаписываю его с помощью session.setAttribute? Когда я добавляю новый объект count в модель (model.addObject("count", count)) тогда я вижу обновленное значение count. Но почему же session.setAttribute не дает такого же результата?


person akcasoy    schedule 05.05.2013    source источник


Ответы (3)


Во-первых, @SessionAttribute не обязательно использовать сеанс http. Он использует SessionAttributeStore, который может иметь что угодно в качестве резервного хранилища. Только реализация по умолчанию использует сеанс http.

Причина, по которой ваш код не работает должным образом, заключается в том, как работает @SessionAttribute.

Перед вызовом метода контроллера все перечисленное в @SessionAttributes, в вашем случае {"warenkorb", "count"}, считывается из сеанса и добавляется в модель.

После возврата метода сеанс обновляется всем, что было добавлено в модель в методе.

.addObject("count", count)

-> счетчик добавляется в модель, а затем в сеанс.

session.setAttribute("count", count)

-> счетчик добавляется к сеансу, но не к модели. Он будет добавлен в модель перед следующим вызовом любого метода контроллера. Но на данный момент у модели остался старый count. Модель — это то, что добавляется к запросу. И если атрибут можно найти в области запроса, то jsp не заботится о том, что находится в сеансе.

Когда вы используете @SessionAttributes и @ModelAttribute (или вообще Spring MVC), избегайте использования HttpSession или HttpRequest. Даже HttpResponseиспользуется ограниченно. Вместо этого примите красоту Spring MVC :)

person a better oliver    schedule 05.05.2013
comment
Итак, в основном; перед вызовом метода контроллера сеанс обновляет модель. После метода модель обновляет сеанс. Таким образом, единственный момент, когда один и тот же атрибут, который содержится и в Session, и в Model, может иметь разные значения, это метод контроллера. Большое Вам спасибо. - person akcasoy; 06.05.2013

model.addObject помещает объект в область запроса, а HTTPsession.setAttribute помещает его в область сеанса. А поскольку переменные в jsp разрешаются в следующем порядке: область страницы -> область запроса -> область сеанса -> область приложения, вы получаете то, что получаете.

person Patison    schedule 05.05.2013
comment
Но я определил оба атрибута как атрибуты сеанса выше с помощью @SessionAttributes({warenkorb, count}). Когда я перезаписываю счетчик атрибутов с помощью session.setAttribute(count, count), не должен ли он изменить атрибут с областью сеанса? возможно, мне не следовало задавать вопрос как разницу между setAttribute и addObject, а разницу между двумя объектами сеанса, используемыми в методе deleteFromBasket, которые оба приводят к разным результатам (warenkorb обновляется, count по-прежнему является старым объектом count). - person akcasoy; 05.05.2013
comment
Фух, наконец-то решил.. измените подпись метода на public ModelAndView deleteFromBasket(@PathVariable Integer position, @ModelAttribute("warenkorb") List<Item> warenkorb, @ModelAttribute("count") Integer count, ModelMap modelMap) и установите атрибут в сеансе: modelMap.addAttribute("count", --count); - person Patison; 06.05.2013
comment
Когда я меняю сигнатуру метода, как вы пишете, это работает, я знаю :) мой метод addToBasket работает так же. Я просто пытался понять общую концепцию, пытаясь использовать разные вещи, такие как HttpSession и т. д. Тем не менее, большое спасибо. - person akcasoy; 06.05.2013

Параметры метода Java передаются значениями. Вы можете присвоить этому параметру все, что хотите внутри метода, но вне его это не будет иметь никакого эффекта. Внутри метода вы имеете дело с копией параметра

person Radek    schedule 14.09.2014