SpringBoot integra Redis para implementar cache de dados

1. Conheça o Spring Cache

Spring Cache é um conjunto de soluções de cache fornecidas pela Spring. Ele não fornece implementação de cache em si, mas fornece uma interface unificada e especificações de código, configuração, anotações, etc., a fim de integrar várias soluções de Cache, de modo que os usuários não precisem se preocupar com os detalhes do Cache.

O Spring suporta a adição "transparente" de um cache ao aplicativo, aplicando o cache ao método e verificando se há dados disponíveis no cache antes que o método seja executado. Isso pode reduzir o número de execuções do método e aumentar a velocidade de resposta. A aplicação do cache é "transparente" e não causará nenhuma interferência ao chamador. Contanto que o suporte ao cache seja habilitado por meio da anotação @EnableCaching, o Spring Boot irá lidar automaticamente com a configuração básica do cache.

Spring Cache atua em métodos. Quando um método de cache é chamado, os parâmetros do método e o resultado de retorno serão armazenados no cache como uma "chave / valor" (chave / valor), e o método não será mais executado quando o método for chamado com os mesmos parâmetros da próxima vez. Em vez disso, obtenha o resultado diretamente do cache e retorne-o. Portanto, ao usar o Spring Cache, você deve garantir que os mesmos resultados sejam retornados quando o método em cache e os parâmetros do método são os mesmos.

As anotações de cache declarativas fornecidas pelo Spring Boot são as seguintes:

anotação Descrição
@EnableCaching Ative o cache.
@Cacheable Ele pode ser usado em classes e métodos para armazenar em cache os valores de retorno de classes ou métodos na forma de pares de valores-chave.
@CachePut O método é chamado e o resultado é armazenado em cache.
@CacheEvict Esvaziar o cache.
@Caching Usado para combinar várias tags de anotação.

Uso detalhado de anotações de cache declarativas: "Spring usa Cache e integra Ehcache"

 

2. Conheça o Redis

O Redis é atualmente um dos sistemas de armazenamento de dados de memória mais amplamente usados. Ele suporta uma estrutura de dados mais rica, persistência de dados, transação, HA (alta disponibilidade), sistema de cluster de computador duplo, biblioteca mestre-escravo.

Redis é um sistema de armazenamento de valor-chave. Os tipos de valor que ele suporta incluem String, List, Set, Zset (conjunto ordenado) e Hash. Todos esses tipos de dados oferecem suporte a push / pop, add / remove e interseção, união, diferença ou operações mais ricas, e essas operações são todas atômicas. Com base nisso, o Redis oferece suporte a vários tipos e algoritmos de maneiras diferentes.

 

3. Crie um projeto SpringBoot e uma tabela de dados

[Exemplo] Integre a estrutura SpringBoot e MyBaits e use o Redis como cache para implementar funções de operação relacionadas aos dados do usuário, como adicionar usuários, consultar informações do usuário com base no ID do usuário, atualizar informações do usuário e excluir usuários com base no ID do usuário. Como mostrado abaixo:

3.1 Criar um projeto

(1) Crie um projeto SpringBoot, a estrutura do projeto é a seguinte:

(2) Use Maven para adicionar arquivos dependentes

No arquivo de informações de configuração pom.xml, adicione Redis, MyBatis, banco de dados MySQL, mecanismo de modelo Thymeleaf e outras dependências relacionadas:

<!-- Redis启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>

<!-- MyBatis与SpringBoot整合依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

<!-- MySQL的JDBC数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
</dependency>

<!-- 引入Thymeleaf模板引擎 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3.2 Criar uma tabela de dados

Use o banco de dados MySQL, crie a tabela de informações do usuário tb_user e adicione dados.

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tb_user;
 
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tb_user
( 
	user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
	user_name VARCHAR(50) NOT NULL COMMENT '用户姓名',
	age int default(0) NOT NULL COMMENT '年龄',
	blog_url VARCHAR(50) NOT NULL COMMENT '博客地址',
	blog_remark VARCHAR(50) COMMENT '博客信息'
) COMMENT = '用户信息表';
 
-- 添加数据
INSERT INTO tb_user(user_name,age,blog_url,blog_remark) VALUES('pan_junbiao的博客',32,'https://blog.csdn.net/pan_junbiao','您好,欢迎访问 pan_junbiao的博客');

 

4. Redis implementa armazenamento em cache de dados

4.1 Configuração do Redis

Primeiro, configure o gerenciador de cache de destino Spring no arquivo de configuração application.yml para suportar Ehcache, Generic, Redis, Jcache, etc. Esta configuração usa Redis.

#Spring配置
spring:
  #缓存管理器
  cache:
    type: redis

Configure Redis e MyBatis no arquivo de configuração application.yml. As informações de configuração completas são as seguintes:

#Spring配置
spring:
  #缓存管理器
  cache:
    type: redis
  #Redis配置
  redis:
    database: 0 #Redis数据库索引(默认为0)
    host: 127.0.0.1 #Redis服务器地址
    port: 6379 #Redis服务器连接端口
    password:  #Redis服务器连接密码(默认为空)
    jedis:
      pool:
        max-active: 8 #连接池最大连接数(使用负值表示没有限制)
        max-wait: -1s #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 8  #连接池中的最大空闲连接
        min-idle: 0 #连接池中的最小空闲连接
    lettuce:
      shutdown-timeout: 100ms #关闭超时时间,默认值100ms
  #使用Thymeleaf模板引擎
  thymeleaf:
    mode: HTML5
    encoding: UTF-8
    cache: false  #使用Thymeleaf模板引擎,关闭缓存
    servlet:
      content-type: text/html
  #DataSource数据源
  datasource:
    url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&amp
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

#MyBatis配置
mybatis:
  type-aliases-package: com.pjb.entity #别名定义
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #指定 MyBatis 所用日志的具体实现,未指定时将自动查找
    map-underscore-to-camel-case: true #开启自动驼峰命名规则(camel case)映射
    lazy-loading-enabled: true #开启延时加载开关
    aggressive-lazy-loading: false #将积极加载改为消极加载(即按需加载),默认值就是false
    #lazy-load-trigger-methods: "" #阻挡不相干的操作触发,实现懒加载
    cache-enabled: true #打开全局缓存开关(二级环境),默认值就是true

4.2 Classe de configuração do Redis (camada de configuração)

No pacote com.pjb.config, crie uma classe RedisConfig (classe de configuração Redis) e herde a classe CachingConfigurerSupport.

package com.pjb.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
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.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.lang.reflect.Method;

/**
 * Redis配置类
 * @author pan_junbiao
 **/
@Configuration
public class RedisConfig extends CachingConfigurerSupport
{
    /**
     * 缓存对象集合中,缓存是以key-value形式保存的,
     * 当不指定缓存的key时,SpringBoot会使用keyGenerator生成Key。
     */
    @Bean
    public KeyGenerator keyGenerator()
    {
        return new KeyGenerator()
        {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                //类名+方法名
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * 缓存管理器
     */
    @SuppressWarnings("rawtypes")
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory)
    {
        RedisCacheManager cacheManager = RedisCacheManager.create(connectionFactory);
        //设置缓存过期时间

        return cacheManager;
    }

    /**
     * 实例化RedisTemplate对象
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory)
    {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4.3 Criar uma classe de entidade (camada de entidade)

No pacote com.pjb.entity, crie uma classe UserInfo (classe de entidade de informações do usuário).

package com.pjb.entity;

import java.io.Serializable;

/**
 * 用户信息实体类
 * @author pan_junbiao
 **/
public class UserInfo implements Serializable
{
    private int userId; //用户编号
    private String userName; //用户姓名
    private int age; //年龄
    private String blogUrl; //博客地址
    private String blogRemark; //博客信息

    //省略getter与setter方法...
}

Nota: A classe de entidade deve implementar a interface serializável, caso contrário, a função de cache não pode ser implementada.

4.4 Camada de mapeamento de banco de dados (camada de mapeador)

No pacote com.pjb.mapper, crie a interface UserMapper (interface de proxy dinâmico do Mapeador de informações do usuário).

package com.pjb.mapper;

import com.pjb.entity.UserInfo;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

/**
 * 用户信息Mapper动态代理接口
 * @author pan_junbiao
 **/
@Mapper
@Repository
public interface UserMapper
{
    /**
     * 根据用户ID,获取用户信息
     */
    @Select("SELECT * FROM tb_user WHERE user_id = #{userId}")
    public UserInfo getUserById(int userId);

    /**
     * 新增用户,并获取自增主键
     */
    @Insert("INSERT INTO tb_user(user_name,age,blog_url,blog_remark) VALUES(#{userName},#{age},#{blogUrl},#{blogRemark});")
    @Options(useGeneratedKeys = true, keyColumn = "user_id", keyProperty = "userId")
    public int insertUser(UserInfo userInfo);

    /**
     * 修改用户
     */
    @Update("UPDATE tb_user SET user_name = #{userName} ,age = #{age} ,blog_url = #{blogUrl} ,blog_remark = #{blogRemark} WHERE user_id = #{userId}")
    public int updateUser(UserInfo userInfo);

    /**
     * 删除用户
     */
    @Delete("DELETE FROM tb_user WHERE user_id = #{userId}")
    public int deleteUser(int userId);
}

4.5 Camada de lógica de negócios (camada de serviço)

Crie a interface UserService (interface lógica de negócios de informações do usuário) no pacote com.pjb.service.

package com.pjb.service;

import com.pjb.entity.UserInfo;

/**
 * 用户信息业务逻辑接口
 * @author pan_junbiao
 **/
public interface UserService
{
    /**
     * 根据用户ID,获取用户信息
     */
    public UserInfo getUserById(int userId);

    /**
     * 新增用户,并获取自增主键
     */
    public UserInfo insertUser(UserInfo userInfo);

    /**
     * 修改用户
     */
    public UserInfo updateUser(UserInfo userInfo);

    /**
     * 删除用户
     */
    public int deleteUser(int userId);
}

No pacote com.pjb.service.impl, crie a classe UserServiceImpl (classe de lógica de negócios de informações do usuário).

package com.pjb.service.impl;

import com.pjb.entity.UserInfo;
import com.pjb.mapper.UserMapper;
import com.pjb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户信息业务逻辑类
 * @author pan_junbiao
 * 缓存就在这层工作
 * @Cacheable,将查询结果缓存到redis中,(key="#p0")指定传入的第一个参数作为redis的key。
 * @CachePut,指定key,将更新的结果同步到redis中
 * @CacheEvict,指定key,删除缓存数据,allEntries=true,方法调用后将立即清除缓存
 **/
@CacheConfig(cacheNames = "userCache")
@Service
@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Exception.class)
public class UserServiceImpl implements UserService
{
    @Autowired
    private UserMapper userMapper;

    /**
     * 根据用户ID,获取用户信息
     */
    @Override
    @Cacheable(key = "#p0") // #p0 表示第一个参数
    public UserInfo getUserById(int userId)
    {
        return userMapper.getUserById(userId);
    }

    /**
     * 新增用户,并获取自增主键
     */
    @Override
    @CachePut(key = "#p0.userId")
    public UserInfo insertUser(UserInfo userInfo)
    {
        userMapper.insertUser(userInfo);
        return userInfo;
    }

    /**
     * 修改用户
     */
    @Override
    @CachePut(key = "#p0.userId")
    public UserInfo updateUser(UserInfo userInfo)
    {
        userMapper.updateUser(userInfo);
        return userInfo;
    }

    /**
     * 删除用户
     * 如果在@CacheEvict注解中添加allEntries=true属性,
     * 将会删除所有的缓存
     */
    @Override
    @CacheEvict(key = "#p0")
    public int deleteUser(int userId)
    {
        return userMapper.deleteUser(userId);
    }
}

Como pode ser visto no código acima, o método de consultar os usuários usa a anotação @Cacheable para habilitar o cache. Métodos de adição e modificação usam a anotação @CachePut, que processa o método primeiro e, em seguida, armazena em cache o resultado. Para excluir dados, você precisa usar a anotação @CacheEvict para limpar o cache.

Anotação @CacheConfig: Se todos os @Cacheable () tiverem um atributo value = "xxx", é obviamente cansativo escrever se houver mais métodos, se você puder declarar tudo de uma vez, isso evitará problemas, então há With @ CacheConfig esta configuração, @CacheConfig é uma anotação em nível de classe que permite compartilhar os nomes de cache.Se você escrever outro nome no método, o nome do método prevalecerá.

4.6 Método do controlador (camada do controlador)

No pacote com.pjb.controller, crie a classe UserController (controlador do usuário) para realizar a consulta, adição, modificação e exclusão dos dados do usuário e realizar o retorno dos dados.

package com.pjb.controller;

import com.pjb.entity.UserInfo;
import com.pjb.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * 用户信息控制器
 * @author pan_junbiao
 **/
@Controller
@RequestMapping("/user")
public class UserController
{
    @Autowired
    private UserService userService;

    /**
     * 获取用户信息
     */
    @RequestMapping("getUserById")
    public ModelAndView getUserById(int userId)
    {
        //根据用户ID,获取用户信息
        UserInfo userInfo = userService.getUserById(userId);

        if(userInfo==null)
        {
            userInfo = new UserInfo();
        }

        //返回结果
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("userInfo",userInfo);
        modelAndView.setViewName("/user-info.html");
        return modelAndView;
    }

    /**
     * 新增用户
     */
    @ResponseBody
    @RequestMapping("insertUser")
    public boolean insertUser()
    {
        //创建新用户
        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("pan_junbiao的博客");
        userInfo.setAge(32);
        userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
        userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

        //执行新增方法
        userService.insertUser(userInfo);

        //返回结果
        return userInfo.getUserId() > 0 ? true : false;
    }

    /**
     * 修改用户
     */
    @ResponseBody
    @RequestMapping("updateUser")
    public boolean updateUser(int userId)
    {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setUserName("pan_junbiao的博客_02");
        userInfo.setAge(35);
        userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao");
        userInfo.setBlogRemark("您好,欢迎访问 pan_junbiao的博客");

        //执行修改方法
        userService.updateUser(userInfo);

        //返回结果
        return true;
    }

    /**
     * 删除用户
     */
    @ResponseBody
    @RequestMapping("deleteUser")
    public boolean deleteUser(int userId)
    {
        //执行新增方法
        int result = userService.deleteUser(userId);

        //返回结果
        return result > 0 ? true : false;
    }
}

4.7 Página de exibição (camada de visualização)

Crie uma página de exibição de informações do usuário user-info.html no diretório resources / templates.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>用户信息</title>
    <meta name="author" content="pan_junbiao的博客">
    <style>
        table { border-collapse: collapse; margin-bottom: 10px}
        table,table tr th, table tr td { border:1px solid #000000; padding: 5px 10px;}
    </style>
</head>
<body>
<div align="center">
    <table>
        <caption>用户信息</caption>
        <tr>
            <th>用户ID:</th>
            <td th:text="${userInfo.userId}"></td>
        </tr>
        <tr>
            <th>用户名称:</th>
            <td th:text="${userInfo.userName}"></td>
        </tr>
        <tr>
            <th>年龄:</th>
            <td th:text="${userInfo.age}"></td>
        </tr>
        <tr>
            <th>博客地址:</th>
            <td th:text="${userInfo.blogUrl}"></td>
        </tr>
        <tr>
            <th>备注信息:</th>
            <td th:text="${userInfo.blogRemark}"></td>
        </tr>
    </table>
</div>
</body>
</html>

Resultados de:

(1) Use o Redis para obter informações do usuário a partir de dados em cache, conforme mostrado na figura a seguir:

(2) Use a ferramenta de visualização Redis Desktop Manager (RDM) para consultar os dados em cache, conforme mostrado na figura abaixo:

 

Acho que você gosta

Origin blog.csdn.net/pan_junbiao/article/details/108813376
Recomendado
Clasificación