1. 導(dǎo)入庫文件
implementation 'org.springframework.session:spring-session-data-redis
于是org.springframework.boot.autoconfigure.session.SessionAutoConfiguration
就生效了崭闲,
2 SessionAutoConfiguration
分析
里面有這個一段
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(SessionRepository.class)
@Import({ ServletSessionRepositoryImplementationValidator.class,
ServletSessionConfigurationImportSelector.class })
static class ServletSessionRepositoryConfiguration {
}
通過@import
引入了ServletSessionConfigurationImportSelector
泽艘,這個類繼承SessionConfigurationImportSelector
static class ServletSessionConfigurationImportSelector extends SessionConfigurationImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return super.selectImports(WebApplicationType.SERVLET);
}
}
最終執(zhí)行完后参滴,會在SessionStoreMappings
的MAPPINGS
中找到RedisSessionConfiguration
并倒入到容器中
static {
Map<StoreType, Configurations> mappings = new EnumMap<>(StoreType.class);
mappings.put(StoreType.REDIS,
new Configurations(RedisSessionConfiguration.class, RedisReactiveSessionConfiguration.class));
mappings.put(StoreType.MONGODB,
new Configurations(MongoSessionConfiguration.class, MongoReactiveSessionConfiguration.class));
mappings.put(StoreType.JDBC, new Configurations(JdbcSessionConfiguration.class, null));
mappings.put(StoreType.HAZELCAST, new Configurations(HazelcastSessionConfiguration.class, null));
mappings.put(StoreType.NONE,
new Configurations(NoOpSessionConfiguration.class, NoOpReactiveSessionConfiguration.class));
MAPPINGS = Collections.unmodifiableMap(mappings);
}
3. RedisSessionConfiguration
作用分析
在RedisSessionConfiguration
中有一個靜態(tài)內(nèi)部類SpringBootRedisHttpSessionConfiguration
繼承自RedisHttpSessionConfiguration
,RedisHttpSessionConfiguration
又繼承了SpringHttpSessionConfiguration
類饼煞,
兩個父類中注冊了很多組建域那,關(guān)鍵就是RedisIndexedSessionRepository
和SessionRepositoryFilter
類剥险。
@Configuration(proxyBeanMethods = false)
public static class SpringBootRedisHttpSessionConfiguration extends RedisHttpSessionConfiguration {
@Autowired
public void customize(SessionProperties sessionProperties, RedisSessionProperties redisSessionProperties,
ServerProperties serverProperties) {
Duration timeout = sessionProperties
.determineTimeout(() -> serverProperties.getServlet().getSession().getTimeout());
if (timeout != null) {
setMaxInactiveIntervalInSeconds((int) timeout.getSeconds());
}
setRedisNamespace(redisSessionProperties.getNamespace());
setFlushMode(redisSessionProperties.getFlushMode());
setSaveMode(redisSessionProperties.getSaveMode());
setCleanupCron(redisSessionProperties.getCleanupCron());
}
}
RedisHttpSessionConfiguration
中注冊RedisIndexedSessionRepository
組件
@Bean
public RedisIndexedSessionRepository sessionRepository() {
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
if (this.indexResolver != null) {
sessionRepository.setIndexResolver(this.indexResolver);
}
if (this.defaultRedisSerializer != null) {
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (StringUtils.hasText(this.redisNamespace)) {
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
}
sessionRepository.setFlushMode(this.flushMode);
sessionRepository.setSaveMode(this.saveMode);
int database = resolveDatabase();
sessionRepository.setDatabase(database);
this.sessionRepositoryCustomizers
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
return sessionRepository;
}
RedisIndexedSessionRepository
用來生成桦锄、保存session睬棚。
SpringHttpSessionConfiguration
中注冊SessionRepositoryFilter
SpringHttpSessionConfiguration
中注冊SessionRepositoryFilter
組件
@Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
return sessionRepositoryFilter;
}
在SessionRepositoryFilterConfiguration
類中,有這樣一段代碼
@Bean
FilterRegistrationBean<SessionRepositoryFilter<?>> sessionRepositoryFilterRegistration(
SessionProperties sessionProperties, SessionRepositoryFilter<?> filter) {
FilterRegistrationBean<SessionRepositoryFilter<?>> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(getDispatcherTypes(sessionProperties));
registration.setOrder(sessionProperties.getServlet().getFilterOrder());
return registration;
}
最終將SessionRepositoryFilter
注冊到servlet容器杜秸。這個SessionRepositoryFilter
類很關(guān)鍵放仗,它將HttpServletRequest
包裝成SessionRepositoryRequestWrapper,HttpServletResponse
包裝成SessionRepositoryResponseWrapper
,SessionRepositoryRequestWrapper
中public HttpSessionWrapper getSession(boolean create)
,以后調(diào)用getSession等方法亩歹,就被攔截了匙监,就會去RedisIndexedSessionRepository
中保存、生成session小作。
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
}