首页 > 数据库 >Caching with Spring Data Redis

Caching with Spring Data Redis

时间:2023-07-24 20:02:10浏览次数:37  
标签:redis Spring cache Redis springframework org Caching import example

by  Joshua White  on  January 25, 2013  in  JAVA

In the example below, I’ll show you how to use the Spring Data – Redis project as a caching provider for the Spring Cache Abstraction that was introduced in Spring 3.1. I get a lot of questions about how to use Spring’s Java based configuration so I’ll provide both XML and Java based configurations for your review.


Dependencies

The following dependencies were used in this example:



<? xml version = "1.0" encoding = "UTF-8" ?>
< project xmlns = "http://maven.apache.org/POM/4.0.0"
     xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
     xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" >
     < modelVersion >4.0.0</ modelVersion >
     < groupId >com.joshuawhite.example</ groupId >
     < artifactId >spring-redis-example</ artifactId >
     < version >1.0</ version >
     < packaging >jar</ packaging >
     < name >Spring Redis Example</ name >
     < dependencies >
         < dependency >
             < groupId >org.springframework.data</ groupId >
             < artifactId >spring-data-redis</ artifactId >
             < version >1.0.2.RELEASE</ version >
         </ dependency >       
         <!-- required for @Configuration annotation -->
         < dependency >
             < groupId >cglib</ groupId >
             < artifactId >cglib</ artifactId >
             < version >2.2.2</ version >
         </ dependency >
         < dependency >
             < groupId >redis.clients</ groupId >
             < artifactId >jedis</ artifactId >
             < version >2.0.0</ version >
             < type >jar</ type >
             < scope >compile</ scope >
         </ dependency >
         < dependency >
             < groupId >log4j</ groupId >
             < artifactId >log4j</ artifactId >
             < version >1.2.14</ version >
         </ dependency >
     </ dependencies >
     < build >
         < plugins >
             < plugin >
                 < groupId >org.apache.maven.plugins</ groupId >
                 < artifactId >maven-compiler-plugin</ artifactId >
                 < configuration >
                     < source >1.6</ source >
                     < target >1.6</ target >
                 </ configuration >
             </ plugin >
         </ plugins >
     </ build >
</ project >

Code and Configuration

The HelloService example below is very simple. As you will see in the implementation, it simply returns a String with “Hello” prepended to the name that is passed in.



package com.joshuawhite.example.service;
 
public interface HelloService {
 
     String getMessage(String name);
 
}

Looking at the HelloServiceImpl class (below), you can see that I am leveraging Spring’s @Cacheable annotation to add caching capabilities to the getMessage method. For more details on the capabilities of this annotation, take a look at theCache Abstraction documentation.  For fun, I am using the Spring Expression Language (SpEL) to define a condition. In this example, the methods response will only be cached when the name passed in is “Joshua”.



package com.joshuawhite.example.service;
 
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
 
@Service ( "helloService" )
public class HelloServiceImpl implements HelloService {
 
     /**
      * Using SpEL for conditional caching - only cache method executions when
      * the name is equal to "Joshua"
      */
     @Cacheable (value= "messageCache" , condition= "'Joshua'.equals(#name)" )
     public String getMessage(String name) {
         System.out.println( "Executing HelloServiceImpl" +
                         ".getHelloMessage(\"" + name + "\")" );
 
         return "Hello " + name + "!" ;
     }
 
}

The App class below contains our main method and is used to select between XML and Java based configurations. Each of the System.out.println‘s are used to demonstrate when caching is taking place. As a reminder, we only expect method executions passing in “Joshua” to be cached. This will be more clear when we look at the programs output later.



package com.joshuawhite.example;
 
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
 
import com.joshuawhite.example.config.AppConfig;
import com.joshuawhite.example.service.HelloService;
 
public class App {
 
     public static void main(String[] args) {
 
         boolean useJavaConfig  = true ;
         ApplicationContext ctx = null ;
 
         //Showing examples of both Xml and Java based configuration
         if (useJavaConfig ) {
                 ctx = new AnnotationConfigApplicationContext(AppConfig. class );
         }
         else {
                 ctx = new GenericXmlApplicationContext( "/META-INF/spring/app-context.xml" );
         }
 
         HelloService helloService = ctx.getBean( "helloService" , HelloService. class );
 
         //First method execution using key="Josh", not cached
         System.out.println( "message: " + helloService.getMessage( "Josh" ));
 
         //Second method execution using key="Josh", still not cached
         System.out.println( "message: " + helloService.getMessage( "Josh" ));
 
         //First method execution using key="Joshua", not cached
         System.out.println( "message: " + helloService.getMessage( "Joshua" ));
 
         //Second method execution using key="Joshua", cached
         System.out.println( "message: " + helloService.getMessage( "Joshua" ));
 
         System.out.println( "Done." );
     }
 
}

Notice that component scanning is still used when using the XML based configuration. You can see that I am using the@Service annotation on line 6 of HelloServiceImpl.java above.


Next we will take a look at how to configure a jedisConnectionFactory, redisTemplate and cacheManager.


Configuring the JedisConnectionFactory

For this example, I chose to use Jedis as our Java client of choice because it is listed on the Redis site as being the“recommended” client library for Java. As you can see, the setup is very straight forward. While I am explicitly setting use-pool=true, it the source code indicates that this is the default. The JedisConnectionFactory also provides the following defaults when not explicitly set:


hostName=”localhost”

port=6379

timeout=2000 ms

database=0

usePool=true

Note: Though the database index is configurable, the  JedisConnectionFactory only supports connecting to one Redis database at a time. Because Redis is single threaded, you are encouraged to set up multiple instances of Redis instead of using multiple databases within a single process. This allows you to get better CPU/resource utilization. If you plan to use redis-cluster, only a single database is supported.

For more information about the defaults used in the connection pool, take a look at the implementation of JedisPoolConfigor the Apache Commons Pool org.apache.commons.pool.impl.GenericObjectPool.Config and it’s enclosingorg.apache.commons.pool.impl.GenericObjectPool class.


Configuring the RedisTemplate

As you would expect from a Spring “template” class, the RedisTemplate takes care of serialization and connection management and (providing you are using a connection pool) is thread safe.


By default, the RedisTemplate uses Java serialization (JdkSerializationRedisSerializer). Note that serializing data into Redis essentially makes Redis an “opaque” cache. While other serializers allow you to map the data into Redis, I have found serialization, especially when dealing with object graphs, is faster and simpler to use. That being said, if you have a requirement that other non-java applications be able to access this data, mapping is your best out-of-the-box option.


I have had a great experience using Hessian and Google Protocol Buffers/protostuff. I’ll share some sample implementations of the RedisSerializer in a future post.


Configuring the RedisCacheManager

Configuring the RedisCacheManager is straight forward. As a reminder, the RedisCacheManager is dependent on aRedisTemplate which is dependent on a connection factory, in our case JedisConnectionFactory, that can only connect to a single database at a time.


As a workaround, the RedisCacheManager has the capability of setting up a prefix for your cache keys.


Warning: When dealing with other caching solutions, Spring’s CacheManger usually contains a map of  Cache(each implementing map like functionality) implementations that are backed by separate caches. Using the default RedisCacheManager configuration, this is not the case. Based on the javadoc comment on the  RedisCacheManager, its not clear if this is a bug or simply incomplete documentation.


“…By default saves the keys by appending a prefix (which acts as a namespace).”


While the DefaultRedisCachePrefix which is configured in the RedisCacheManager certainly supports this, it is not enabled by default. As a result, when you ask the RedisCacheManager for a Cache of a given name, it simply creates a new Cache instance that points to the same database. As a result, the Cache instances are all the same. The same key will retrieve the same value in all Cache instances.


As the javadoc comment alludes to, prefixs can be used to setup client managed (Redis doesn’t support this functionality natively) namespaces that essentially create “virtual” caches within the same database. You can turn this feature on by calling redisCacheManager.setUsePrefix(true) either using the Spring XML or Java configuration.




<? 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:context = "http://www.springframework.org/schema/context"
     xmlns:c = "http://www.springframework.org/schema/c"
     xmlns:p = "http://www.springframework.org/schema/p"
     xmlns:cache = "http://www.springframework.org/schema/cache"
     xsi:schemaLocation="
 
http://www.springframework.org/schema/beansvhttp://www.springframework.org/schema/beans/spring-beans.xsd
 
         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
 
     < context:component-scan base-package = "com.joshuawhite.example.service" />
     < context:property-placeholder location = "classpath:/redis.properties" />
 
     <!-- turn on declarative caching -->
     < cache:annotation-driven />
 
     <!-- Jedis ConnectionFactory -->
     < bean
         id = "jedisConnectionFactory"
         class = "org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
         p:host-name = "${redis.host-name}"
         p:port = "${redis.port}"
         p:use-pool = "true" />
 
     <!-- redis template definition -->
     < bean
         id = "redisTemplate"
         class = "org.springframework.data.redis.core.RedisTemplate"
         p:connection-factory-ref = "jedisConnectionFactory" />
 
     <!-- declare Redis Cache Manager -->
     < bean
         id = "cacheManager"
         class = "org.springframework.data.redis.cache.RedisCacheManager"
         c:template-ref = "redisTemplate" />
 
</ beans >

The Java configuration below is equivalent to the XML configuration above. People usually get hung up on using aPropertySourcesPlaceholderConfigurer. To do that, you need to use both the @PropertySource annotation and define aPropertySourcesPlaceholderConfigurer bean. The PropertySourcesPlaceholderConfigurer will not be sufficient on its own.


package com.joshuawhite.example.config;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
 
@Configuration
@EnableCaching
@ComponentScan ( "com.joshuawhite.example" )
@PropertySource ( "classpath:/redis.properties" )
public class AppConfig {
 
  private @Value ( "${redis.host-name}" ) String redisHostName;
  private @Value ( "${redis.port}" ) int redisPort;
 
  @Bean
  public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
      return new PropertySourcesPlaceholderConfigurer();
  }
 
  @Bean
  JedisConnectionFactory jedisConnectionFactory() {
      JedisConnectionFactory factory = new JedisConnectionFactory();
      factory.setHostName(redisHostName);
      factory.setPort(redisPort);
      factory.setUsePool( true );
      return factory;
  }
 
  @Bean
  RedisTemplate<Object, Object> redisTemplate() {
      RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
      redisTemplate.setConnectionFactory(jedisConnectionFactory());
      return redisTemplate;
  }
 
  @Bean
  CacheManager cacheManager() {
      return new RedisCacheManager(redisTemplate());
  }
 
}

Here is the properties file that is used by both configurations. Replace the values below with the host and port that you are using.



redis.host-name=yourHostNameHere
redis.port= 6379

Output

Finally, here is the output from our brief example application. Notice that no matter how many times we callgetHelloMessage("Josh"), the methods response does not get cached. This is because we defined a condition (seeHelloServiceImpl.java, line 13) where we only cache the methods response when the name equals “Joshua”.


When we call getHelloMessage("Joshua") for the first time, the method is executed. The second time however, it is not.


Executing HelloServiceImpl.getHelloMessage("Josh")
message: Hello Josh!
Executing HelloServiceImpl.getHelloMessage("Josh")
message: Hello Josh!
Executing HelloServiceImpl.getHelloMessage("Joshua")
message: Hello Joshua!
message: Hello Joshua!

Done.

This concludes our brief over view of caching with Spring Data Redis.


标签:redis,Spring,cache,Redis,springframework,org,Caching,import,example
From: https://blog.51cto.com/u_16203469/6838461

相关文章

  • 【深入浅出Spring原理及实战】「夯实基础系列」360全方位透析和探究SpringEL的开发实
    Spring表达式语言(SpEL)本文将介绍SpEL的功能、API和语言语法。概念介绍Spring表达式语言(SpEL)是一种功能强大的表达式语言,用于在运行时查询和操作对象图。它的语法与UnifiedEL相似,但提供了更多功能,其中最主要的是方法调用和基本的字符串模板功能。技术无关性虽然还有其他几......
  • redis基础知识
    Redis是什么?Redis(RemoteDictionaryServer)远程字典服务,是一个开源的使用ANSIC语言编写、支持网路、可基于内存也可持久化的日志型,key-value(NoSql---->non-relational)数据库Redis的特点?性能极高,基于内存,读的速度是11万次/s,写的速度是81千次/s丰富的数据类型,支持string、has......
  • springboot四
    SpringBoot集成Swagger在线接口文档1.Swagger简介1.1解决的问题随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。前端和后端的唯一联系变成了API接口,所以API文档变成了前后端开发人......
  • SpringBoot基于Spring Security的HTTP跳转HTTPS
    简单说说之所以采用SpringSecurity来做这件事,一是SpringSecurity可以根据不同的URL来进行判断是否需要跳转(不推荐),二是不需要新建一个TomcatServletWebServerFactoryBean,新建这个Bean可能会导致SpringBoot关于Server的配置失效。三是网上大部分流传的通过实现WebServerFactor......
  • 如何在Spring Boot中记录用户系统操作流程?
    在现代Web应用程序中,记录用户系统操作流程对于监控用户行为、进行故障排查、安全审计等方面都是非常重要的。在本篇博客中,我们将介绍如何在SpringBoot中使用AOP(面向切面编程)和日志框架来实现用户系统操作流程的记录。1.介绍在大多数Web应用程序中,需要记录用户在系统中的操......
  • 【Spring Boot 初识丨maven】
    上一篇讲了使用spirngboot自带的构建器构建项目本篇来讲一讲如何从零开始构建一个maven项目前提:jdk推荐java17及以上maven推荐Maven3.5+(maven构建)一、安装maven  Maven的安装需要依赖JDK的安装,所以必须先安装完成JDK且配置好JDK环境变量后在进行Maven的安装。......
  • SpringBoot+Vue实现校园二手系统。前后端分离技术【完整功能介绍+实现详情+源码】
    前言       这个系统基本上可以改造为其它类似的系统。后台管理基本上一致。前台进行一些页面样式的改造就可以变成一个新的系统。有时间,做几个变体系统。       闲的无聊,把大学时候做的一个系统进行了重构。将项目拆分成完全前后端分离的形式。客户端采用一套、商家......
  • SpringBoot集成日志入门
    一、日志的作用程序中的日志是记录程序的运行情况,包括用户的各种操作、程序的运行状态等信息。类似于飞机的黑匣子。二、日志的级别表:日志级别及其描述日志级别描述OFF关闭:不输出日志FATAL致命:用于输出可能会导致应用程序终止的错误ERROR错误:用于输出程序的错误(这些错误不会导......
  • Navicat连接Mysql出现“plugin 'caching_sha_password' cannot be loaded”错误
    Navicat连接Mysql出现“plugin'caching_sha_password'cannotbeloaded”错误问题现场​​**官方说明:**​https://dev.mysql.com/doc/refman/8.0/en/caching-sha2-pluggable-authentication.html解决方案使用本地mysql命令行登录;mysql-uroot-p先查一下看看--使用......
  • spring cloud common模块更新后,需要同步更新哪些服务?
    首先common没有启动类,他里面放的是一些静态资源,公共代码。理论上是需要更新所有依赖common模块的,就是maven依赖里面引用common包的都需要更新,但是这样太麻烦了。一般可以只更新需要依赖这一变动的服务。如果有feign调用,需要更新调用方,被调用方和网关gateway......