SpringMVC中的@SessionAttributes注解【详解】

在默认情况下,当ModelMap中的属性作用域是request级别时,也就是说,当本次请求结束后,ModelMap中的属性将销毁。如果希望在多个请求中共享ModelMap中的属性,必须将其属性转存到session中,这样ModelMap的属性才会被跨请求访问;

spring允许我们有选择地指定ModelMap中的哪些属性需要转存到session中,以便下一个请求属对应的ModelMap的属性列表中还能访问到这些属性。

注意:这里所说的将ModelMap中的属性转存Seesion中,不单单指ModelMap,其实控制器向jsp传值绑定数据的五种方式(ModelMap、Model、Session、Map、Request)都可以将其属性转存到Session中,下面就直接以ModelMap为例进行说明。

SpringMVC为我们提供这样一个注解来实现上面的场景:

  • @SessionAttributes:将ModelMap的属性值共享到session中。

1、@SessionAttributes注解:

【重点】:通过@SessionAttributes注解将ModelMap中的属性存入到Session中以后:

  • 可以在本次请求对应的jsp页面中使用request.getAttribute("");session.getAttribute("");获取到该属性;
  • 还可以通过下次其它请求对应的jsp页面中使用session.getAttribute("");ModelMap#get("");获得到该属性;
  • 也可以通过@ModelAattribute注解标识的参数中获取。

补充:request.getAttribute("")的scope为request,也就是在当前请求,从控制器到jsp携带的ModelMap对象中获取,而session.getAattribute("")的scope为session,是从Session中去获取属性值。

注意:@SessionAttributes注解只能使用在上,用于在多个请求之间传递参数,类似于Session的Attribute,但不完全一样,一般来说@SessionAttributes设置的参数只用于暂时的传递(存入sessionAttributeStore),而不是长期的保存,长期保存的数据还是要放到Session中。

有两种方式将ModelMap中的属性值共享到session中:

  1. 使用注解的value属性:可以通过属性名指定需要放到会话中的属性;
  2. 使用注解的types属性:还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。
方式一:通过注解的value属性:

@Controller 
@SessionAttributes("user")	//将ModelMap中key为user的属性共享到session中
public class DemoController {
	@RequestMapping("/hello")  
	public String hello(ModelMap model) {
		//向ModelMap中添加key为user和user1的属性
        model.addAttribute("user", new User(520, "U love me"));
        model.addAttribute("user1", new User("I love U"));
        return "result";
    }
}
@Controller
public class Demo2Controller {
	@RequestMapping(value = "/demo")
    public String dmeo(){
        return "result";
    }
}
  • 在DemoController的hello方法中,将key为user和user1的User对象存入ModelMap中,在hello请求对应的jsp页面可以获得到相应key为user和user1的User对象;同时通过注解@SessionAttributes将key为user的User对象存入到Session中。
  • 在Demo2Controller的demo方法中,我们并没有向jsp页面传入任何的值,正是因为在DemoController中通过注解已将key为user的属性存入到session中,所以在demo请求对应的jsp页面中还是可以获得到key为user对应的User对象。
  • 上面的例子中,我们通过属性名的方式,将对应的属性存入到session中。
方式二:通过注解的types属性:

@SessionAttributes(types = {User.class})
@Controller
public class DemoController{
    @RequestMapping("/hello")
    public String hello(Map<String, Object> map){
        map.put("user1", new User(520, "U love me"));
        return "hello";
    }
}
@Controller
public class Demo2Controller {
	@RequestMapping(value = "/demo")
    public String dmeo(){
        return "result";
    }
}
  • 类似的例子,这次是通过@SessionAttributes注解的types属性指定了User类型,所以它会将User类型的所有属性值存入到Session中,这样我们再demo请求所对应的jsp页面可以正常的获取到key为user1对应的属性值。

补充:这里我们仅将一个ModelMap的属性放入Session中,其实@SessionAttributes注解允许通过字符串数组的方式指定多个属性,例:@SessionAttributes(types = {User.class, String.class}, value={"user1", "user2"}

@SessionAttributes使用后可以调用SessionStatus.setComplete来清除,这个方法只是清除SessionAttribute里的参数,而不会应用Session中的参数。

@Controller
@SessionAttributes("pet") 
public class EditPetForm {
    // ...
    @PostMapping("/pets/{id}")
    public String handle(Pet pet, BindingResult errors, SessionStatus status){
        if (errors.hasErrors) {
            // ...
            status.setComplete(); //调用SessionStatus.setComplete来清除
            // ...
        }
    }
}

2、@SessionAttribute注解:

Springmvc中有一个和@SessionAttributes很像的注解:@SessionAttribute注解标注在方法参数上,它的作用是获取session中可能预先存入的属性值。(我自己测试该注解并没有什么作用,想在当前控制器内获取其它控制器通过@SessionAttributes预先存入Session中的值,主要还是需要在当前控制器的类上添加@SessionAttributes注解的方式获取,看下面测试)

测试1:

在DemoController中通过demo请求将key为user值为User user的属性通过@SessionAttributes(“user”)存入到session中,在demo请求对应的demo.jsp页面中可以通过${user}获得key为user对应的属性值,也可以通过${sessionScope.user}获取到该属性值;

@Controller
@SessionAttributes("user")
public class DemoController {
    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        User user = new User(520, "U love me");
        map.put("user", user);
        return "result";
    }
}

此时,我在Demo2Controller中通过demo2请求的方法中,将User user参数标注为@SessionAttribute(“user”),此时在方法中并没有获取到Session中的key为user的属性值,在demo2请求对应的jsp页面中能${sessionScope.user}属性值,不能通过${user}获取到对应的属性值,控制台打印:UserId: 0、UserName: null;

结论:说明只在方法参数上添加@SessionAttribute注解,在当前方法中并不能从session获取到key为user的属性值,也没有将其属性值存入到当前控制器的ModelMap中,@SessionAttribute注解没生效。

测试2:

DemoController不变,在Demo2Controller类上添加@SessionAttributes(“user”)注解,将参数中的@SessionAttribute(“user”)注解去掉,然后通过demo2方法对应的jsp页面中,成功通过${sessionScope.user}${user}获取到了对应的属性值,控制台打印:UserId: 520,UserName: U love me。

@Controller
@SessionAttributes("user")
public class DemoController {
    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        User user = new User(520, "U love me");
        map.put("user", user);
        return "result";
    }
}
@Controller
@SessionAttributes("user") 
public class Demo2Controller {
    @RequestMapping("demo2")
    public String demo2(User user){
       	System.out.println("UserId: " + user.getId());
        System.out.println("UserName: " + user.getDesc());
        return "result";
    }
}

结论:在方法参数上添加@SessionAttribute注解并没有什么实际意义,要想在当前控制器中获取Session中的属性值,必须在当前控制器类上添加@SessionAttributes注解才能生效。

测试3:

DemoController不变,将Demo2Controller类上将@SessionAttributes("user1")注解的value值改为user1,在demo2请求对应的jsp页面中能${sessionScope.user}属性值,不能通过${user}获取到对应的属性值,控制台打印:UserId: 0、UserName: null;

@Controller
@SessionAttributes("user1") 
public class Demo2Controller {
    @RequestMapping("demo2")
    public String demo2(User user){
       	System.out.println("UserId: " + user.getId());
        System.out.println("UserName: " + user.getDesc());
        return "result";
    }
}

结论:要想在当前控制器获取到预先存入到Session的属性值,@SessionAttributes注解的value值必须和预先存入的值相对应。

测试4:

Demo2Controller和测试3保持不变,将DemoController类上的@SessionAttribute(“user1”)注解上的value值改为user1,将map中put的key也改为user1,保证将key为user1的值存入到Session中,结果在demo2对应的jsp页面中,成功通过${sessionScope.user}${user}获取到了对应的属性值,控制台打印:UserId: 0、UserName: null;

@Controller
@SessionAttributes("user1")
public class DemoController {
    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        User user = new User(520, "U love me");
        map.put("user1", user);
        return "result";
    }
}
@Controller
@SessionAttributes("user1") 
public class Demo2Controller {
    @RequestMapping("demo2")
    public String demo2(User user){
       	System.out.println("UserId: " + user.getId());
        System.out.println("UserName: " + user.getDesc());
        return "result";
    }
}

结论:在demo2请求对应的jsp页面可以通过${sessionScope.user}${user}获取到了对应的属性值,说明@SessionAttributes注解已经成功接收到key为user1的属性值,并绑定到demo2请求的ModelMap中传入jsp页面,但是控制台并打印UserId: 0、UserName: null,说明demo2方法并没有接收到该属性值。

测试5:

在测试4的基础上,在Demo2Controller的参数添加@SessionAttribute(“user1”)注解,结果和测试3相同,并没有任何改变;

@Controller
@SessionAttributes("user1")
public class DemoController {
    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        User user = new User(520, "U love me");
        map.put("user1", user);
        return "result";
    }
}
@Controller
@SessionAttributes("user1") 
public class Demo2Controller {
    @RequestMapping("demo2")
    public String demo2(@SessionAttribute("user1") User user){
       	System.out.println("UserId: " + user.getId());
        System.out.println("UserName: " + user.getDesc());
        return "result";
    }
}

结论:@SessionAttribute真的一点作用都没有!!!

测试6:

将DemoController的@SessionAttributes(“user1”)注解value值改为value1,map.put(“user1”,user)的key也改为user1,将Demo2Controller类上的@SessionAttributes注解改为通过types属性接收,结果405报错,提示无法从session中获取key为user的属性值;

@Controller
@SessionAttributes("user1")
public class DemoController {
    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        User user = new User(520, "U love me");
        map.put("user1", user);
        return "result";
    }
}
@Controller
@SessionAttributes(types = User.class) 
public class Demo2Controller {
    @RequestMapping("demo2")
    public String demo2(User user){
       	System.out.println("UserId: " + user.getId());
        System.out.println("UserName: " + user.getDesc());
        return "result";
    }
}

结论:通过@SessionAttributes注解的types属性接收时,会根据类型的首字母小写对应的key去Session中获取对应的属性。

测试7:

修改DemoController类上@SessionAttributes(“user”)改回user,map.put(“user”,user),结果成功通过${sessionScope.user}${user}获取到了对应的属性值,控制台打印:UserId: 520,UserName: U love me;

@Controller
@SessionAttributes("user")
public class DemoController {
    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        User user = new User(520, "U love me");
        map.put("user", user);
        return "result";
    }
}

上面接收的类型为User类型,如果换为String类型,并且同时在Session中存入两个相同的类型的值,再根据types接收会怎么样呢,看下面测试8;

测试8:

DemoController存入两个String类型的key:abc和bcd的属性值到Session中,在DemoController2中通过@SessionAttributes()的types获取属性值,结果在demo2对应的jsp页面中可以通过${abc}${bcd}${sessionScope.abc}${sessionScope.bcd}获取到对应的属性值,控制台打印:abc: null,bcd: null;

@Controller
@SessionAttributes("abc,bdc")
public class DemoController {

    @RequestMapping("demo")
    public String demo(Map<String, Object> map){
        map.put("abc", "abcStr");
        map.put("bcd", "bcdStr");
        return "result";
    }
}
@Controller
@SessionAttributes(types = String.class)
public class Demo2Controller {
    @RequestMapping("demo2")
    public String demo2(String abc, String bcd) {
        System.out.println("abc: " + abc);
        System.out.println("bcd: " + bcd);
        return "result";
    }
}

结论:在Session中存在多个同类型属性,而@SessionAttributes根据types匹配时,在请求对应的jsp页面中可以获取到对应的属性值,而在当前方法中则获取不到,此时必须通过value去获取。

总结:

@SessionAttributes注解可以将当前Controller向jsp绑定的数据转存到Session中,如果在其它Controller中想获取到预先存入Session中的值,也要在该Controller的类上添加@SessionAttributes注解,并且保证存入和取出的value值相匹配,如果根据types从Session中获取值时,必须保证同类型只有一个属性值保存到Session中否则必须通过value去获取。

补充:在控制器的方法中,如果想从Session获取到自定义的key值对应的属性,可以使用@ModelAttribute进行获取。

3、控制器方法参数初始化顺序:【重点】

假设控制器中方法带有User user参数:(底层初始化顺序)

  1. 如果请求参数中带有User user,那么不论是否标有其它注解,都会使用请求中的User user值(即使有其它注解其值也会被请求中的参数值覆盖);
  2. 如果发现请求中没有User user参数,那么会检查该类上是否标有@SessionAttributes注解(scope为session),如果有会尝试去Session中获取;
  3. 如果发现类上没有@SessionAttributes属性,则会检查参数上是否有@ModelAttribute属性(scope为request),如果有则尝试从当前请求的model中获取;
  4. 如果都没有获取到值,则会初始化一个空的对象并存入到当前请求的ModelMap中。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你好像很好吃a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值