6. Spring Session 源碼分析-配置初始化

Spring Session 官方文檔的描述是非常簡單的, 很多東西講的並不是很清楚, 也可能是因爲Spring 官方覺得Spring session太簡單, 沒什麼值得多介紹吧. 目前網上關於Spring session的教程並沒有太多, 所以筆者決定嘗試從源碼角度對spring session 的核心步驟做一下簡單的分析.

1. Spring Session 配置初始化分析

1.1 @EnableRedisHttpSession 分析

SpringBoot 整合大部分框架都是通過@Enablexxx作爲框架的入口的, 因此筆者也嘗試從@EnableRedisHttpSession 配置類開始分析:

  • @EnableRedisHttpSession 註解定義時, 使用@Configuration 修飾, 說明是@EnableRedisHttpSession修飾的類, 會被Spring 當做配置類進行解析
  • @EnableRedisHttpSession 中使用@Import 註解導入了RedisHttpSessionConfiguration, 那麼在應用啓動時會向spring 容器中注入RedisHttpSessionConfiguration 實例

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(RedisHttpSessionConfiguration.class)
@Configuration
public @interface EnableRedisHttpSession {

	int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;

	String redisNamespace() default RedisOperationsSessionRepository.DEFAULT_NAMESPACE;

	RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;

	String cleanupCron() default RedisHttpSessionConfiguration.DEFAULT_CLEANUP_CRON;

}

1.2 RedisHttpSessionConfiguration 分析

  • RedisHttpSessionConfiguration 本身是一個Spring 配置類, 會向spring 容器註冊sessionRepository, redisMessageListenerContainer 等實例
  • RedisHttpSessionConfiguration 會註冊Redis 消息監聽器容器RedisMessageListenerContainer, 並將RedisOperationsSessionRepository 作爲Redis 消息訂閱的監聽器, 因爲它實現了MessageListener接口. 當Redis 中key 過期或銷燬時, 會通知將RedisOperationsSessionRepository 調用其onMessage 方法來處理消息.
  • RedisHttpSessionConfiguration 繼承了RedisHttpSessionConfiguration, 所以會繼承RedisHttpSessionConfiguration中的公有方法
@Configuration
@EnableScheduling
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
		implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,
		SchedulingConfigurer {
	
	// 省略部分代碼....	
		
	// 初始化Spring session 配置信息, 即解析@EnableRedisHttpSession 註解的參數配置信息
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		Map<String, Object> attributeMap = importMetadata
				.getAnnotationAttributes(EnableRedisHttpSession.class.getName());
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(attributeMap);
		this.maxInactiveIntervalInSeconds = attributes
				.getNumber("maxInactiveIntervalInSeconds");
		String redisNamespaceValue = attributes.getString("redisNamespace");
		if (StringUtils.hasText(redisNamespaceValue)) {
			this.redisNamespace = this.embeddedValueResolver
					.resolveStringValue(redisNamespaceValue);
		}
		this.redisFlushMode = attributes.getEnum("redisFlushMode");
		String cleanupCron = attributes.getString("cleanupCron");
		if (StringUtils.hasText(cleanupCron)) {
			this.cleanupCron = cleanupCron;
		}
	}	
		
	// 利用Redis 發佈訂閱機制, 訂閱消息的	
	@Bean
	public RedisMessageListenerContainer redisMessageListenerContainer() {
		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
		container.setConnectionFactory(this.redisConnectionFactory);
		if (this.redisTaskExecutor != null) {
			container.setTaskExecutor(this.redisTaskExecutor);
		}
		if (this.redisSubscriptionExecutor != null) {
			container.setSubscriptionExecutor(this.redisSubscriptionExecutor);
		}
		// 訂閱監聽消息銷燬/過期
		container.addMessageListener(sessionRepository(), Arrays.asList(
				new ChannelTopic(sessionRepository().getSessionDeletedChannel()),
				new ChannelTopic(sessionRepository().getSessionExpiredChannel())));
		// 訂閱消息創建
		container.addMessageListener(sessionRepository(),
				Collections.singletonList(new PatternTopic(
						sessionRepository().getSessionCreatedChannelPrefix() + "*")));
		return container;
	}
		
}

1.3 RedisHttpSessionConfiguration 分析

  • SpringHttpSessionConfiguration 會將自定義的所有SessionListener 封裝爲一個SessionEventHttpSessionListenerAdapter
  • SpringHttpSessionConfiguration 會初始化一個最核心的組件SessionRepositoryFilter, 該過濾器會攔截所有的http 請求, 解析並處理session.
@Configuration
public class SpringHttpSessionConfiguration implements ApplicationContextAware {

    // 省略部分代碼

    // 註冊SessionListener
    @Bean
	public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() {
		return new SessionEventHttpSessionListenerAdapter(this.httpSessionListeners);
	}

    // 向spring 容器注入核心的過濾器
    @Bean
    public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
    		SessionRepository<S> sessionRepository) {
    	SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(
    			sessionRepository);
    	sessionRepositoryFilter.setServletContext(this.servletContext);
    	sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
    	return sessionRepositoryFilter;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章