实现分布式会话的常用模式,是把会话信息集中保存在Redis服务器中,业务服务器实现为负状态模式,方便会话的一致性。利用Spring中的request bean,可以非常优雅地实现会话信息的自动管理。
import com.google.common.base.Strings;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import redis.clients.jedis.JedisCommands;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 基于Redis的懒加载会话信息(没有与Redis实时保持统统不).
*
* @author tenmao
*/
@Slf4j
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Getter
@Component
public class LazyRedisSession {
private static final String PREFIX = "com.tenmao.redis.";
@Resource
private JedisCommands jedisCommands;
@Resource
private HttpServletRequest request;
private String sessionId;
private Map<String, String> attributes = new ConcurrentHashMap<>();
@PostConstruct
private void init() {
//这里是从HttpServletRequest中获取会话ID,实际项目中,也可以从ThreadLocal或者MDC中获取
sessionId = request.getHeader("Session-Id");
log.info("redis session loaded for session [{}]", sessionId);
if (!Strings.isNullOrEmpty(sessionId)) {
final Map<String, String> attributesInRedis = jedisCommands.hgetAll(PREFIX + sessionId);
attributes.putAll(attributesInRedis);
}
}
@PreDestroy
private void close() {
log.info("redis session saved to redis session [{}]", sessionId);
if (!Strings.isNullOrEmpty(sessionId)) {
jedisCommands.del(PREFIX + sessionId);
if (!attributes.isEmpty()) {
jedisCommands.hmset(PREFIX + sessionId, attributes);
}
}
}
/**
* Binds an object to this session, using the name specified.
* If an object of the same name is already bound to the session,
* the object is replaced.
*
* @param name the name to which the object is bound;
* cannot be null
* @param value the string to be bound
*/
public void setAttribute(String name, String value) {
attributes.put(name, value);
}
/**
* Returns the object bound with the specified name in this session, or
* <code>null</code> if no object is bound under the name.
*
* @param name a string specifying the name of the object
* @return the string with the specified name
*/
public String getAttribute(String name) {
return attributes.get(name);
}
}
使用方式特别简单,就把
LazeRedisSession
当做普通的bean注入到其他Bean中就可以了
@RestController
@RequestMapping("test")
public class TestController {
@Resource
private LazyRedisSession lazyRedisSession;
@RequestMapping(value = "/ping", method = RequestMethod.GET)
public String ping(@RequestParam String name) {
final String lastName = lazyRedisSession.getAttribute("name");
lazyRedisSession.setAttribute("name", name);
return lastName;
}
}
ps: 这个方法在实践中特别好用,如果你有其他的方法,还请不吝赐教