您的位置:

SessionScope全面解析

在Java web开发过程中,session是一个不可或缺的重要概念,用于在服务器和客户端之间保存用户状态。Spring框架提供了另一个有用的特性,即SessionScope。本文将深入介绍SessionScope,包括如何使用以及使用注意事项等。

一、从SessionScope判断

SessionScope是Spring Framework提供的一种作用域类型,它与HTTP Session相关,是一个在HTTP Session生命周期内存活的Bean作用域。

在一个HTTP Session中,我们可以存储和获取用户状态。但是,如果我们需要保留一些状态,并且在多个页面间分享,则需要使用SessionScope来管理Bean实例。

二、如何使用SessionScope

1、配置Bean作用域为SessionScope


import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class User {
    private String username;
    private String password;
    
    // constructors and getters/setters
}

2、获取SessionScope中的Bean实例

在SessionScope中,我们并不会像Singleton和Prototype那样通过自动注入或构造函数注入到Bean中。相反的,我们需要通过Spring的SessionScope Bean Factory来手动创建SessionScope Bean实例。

我们可以使用注释“@Autowired”将SessionScope Bean Factory注入到另一个Bean对象中,并使用它来手动创建SessionScope Bean实例。这里需要注意,不能将SessionScope Bean Factory注入到Singleton Bean中。

3、使用注释“@SessionAttributes”

除了手动创建和管理SessionScope Bean实例之外,我们还可以使用注释“@SessionAttributes”自动将Bean的属性添加到HTTP Session中,并在请求处理期间将其保留在HTTP Session中。

以下是一个用于演示的UserController类。


@RestController
@RequestMapping("/user")
@SessionAttributes("user")
public class UserController {
 
    @GetMapping("/{userId}")
    public User loadUser(@PathVariable String userId, Model model) {
        User user = userRepository.findOne(userId);
        model.addAttribute("user", user);
        return user;
    }
 
    @PutMapping("/{userId}")
    public User updateUser(@PathVariable String userId, @ModelAttribute("user") User user) {
        userRepository.save(user);
        return user;
    }

    // ...
}

在上述示例中,我们的User类被注释为@SessionAttributes("user")。在GET方法中,我们使用“Model”将User Bean添加到模型中,并将其添加到HTTP Session中。在PUT方法中,我们使用@ModelAttribute将User Bean注入到方法中,并使用userRepository将其保存到数据库中。

三、SessionScope和Session Attribute的区别

“Session Attribute”指的是将对象添加到HTTP Session中。Spring提供了@SessionAttributes注释,使我们可以在请求处理期间保留这些对象。

而SessionScope是一个非常范围特定的作用域类型。它管理在HTTP Session生命周期内存活的Bean实例。我们还可以手动在SessionScope Bean内部保存Session Attribute,但它们并不需要将这些属性添加到@SessionAttributes注释中声明的列表中。

四、访问SessionScope Bean实例中的属性

我们已经知道如何手动将Bean添加到HTTP Session中或使用@SessionAttributes注解。那么,如何在其他组件中访问SessionScope Bean实例中的属性呢?

下面是一个演示如何访问SessionScope Bean实例中的属性的例子。


@Controller
@RequestMapping("/user")
@SessionAttributes("user")
public class UserController {
 
    @GetMapping("/{userId}")
    public String loadUser(@PathVariable String userId, Model model) {
        User user = userRepository.findOne(userId);
        model.addAttribute("user", user);
        return "redirect:/user/home";
    }
 
    @GetMapping("/home")
    public String home(Model model) {
        User user = (User) model.asMap().get("user");
        System.out.println(user.getUsername() + " " + user.getPassword());
        return "home";
    }
 
    // ...
}

在上面的示例中,我们在“GET /{userId}”方法中手动将User添加到模型中,并将其添加到HTTP Session中。但是,在Model中添加User对象并不足以让我们在不同的请求处理方法之间共享它。为了让它在“GET /home”方法中可用,我们需要使用“@SessionAttributes”注释将其声明为Session属性。

在“GET /home”方法中,我们从模型中获取User对象并访问它的属性。

五、SessionScope和“@Autowired”

在SessionScope中,@Autowired将不会自动注入Session域中的Bean。


@Service
public class UserService {
 
    @Autowired
    private User user;
 
    public void printUsername() {
        System.out.println(user.getUsername());
    }
}

当我们运行上面的代码时会抛出一个异常“java.lang.NullPointerException: Cannot invoke 'xxx' because 'user' is null”。

当我们创建Session域Bean实例时,它并不会自动注入到其他组件中——即使它们具有与之匹配的类型和名称。相反,我们需要通过SessionScope Bean Factory手动获取SessionScope Bean实例,如下所示:


@Service
public class UserService {
 
    private User user;
 
    public void setUser(User user) {
        this.user = user;
    }
 
    public void printUsername() {
        System.out.println(user.getUsername());
    }
}
 
@RestController
@RequestMapping("/user")
@SessionAttributes("user")
public class UserController {
 
    private final UserService userService;// 先通过显式装配导入UserService,以便在当前会话中引用它。

    @Autowired // 装配 SessionScope Bean Factory
    public UserController(SessionScope sessionScope) {
        this.userService = sessionScope.getProxy(UserService.class);
    }
 
    @GetMapping("/{userId}")
    public String loadUser(@PathVariable String userId, Model model) {
        User user = userRepository.findOne(userId);
        model.addAttribute("user", user);
        userService.setUser(user);
        return "redirect:/user/home";
    }
 
    // ...
}

在上面的示例中,我们将SessionScope Bean Factory注入到UserController中。我们通过SessionScope Bean Factory手动获取UserService Bean,并在loadUser()方法中设置User对象。

六、SessionScope和Prototype

在SessionScope中,Prototype Bean实例与Session实例之间的关系与Singleton Bean实例不同。虽然SessionScope Bean实例只是特定HTTP Session的一个实例,但Prototype Bean实例不是实例化器的一部分,而是每次请求Spring Container时创建。

当我们将Prototype Bean注入到SessionScope Bean中时,单个Prototype Bean实例将在一次HTTP Session中被重复使用。


@Component
@Scope(value="prototype")
public class PrototypeBean {
    private String name;

    // constructors and getters/setters
}
 
@Component
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class SessionBean {
 
    @Autowired
    private PrototypeBean prototypeBean;
 
    public void printPrototypeName() {
        System.out.println(prototypeBean.getName());
    }
}

在上面的示例中,我们将PrototypeBean注入到SessionBean中。prototypeBean在SessionBean中循环注入。在一次HTTP Session中,SessionBean只会创建一个实例,但是PrototypeBean实例根据每个HTTP请求来创建。

这种情况下,我们建议使用“@Scope(value="request")”代替“@Scope(value="prototype")”,因为Session Bean的代理是在Session Bean源对象创建时创建的,因此不会受到非线程安全的影响。

七、使用Session and Session scope

虽然SessionScope与HTTP Session相关联,但是我们并不应该直接使用HTTP Session。相反,我们应该尽量避免在应用程序的各个层中使用HTTP Session,并使用更抽象的概念(例如Spring Security)来处理会话管理。

如果我们确实需要使用HTTP Session,请遵循以下准则:

  • 在适当的时候清除HTTP Session以释放资源和减少安全性风险。
  • 避免HTTP Session中保存太多状态,以免泄露敏感信息。
  • 避免在HTTP Session中保存太长时间的数据,因为它们可能会变得过时或无用。

八、使用注意事项

下面是一些使用SessionScope过程中注意事项:

  • 不能将SessionScope Bean注入到Singleton Bean中。
  • SessionScope Bean代理可能导致性能问题。
  • SessionScope Bean实例化不是线程安全的,必须小心处理。
  • 不能使用“@Autowired”将SessionScope Bean直接注入到其他Bean中。
  • Prototype Bean实例化与Session Scope Bean不同。

九、总结

在Java web开发中,SessionScope是一个重要的作用域类型。它允许我们在HTTP Session生命周期内管理Bean实例,并在多个请求处理方法之间共享它们。

本文中,我们介绍了如何配置SessionScope Bean,如何使用@SessionAttributes自动添加Bean到HTTP Session中,以及如何手动在HTTP Session中保存属性。此外,本文还讨论了SessionScope Bean和Prototype Bean的关系,以及使用SessionScope时应遵循的最佳实践。

最后,我们提醒您在使用SessionScope时需要小心处理,遵循HTTP Session最佳实践。