日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring整合Redis详解

發(fā)布時(shí)間:2025/3/12 javascript 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring整合Redis详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

用注解驅(qū)動(dòng)的方式來使用 Redis。和數(shù)據(jù)庫事務(wù)一樣,Spring 提供了緩存的管理器和相關(guān)的注解來支持類似于 Redis 這樣的鍵值對(duì)緩存。

準(zhǔn)備測試環(huán)境

首先,定義一個(gè)簡單的角色 POJO,代碼如下所示。

package com.pojo; import java.io.Serializable; public class Role implements Serializable {private static final long serialVersionUID = 3447499459461375642L;private long id;private String roleName;private String note;// 省略setter和getter方法 }

注意:該類實(shí)現(xiàn)了 Serializable 接口,這說明這個(gè)類支持序列化,這樣就可以通過 Spring 的序列化器,將其保存為對(duì)應(yīng)的編碼,緩存到 Redis 中,也可以通過 Redis 讀回那些編碼,反序列化為對(duì)應(yīng)的 Java 對(duì)象。

接下來是關(guān)于 MyBatis 的開發(fā)環(huán)境,這樣我們就可以操作數(shù)據(jù)庫了。創(chuàng)建 RoleMapper.xml,代碼如下所示。

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.dao.RoleDao"><select id="getRole" resultType="com.pojo.Role">select id, role_name asroleName, note from t_role where id = #{id}</select><delete id="deleteRole">delete from t_role where id=#{id}</delete><insert id="insertRole" parameterType="com.pojo.Role"useGeneratedKeys="true" keyProperty="id">insert into t_role (role_name,note) values(#{roleName}, #{note})</insert><update id="updateRole" parameterType="com.pojo.Role">update t_role setrole_name = #{roleName}, note = #{note} where id = #{id}</update><select id="findRoles" resultType="com.pojo.Role">select id, role_name as roleName, note from t_role<where><if test="roleName != null">role_name like concat('%', #{roleName}, '%')</if><if test="note != null">note like concat ('%', #{note},'%')</if></where></select> </mapper>

然后,需要一個(gè) MyBatis 角色接口,以便使用這樣的一個(gè)映射文件,代碼如下所示。

package com.dao;import java.util.List; import org.apache.ibatis.annotations.Param; import com.pojo.Role;public interface RoleDao {public Role getRole(Long id);public int deleteRole(Long id);public int insertRole(Role role);public int updateRole(Role role);public List<Role> findRoles(@Param("roleName") String roleName, @Param("note") String note); }

注解 @Repository 表示它是一個(gè)持久層的接口。通過掃描和注解聯(lián)合定義 DAO 層,就完成了映射器方面的內(nèi)容。定義角色服務(wù)接口(RoleService),代碼如下所示,不過服務(wù)接口實(shí)現(xiàn)類會(huì)在后面談起,因?yàn)樗枰尤?Spring 緩存注解,以驅(qū)動(dòng)不同的行為。

package com.service;import java.util.List; import com.pojo.Role;public interface RoleService {public Role getRole(Long id);public int deleteRole(Long id);public Role insertRole(Role role);public int updateRole(Role role);public List<Role> findRoles(String roleName, String note); }

通過 Java 配置定義數(shù)據(jù)庫和相關(guān)的掃描內(nèi)容,代碼如下所示。

package com.config;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSourceFactory; import org.aspectj.apache.bcel.Repository; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer;@Configuration // 定義Spring掃描的包 @ComponentScan("com.*") // 使用事務(wù)驅(qū)動(dòng)管理器 @EnableTransactionManagement // 實(shí)現(xiàn)接口 TransactionManagementConfigurer,這樣可以配置注解驅(qū)動(dòng)事務(wù) public class RootConfig implements TransactionManagementConfigurer {private DataSource dataSource = null;/*** 配置數(shù)據(jù)庫** @return數(shù)據(jù)連接池*/@Bean(name = "dataSource")public DataSource initDataSource() {if (dataSource != null) {return dataSource;}Properties props = new Properties();props.setProperty("driverClassName", "com.mysql.jdbc.Driver");props.setProperty("url", "jdbc:mysql://localhost:3306/redis");props.setProperty("username", "root");props.setProperty("password", "1128");try {dataSource = BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return dataSource;}/*** 配置 SqlSessionFactoryBean** @return SqlSessionFactoryBean*/@Bean(name = "SqlSessionFactory")public SqlSessionFactoryBean initSqlSessionFactory() {SqlSessionFactoryBean SqlSessionFactory = new SqlSessionFactoryBean();SqlSessionFactory.setDataSource(initDataSource());// 配置MyBatis配置文件Resource resource = new ClassPathResource("mybatis/mybatis-config.xml");SqlSessionFactory.setConfigLocation(resource);return SqlSessionFactory;}/*** 通過自動(dòng)掃描,發(fā)現(xiàn)MyBatis Mapper接口** @return Mapper 掃描器*/@Beanpublic MapperScannerConfigurer initMapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();// 掃描包msc.setBasePackage("com.*");msc.setSqlSessionFactoryBeanName("SqlSessionFactory");// 區(qū)分注解掃描msc.setAnnotationClass(Repository.class);return msc;}/*** 實(shí)現(xiàn)接口方法,注冊注解事務(wù),當(dāng)@Transactional使用的時(shí)候產(chǎn)生數(shù)據(jù)庫事務(wù)*/@Override@Bean(name = "annotationDrivenTransactionManager")public PlatformTransactionManager annotationDrivenTransactionManager() {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(initDataSource());return transactionManager;} }

在 SqlSessionFactoryBean 的定義中引入了關(guān)于 MyBatis 的一個(gè)配置文件——mybatis-config.xml,它放在源碼的 mybatis 目錄之下,它的作用是引入 RoleMapper.xml,我放在目錄 com/dao 下,代碼如下所示。

<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration><mappers><mapper resource="com/dao/RoleMapper.xml" /></mappers> </configuration>

這樣測試只要一個(gè) RoleService 實(shí)現(xiàn)類就可以了,這個(gè)類的實(shí)現(xiàn)就是我們后面所需要討論的主要的內(nèi)容,不過在此之前要先了解 Spring 的緩存管理器。

Spring 的緩存管理器

在 Spring 項(xiàng)目中它提供了接口 CacheManager 來定義緩存管理器,這樣各個(gè)不同的緩存就可以實(shí)現(xiàn)它來提供管理器的功能了,而在 spring-data-redis.jar 包中實(shí)現(xiàn) CacheManager 接口的則是 RedisCacheManager,因此要定義 RedisCacheManager 的 Bean,不過在此之前要先定義 RedisTemplate。

下面使用注解驅(qū)動(dòng) RedisCacheManager 定義,代碼如下所示。

package com.config;import java.util.ArrayList; import java.util.List;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;import redis.clients.jedis.JedisPoolConfig;@Configuration @EnableCaching public class RedisConfig {@Bean(name = "redisTemplate")public RedisTemplate initRedisTemplate() {JedisPoolConfig poolConfig = new JedisPoolConfig();// 最大空閑數(shù)poolConfig.setMaxIdle(50);// 最大連接數(shù)poolConfig.setMaxTotal(100);// 最大等待亳秒數(shù)poolConfig.setMaxWaitMillis(20000);// 創(chuàng)建Jedis連接工廠JedisConnectionFactory connectionFactory = JedisConnectionFactory(poolConfig);connectionFactory.setHostName("localhost");connectionFactory.setPort(6379);// 調(diào)用后初始化方法,沒有它將拋出異常connectionFactory.afterPropertiesSet();// 自定Redis序列化器RedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();RedisSerializer stringRedisSerializer = new StringRedisSerializer();// 定義RedisTemplate,并設(shè)置連接工程RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(connectionFactory);// 設(shè)置序列化器redisTemplate.setDefaultSerializer(stringRedisSerializer);redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(jdkSerializationRedisSerializer);return redisTemplate;}@Bean(name = "redisCacheManager")public CacheManager initRedisCacheManager(@Autowired RedisTemplate redisTempate) {RedisCacheManager cacheManager = new RedisCacheManager(redisTempate);// 設(shè)置超時(shí)時(shí)間為10分鐘,單位為秒cacheManager.setDefaultExpiration(600);// 設(shè)置緩存名稱List<String> cacheNames = new ArrayList<String>();cacheNames.add("redisCacheManager");cacheManager.setCacheNames(cacheNames);return cacheManager;} }

@EnableCaching 表示 Spring IoC 容器啟動(dòng)了緩存機(jī)制。對(duì)于 RedisTemplate 的定義實(shí)例和 XML 的方式差不多。注意,在創(chuàng)建 Jedis 連接工廠(JedisConnectionFactory)后,要自己調(diào)用其 afterPropertiesSet 方法,因?yàn)檫@里不是單獨(dú)自定義一個(gè) Spring Bean,而是在 XML 方式中是單獨(dú)自定義的。

這個(gè)類實(shí)現(xiàn)了 InitializingBean 接口,按照 Spring Bean 的生命周期,我們知道它會(huì)被 Spring IoC 容器自己調(diào)用,而這里的注解方式?jīng)]有定義 SpringBean,因此需要自己調(diào)用。

字符串定義了 key(包括 hash 數(shù)據(jù)結(jié)構(gòu)),而值則使用了序列化,這樣就能夠保存 Java 對(duì)象了。

緩存管理器 RedisCacheManager 定義了默認(rèn)的超時(shí)時(shí)間為 10 分鐘,這樣就可以在一定的時(shí)間間隔后重新從數(shù)據(jù)庫中讀取數(shù)據(jù)了,而名稱則定義為 redisCacheManager,名稱是為了方便后面注解引用的。

這里只定義 RedisCacheManager,使用 XML 定義緩存管理器代碼如下所示。

<?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:cache="http://www.springframework.org/schema/cache"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cachehttp://www.springframework.org/schema/cache/spring-cache.5.0.xsd"><!--使用注解驅(qū)動(dòng),其中屬性cache-manager默認(rèn)值為cacheManager, 所以如果你的緩存管理器名稱也是cacheManager則無需重新定義 --><cache:annotation-driven cache-manager="redisCacheManager" /><!-- 定義緩存管理器,如果你使用id="cacheManager",則驅(qū)動(dòng)不需要顯式配置cache-manager屬性 --><bean id="redisCacheManager"class="org.springframework.data.redis.cache.RedisCacheManager"><!--通過構(gòu)造方法注入RedisTemplate --><constructor-arg index="0" ref="redisTemplate" /><!-- 定義默認(rèn)超時(shí)時(shí)間,單位秒 --><property name="defaultExpiration" value="600" /><!--緩存管理器名稱 --><property name="cacheNames"><list><value>redisCacheManager</value></list></property></bean> </beans>

這樣也可以配置好對(duì)應(yīng)的緩存管理器。

緩存注解簡介

配置了緩存管理器之后,Spring 就允許用注解的方式使用緩存了,這里的注解有 4 個(gè)。XML 也可以使用它們,但是用得不多,我們就不再介紹了,還是以注解為主。首先簡介一下緩存注解,如下表所示。

緩存注解

注 解描 述
@Cacheable表明在進(jìn)入方法之前,Spring 會(huì)先去緩存服務(wù)器中査找對(duì)應(yīng) key 的緩存值,如果找到緩存值,那么 Spring 將不會(huì)再調(diào)用方法,而是將緩存值讀出,返回給調(diào)用者;如果沒有找到緩存值,那么 Spring 就會(huì)執(zhí)行你的方法,將最后的結(jié)果通過 key 保存到緩存服務(wù)器中
@CachePutSpring 會(huì)將該方法返回的值緩存到緩存服務(wù)器中,這里需要注意的是,Spring 不會(huì)事先去緩存服務(wù)器中查找,而是直接執(zhí)行方法,然后緩存。換句話說,該方法始終會(huì)被 Spring 所調(diào)用
@CacheEvict移除緩存對(duì)應(yīng)的 key 的值
@Caching這是個(gè)分組注解,它能夠同時(shí)應(yīng)用于其他緩存的注解

注解 @Cacheable 和 @CachePut 都可以保存緩存鍵值對(duì),只是它們的方式略有不同,請(qǐng)注意二者的區(qū)別,它們只能運(yùn)用于有返回值的方法中,而刪除緩存 key 的 @CacheEvict 則可以用在 void 的方法上,因?yàn)樗⒉恍枰ケ4嫒魏沃怠?/p>

上述注解都能標(biāo)注到類或者方法之上,如果放到類上,則對(duì)所有的方法都有效;如果放到方法上,則只是對(duì)方法有效。在大部分情況下,會(huì)放置到方法上。因?yàn)?@Cacheable 和 @CachePut 可以配置的屬性接近,所以把它們歸為一類去介紹,而 @Caching 不常用。

一般而言,對(duì)于查詢,我們會(huì)考慮使用 @Cacheable;對(duì)于插入和修改,我們會(huì)考慮使用 @CachePut;對(duì)于刪除操作,我們會(huì)考慮使用 @CacheEvict。

注解@Cacheable和@CachePut

因?yàn)?@Cacheable 和 @CachePut 兩個(gè)注解的配置項(xiàng)比較接近,所以這里就將這兩個(gè)注解一并介紹了,它們的屬性,如下表所示。

@Cacheable 和 @CachePut 配置屬性

其中,因?yàn)?value 和 key 這兩個(gè)屬性使用得最多,所以先來討論這兩個(gè)屬性。value 是一個(gè)數(shù)組,可以引用多個(gè)緩存管理器。

比如使用注解驅(qū)動(dòng) RedisCacheManager 定義代碼中所定義的 RedisCacheManager,就可以引用它了,而對(duì)于 key 則是緩存中的鍵,它支持 Spring 表達(dá)式,通過 Spring 表達(dá)式就可以自定義緩存的 key。編寫剩下的 RoleService 接口的實(shí)現(xiàn)類——RoleServiceImpl 的方法。

先了解一些 Spring 表達(dá)式和緩存注解之間的約定,通過這些約定去引用方法的參數(shù)和返回值的內(nèi)容,使得其注入 key 所定義的 Spring 表達(dá)式的結(jié)果中,表達(dá)式值的引用如下表所示。

表達(dá)式值的引用

這樣就方便使用對(duì)應(yīng)的參數(shù)或者返回值作為緩存的 key 了。

RoleService 接口的實(shí)現(xiàn)類——RoleServiceImpl,它有 3 個(gè)方法,使用這個(gè)緩存可以啟動(dòng)緩存管理器來保存數(shù)據(jù),代碼如下所示。

package com.service;import java.util.List;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional;import com.dao.RoleDao; import com.pojo.Role;@Service public class RoleServiceImpl implements RoleService { //角色DAO,方便執(zhí)行SQL@Autowiredprivate RoleDao roleDao = null;/*** 使用@Cacheable定義緩存策略 當(dāng)緩存中有值,則返回緩存數(shù)據(jù),否則訪問方法得到數(shù)據(jù) 通過value引用緩存管理器,通過key定義鍵** @param id角色編號(hào)* @return 角色*/@Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)@Cacheable(value = "redisCacheManager", key = "'redis_role_'+#id")public Role getRole(Long id) {return roleDao.getRole(id);}/*** 使用@CachePut則表示無論如何都會(huì)執(zhí)行方法,最后將方法的返回值再保存到緩存中 使用在插入數(shù)據(jù)的地方,則表示保存到數(shù)據(jù)庫后,會(huì)同期插入Redis緩存中** @param role角色對(duì)象 @return角色對(duì)象(會(huì)回填主鍵)*/@Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)@CachePut(value = "redisCacheManager", key = "'redis_role_'+#resuIt.id")public Role insertRole(Role role) {roleDao.insertRole(role);return role;}/*** 使用@CachePut,表示更新數(shù)據(jù)庫數(shù)據(jù)的同時(shí),也會(huì)同步更新緩存** @param role角色對(duì)象* @return影響條數(shù)*/@Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)@CachePut(value = "redisCacheManager", key = "'redis_role_' +#role. id")public Role updateRole(Role role) {roleDao.updateRole(role);return role;}...... }

因?yàn)?getRole 方法是一個(gè)查詢方法,所以使用 @Cacheable 注解,這樣在 Spring 的調(diào)用中,它就會(huì)先查詢 Redis,看看是否存在對(duì)應(yīng)的值,那么采用什么 key 去查詢呢?

注解中的 key 屬性,它配置的是 ‘redis_role_’+#id,這樣 Spring EL 就會(huì)計(jì)算返回一個(gè) key,比如參數(shù) id 為 1L,其 key 計(jì)算結(jié)果就為 redis_role_1。以一個(gè) key 去訪問 Redis,如果有返回值,則不再執(zhí)行方法,如果沒有則訪問方法,返回角色信息,然后通過 key 去保存數(shù)據(jù)到 Redis 中。

先執(zhí)行 insertRole 方法才能把對(duì)應(yīng)的信息保存到 Redis 中,所以采用的是注解 @CachePut。由于主鍵是由數(shù)據(jù)庫生成,所以無法從參數(shù)中讀取,但是可以從結(jié)果中讀取,那么 #result.id 的寫法就會(huì)返回方法返回的角色 id。而這個(gè)角色 id 是通過數(shù)據(jù)庫生成,然后由 MyBatis 進(jìn)行回填得到的,這樣就可以在 Redis 中新增一個(gè) key,然后保存對(duì)應(yīng)的對(duì)象了。

對(duì)于 updateRole 方法而言,采用的是注解 @CachePut,由于對(duì)象有所更新,所以要在方法之后更新 Redis 的數(shù)據(jù),以保證數(shù)據(jù)的一致性。這里直接讀取參數(shù)的 id,所以表達(dá)式寫為 #role.id,這樣就可以引入角色參數(shù)的 id 了。在方法結(jié)束后,它就會(huì)去更新 Redis 對(duì)應(yīng)的 key 的值了。

為此可以提供一個(gè) log4j.properties 文件來監(jiān)控整個(gè)過程:

# Global logging configuration log4j.rootLogger=DEBUG,stdout # MyBatis logging configuration... log4j.logger.com.mybatis=DEBUG # Console output... log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

然后通過以下代碼來測試緩存注解。

// 使用注解Spring IoC容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(RootConfig.class, RedisConfig.class); // 獲取角色服務(wù)類 RoleService roleService = ctx.getBean(RoleService.class); Role role = new Role(); role.setRoleName("role_name_1"); role.setNote("role_note_1"); // 插入角色 roleService.insertRole(role); // 獲取角色 Role getRole = roleService.getRole(role.getId()); getRole.setNote("role_note_1_update"); // 更新角色 roleService.updateRole(getRole);

將關(guān)于數(shù)據(jù)庫和 Redis 的相關(guān)配置通過注解 Spring IoC 容器加載進(jìn)來,這樣就可以用 Spring 操作這些資源了,然后執(zhí)行插入、獲取、更新角色的方法。

總結(jié)

以上是生活随笔為你收集整理的Spring整合Redis详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。