Spring-data-redis 的实现原理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring-data-redis 的实现原理相关的知识,希望对你有一定的参考价值。
参考技术A强烈建议阅读 Spring-session原理 的"2.spring-session重写servlet request 及 redis实现存储相关问题"
另外在写作中,适当参考了下面的文章。 它们的分析并不完全符合本文的场景,但都有值得参考之处 :
我们知道,引入spring-data-redis后,request.getSession()的行为将会从redis中寻找,为什么会这样呢?简单来说,这是因为SessionRepositoryFilter被添加到Servlet拦截链,将request和response替换了。
它是这样起作用的:
SessionRepositoryFilter是如何被生成、加入拦截链的呢?
参考 https://zhuanlan.zhihu.com/p/75776430 ,一切得从@EnableRedisHttpSession讲起:
@EnableRedisHttpSession直接引入了RedisHttpSessionConfiguration。
看下该类的定义:
上面两张图暴露两个信息:
跟着第一点的思路,我们看下父类SpringHttpSessionConfiguration。生成了SessionRepositoryFilter,我们看下父类:
可知,该方法会接受springboot自动注入的SessionRepository<S>为参数,并以此构建SessionRepositoryFilter。而回忆前面提到的,这个参数就是RedisHttpSessionConfiguration中提供的RedisOperationsSessionRepository。
这一章的总结了包含了RedisOperationsSessionRepository的SessionRepositoryFilter,是如何被生成为Bean的。我们之后会提到,这个Bean是如何加入拦截链的
首先,spring-boot-autoconfigure里的spring.factories里有提到SessionAutoConfiguration。springboot就会自动加载该类(不知道为什么会自动加载的话,建议先百度springboot自动加载原理)。
我们看下该类的定义:
其中的 @Import... 导入了SessionRepositoryFilterConfiguration。看下定义:
SessionRepositoryFilterConfiguration是一个 @Configuration ,我们分析下这个类:
在此处debug,会得到以下信息,供读者参考:
接下来我们要分析FilterRegistrationBean。
根据Servlet3.0规范 ,这种Bean会被自动调用onStartup方法。这个方法的行为是把内部的filter注册,而上文提到这里的filter就是SessionRepositoryFilter。
关于Servlet3.0,可参考 https://www.cnblogs.com/duanxz/archive/2012/10/25/2738173.html
之后,ServletRegistrationBean::onStartup到底是如何被调用的呢?它内部又是如何注册filter的呢?这就不是本文需要关注的了。 我们只需要知道,SessionRepositoryFilter在此会被加入拦截链,而它之后又会在拦截链中替换Request、Response,从而更改Session的行为是查看redis 。
Spring集成Redis方案(spring-data-redis)(基于Jedis的单机模式)(待实践)
说明:请注意Spring Data Redis的版本以及Spring的版本!最新版本的Spring Data Redis已经去除Jedis的依赖包,需要自行引入,这个是个坑点。并且会与一些低版本的Spring有冲突,要看官方文档和不断的测试。
继上一篇文章http://www.cnblogs.com/EasonJim/p/7625738.html中提到的几款客户端,它们基本都能和Spring集成。
下面介绍的是基于Spring原生的spring-data-redis去集成。
还要注意的是,Spring整合了Jedis框架进去,所以下面配置上还会基于Jedis去实现的,但是Spring在上层已经集成了很好用的工具类。
而整个框架基于Spring Data,里面集成了主流的应用,比如Redis,MongoDB等,而基于这些应用的一些非常好用的框架也集成了,比如Jedis这些。
我猜测,基于Spring Data Redis去集成的,底层还是用Jedis去实现,应该是可以实现客户端集群的,下次我再实践详细分析一下。
Spring Data Redis
项目结构:
POM:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.x.redis</groupId> <artifactId>Spring_redis</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Spring_redis</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>3.1.2.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.1.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.1</version> </dependency> <!-- 将现有的jakarta commons logging的调用转换成lsf4j的调用。 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.6.1</version> </dependency> <!-- Hack:确保commons-logging的jar包不被引入,否则将和jcl-over-slf4j冲突 --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> <scope>provided</scope> </dependency> <!-- slf4j的实现:logback,用来取代log4j。更快、更强! --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>0.9.24</version> <scope>runtime</scope> </dependency> </dependencies> </project>
applicationContext.xml:
context:property-placeholder标签用来导入properties文件。从而替换${redis.maxIdle}这样的变量。
context:component-scan是为了在com.x.redis.dao报下的类能够实用spring的注解注入的方式。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:redis.properties" /> <context:component-scan base-package="com.x.redis.dao"> </context:component-scan> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="${redis.maxIdle}" /> <property name="maxActive" value="${redis.maxActive}" /> <property name="maxWait" value="${redis.maxWait}" /> <property name="testOnBorrow" value="${redis.testOnBorrow}" /> </bean> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/> <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"> <property name="connectionFactory" ref="connectionFactory" /> </bean> <bean id="userDAO" class="com.x.redis.dao.impl.UserDAOImpl" /> </beans>
redis.properties:
# Redis settings
#redis.host=192.168.20.101
#redis.port=6380
#redis.pass=foobared
redis.host=127.0.0.1
redis.port=6379
redis.pass=
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true
UserDAOImpl:
Spring对DAO层的封装很多用了类似于下面代码的模板方式。
RedisTemplate就是Spring对Redis的一个封装。
public class UserDAOImpl implements UserDAO { @Autowired protected RedisTemplate<Serializable, Serializable> redisTemplate; public void saveUser(final User user) { redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { connection.set(redisTemplate.getStringSerializer().serialize("user.uid." + user.getId()), redisTemplate.getStringSerializer().serialize(user.getName())); return null; } }); } @Override public User getUser(final long id) { return redisTemplate.execute(new RedisCallback<User>() { @Override public User doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = redisTemplate.getStringSerializer().serialize("user.uid." + id); if (connection.exists(key)) { byte[] value = connection.get(key); String name = redisTemplate.getStringSerializer().deserialize(value); User user = new User(); user.setName(name); user.setId(id); return user; } return null; } }); } }
User:
public class User { private long id; private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
测试代码:
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/applicationContext.xml"); UserDAO userDAO = (UserDAO)ac.getBean("userDAO"); User user1 = new User(); user1.setId(1); user1.setName("obama"); userDAO.saveUser(user1); User user2 = userDAO.getUser(1); System.out.println(user2.getName()); }
参考:
http://www.cnblogs.com/tankaixiong/p/3660075.html(以上内容转自此篇文章)
http://blog.csdn.net/defonds/article/details/48716161
http://blog.csdn.net/albertfly/article/details/51494080
http://www.cnblogs.com/mrlinfeng/p/5857775.html
http://blog.csdn.net/fighterandknight/article/details/53432276/
http://www.cnblogs.com/cuglkb/p/6862609.html
http://snowolf.iteye.com/blog/1666908
http://www.cnblogs.com/wuxinliulei/p/5216712.html
http://blog.csdn.net/tomcat_2014/article/details/55260306
https://my.oschina.net/u/866380/blog/521658
以上是关于Spring-data-redis 的实现原理的主要内容,如果未能解决你的问题,请参考以下文章