我编写了一个应用程序,它使用Spring Session在Sring MVC级别同时管理多个浏览器会话 . 但是,我不喜欢我的解决方案(它使用多个重定向),并希望找到改进它的方法 . 详情如下 .
我写了Spring Boot应用程序,它使用Spring Session 1.3.0,Spring MVC和Thymeleaf模板 . 我添加了它可能同时管理多个浏览器会话,即使在单独的选项卡中单个浏览器中打开也是如此 . 我从这里尝试了示例代码:https://github.com/spring-projects/spring-session/tree/1.3.x/samples/users并且它有效 . 但是我觉得除了转到servlet级别并编写大量自定义代码来管理会话之外,还有更好的方法可以做到这一点,就像在上面的链接中所做的那样:UserAccountsFilter implements javax.servlet.Filter
LogoutServlet extends javax.servlet.http.HttpServlet
LoginServlet extends javax.servlet.http.HttpServlet
这个想法是在阅读的结果:https://docs.spring.io/spring-session/docs/1.3.2.RELEASE/reference/html5/guides/users.html我想在Spring MVC级别使用它,这是我到目前为止所做的:
1)SessionConfig:配置内存会话存储和CookieHttpSessionStrategy
@Configuration
@EnableSpringHttpSession
public class SessionConfig
{
@Bean
public CookieHttpSessionStrategy sessionStragegy()
{
return new CookieHttpSessionStrategy();
}
@Bean
public MapSessionRepository sessionRepository()
{
return new MapSessionRepository();
}
}
2)当用户打开链接http://mysite/employee.htm/startEdit?id=0时,则创建新员工,否则如果id> 0,则加载现有员工进行编辑 .
EmployeeEditStartController的目的是在多会话模式下启动新会话并加载到employee中,以便EmployeeEditController将其用作模型对象 .
@Controller
@RequestMapping(EmployeeEditController.URL)
public class EmployeeEditStartController extends AbstractController
{
public static final String LOAD_USER = "loadUser";
@Autowired EmployeeEditService employeeEditService;
@Autowired CookieHttpSessionStrategy sessionManager;
private String getFreeAliasUrl(HttpServletRequest request)
{
String addAlias = sessionManager.getNewSessionAlias(request);
return sessionManager.encodeURL("", addAlias);
}
//startEdit used only for creating new session if there is no one, for example if cookies does not exist or if they are deleted
@GetMapping("startEdit")
public String startNewSession(@RequestParam(value = ID_PARAMETER, required = true) Long id, HttpServletRequest request)
{
request.getSession();
return REDIRECT_PREFIX + "startEdit1" + "?" + ID_PARAMETER + "=" + id;
}
//startEdit1 should be run after new session is created
@GetMapping("startEdit1")
public String startNewSession1(@RequestParam(value = ID_PARAMETER, required = true) Long id, HttpServletRequest request)
{
return REDIRECT_PREFIX + LOAD_USER + getFreeAliasUrl(request) + "&" + ID_PARAMETER + "=" + id;
}
//load user into EmployeeForm and add it to session
@GetMapping(LOAD_USER)
public String addCommandToSession(@RequestParam(value = ID_PARAMETER, required = true) Long id, HttpSession session)
{
session.setAttribute(EmployeeEditController.COMMAND, employeeEditService.getEmployeeForm(id));
return REDIRECT_PREFIX;
}
}
3)EmployeeEditController是添加/编辑员工主屏幕的控制器:
@Controller
@SessionAttributes(EmployeeEditController.COMMAND)
@RequestMapping(EmployeeEditController.URL)
public class EmployeeEditController extends AbstractController
{
static final String COMMAND = "employeeForm";
public static final String URL = "employee.htm";
public static final String REDIRECT_URL = REDIRECT_PREFIX + URL;
static final String VIEW = "employee/addEditEmployee/menu";
@GetMapping
public String get()
{
return VIEW;
}
@PostMapping(params = DISPATCH_PARAMETER + "=save")
public String saveEmployee(@Valid @ModelAttribute(COMMAND) EmployeeForm command, BindingResult bindingResult, SessionStatus status, HttpSession session)
{
//here we saving employee
//each new session will have new id, old session will be destroyed completely and removed
status.setComplete();
session.invalidate();
return REDIRECT_PREFIX + "/" + EmployeeViewController.MAIN_MENU_URL + "?_s=0";
}
}
4)AbstractController只包含一些控制器使用的常用常量:
public class AbstractController
{
public static final String DISPATCH_PARAMETER = "dispatch";
public static final String ID_PARAMETER = "id";
public static final String INDEX_PARAMETER = "index";
public static final String REDIRECT_PREFIX = "redirect:";
}
5)我有单独的控制器,用于更改员工姓名,设置部门等 . 我提供了其中一个(NameController)的示例,因为它显示了我如何管理其中的会话:
@Controller
@SessionAttributes({NameController.COMMAND, EmployeeEditController.COMMAND})
@RequestMapping(NameController.URL)
public class NameController extends AbstractController
{
static final String VIEW = "employee/addEditEmployee/name";
static final String COMMAND = "employeeName";
static final String URL = "name";
@Autowired private EmployeeEditService employeeEditService;
@GetMapping
public String get(ModelMap model, @ModelAttribute(EmployeeEditController.COMMAND) EmployeeForm employeeForm)
{
model.addAttribute(COMMAND, employeeEditService.getPersonName(employeeForm.getEmployee()));
return VIEW;
}
@PostMapping(params = DISPATCH_PARAMETER + "=ok")
public String ok(@Valid @ModelAttribute(COMMAND) PersonName command, BindingResult bindingResult, @ModelAttribute(EmployeeEditController.COMMAND) EmployeeForm employeeForm)
{
//saving name here and then redirect
return EmployeeEditController.REDIRECT_URL;
}
@PostMapping(params = DISPATCH_PARAMETER + "=cancel")
public String cancel()
{
return EmployeeEditController.REDIRECT_URL;
}
}
我的方法有效,但在EmployeeEditStartController中我有3个控制器方法,通过重定向在链中调用: startNewSession
, startNewSession1
和 addCommandToSession
. 最后的重定向加载 EmployeeEditController#get
. 感觉就像是超自然的解决方案 .
最后我的问题是:是否有可能摆脱方法 startNewSession1
, addCommandToSession
并使 startNewSession
方法中的所有工作都没有重定向(1重定向实际上将是:从 startNewSession
到 EmployeeEditController#get
)并使其在Spring MVC级别上工作(不是servlet级别)?或者理想情况下在 EmployeeEditController#get
中包含所有必需的代码而根本不需要任何重定向 .