3.6 Redis在SpringBoot工程中的综合应用

RedisTemplate配置的jackson.ObjectMapper里的一个enableDefaultTyping方法过期解决 1、前言 最近升级SpringBoot,从2.1.6版本升级到2.2.6版本,发现enableDefaultTyping方法过期过期了。 该方法是指定序列化输入的类型,就是将数据库里的数据安装一定类型存储到redis缓存中。 2、为什么要指定序列化输入类型 2.1、没有指定序列化输入类型 如果注释掉enableDefaultTyping(ObjectMapper.DefaultTyp... 阅读详情


1.业务描述

从一个博客数据库中查询所有的文章标签,然后存储到缓存(Cache),后续查询时可从缓存获取。提高其查询性能。

2.准备工作

2.1 初始化数据

初始化数据库中数据,SQL脚本如下:

DROP DATABASE IF EXISTS `blog`;
CREATE DATABASE `blog` DEFAULT character set utf8mb4;
SET names utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
USE `blog`;

CREATE TABLE `tb_tag` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(255) NOT NULL COMMENT 'data_id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tb_tag';

insert into `tb_tag` values (null,"mysql"),(null,"redis");

2.2 添加项目依赖

在jt-template工程的原有依赖基础上添加mysql数据库访问依赖,例如:

<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>

2.3 添加数据库访问配置

在项目的配置文件(例如application.yml)中添加数据库访问配置,例如:

spring:
  datasource:
    url: jdbc:mysql:///blog?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    username: root
    password: root

3.业务逻辑代码设计及实现

3.1 Domain对象设计

创建一个Tag类,基于此类型的对象存储Tag(标签信息),代码如下:

package com.jt.blog.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;

/**
 * 标签类的设计
 */
@TableName("tb_tag")
public class Tag implements Serializable {
    private static final long serialVersionUID = 4504013456197711455L;
    /**标签id*/
    @TableId(type = IdType.AUTO)
    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;
    }
    @Override
    public String toString() {
        return "Tag{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

3.2 Dao 逻辑对象设计

创建Tag信息的数据访问接口,代码如下:

package com.jt.blog.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.blog.domain.Tag;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface TagMapper
        extends BaseMapper<Tag> {
}

创建单元测试类,TagMapper中的相关方法进行单元测试,例如:

package com.jt.blog.dao;

import com.jt.blog.domain.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class TagMapperTests {
    @Autowired
    private TagMapper tagMapper;
    @Test
    void testSelectList(){
        List<Tag> tags =
        tagMapper.selectList(null);
        for(Tag t:tags){
            System.out.println(t);
            //System.out.println(t.getId()+"/"+t.getName());
        }
    }
}

3.3 Service 逻辑对象设计

设计TagService接口及实现类,定义Tag(标签)业务逻辑。
第一步:定义TagService接口,代码如下:

package com.jt.blog.service;
import com.jt.blog.domain.Tag;
import java.util.List;
public interface TagService {
    /**
     * 查询所有的标签
     * @return
     */
    List<Tag> selectTags();
}

第二步:定义TagServiceImpl类,代码如下:

package com.jt.blog.service.impl;

import com.jt.blog.dao.TagMapper;
import com.jt.blog.domain.Tag;
import com.jt.blog.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class TagServiceImpl implements TagService {
    //RedisAutoConfiguration 类中做的RedisTemplate的配置
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TagMapper tagMapper;
    @Override
    public List<Tag> selectTags() {
        //1.从redis查询Tag信息,redis有则直接返回
        ValueOperations<String,List<Tag>> valueOperations =
        redisTemplate.opsForValue();
        List<Tag> tags=valueOperations.get("tags");
        if(tags!=null&&!tags.isEmpty())return tags;
        //2.从redis没有获取tag信息,查询mysql
        tags = tagMapper.selectList(null);
        //3.将从mysql查询到tag信息存储到redis
        valueOperations.set("tags", tags);
        //4.返回查询结果
        return tags;
    }
}

说明,假如将List存储到redis,此时Tag必须实现Serializable接口。

第三步:定义TagServiceTests单元测试类并进行单元测试,代码如下:

package com.jt.blog.service;

import com.jt.blog.domain.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class TagServiceTests {
    @Autowired
    private TagService tagService;
    
    @Test
    void testSelectTags(){
        List<Tag> tags=
        tagService.selectTags();
        System.out.println(tags);
    }
}

3.4 Controller逻辑对象设计

创建Tag控制逻辑对象,用于处理请求和响应逻辑,代码如下:

package com.jt.blog.controller;

import com.jt.blog.domain.Tag;
import com.jt.blog.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/tag")
public class TagController {
    @Autowired
    private TagService tagService;
    
    @GetMapping
    public  List<Tag> doSelectTags(){
      return  tagService.selectTags());//1.redis,2.mysql
    }
}

启动服务,打开浏览器进行访问测试。同时思考,我们是否可以在这个层加一个本地cache。

4.业务逻辑代码优化

4.1 定制RedisTemplate对象

RedisTemplate默认采用的是JDK的序列化方式,假如对系统对序列化做一些调整,可以自己定义RedisTemplate对象,例如:

package com.jt;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.net.UnknownHostException;

@Configuration
public class RedisCacheConfig {
    //代码定制参考RedisAutoConfiguration类
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(
         RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        System.out.println("===redisTemplate===");
        RedisTemplate<Object,Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //定义redisTemplate对象的序列化方式
        //1.定义key的序列化方式
        StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        //2.定义Value的序列化方式
        Jackson2JsonRedisSerializer jsonRedisSerializer=
                new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper=new ObjectMapper();
        objectMapper.setVisibility(
                PropertyAccessor.GETTER,
                JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(
                objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        jsonRedisSerializer.setObjectMapper(objectMapper);
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        //3.redisTemplate默认特性设置(除了序列化,其它原有特性不丢)
        template.afterPropertiesSet();
        return template;
    }
}

4.2 Service中AOP方式缓存应用

目标:简化缓存代码的编写
解决方案:基于AOP(面向切面编程)方式实现缓存应用
实践步骤:
第一步:在启动上类添加@EnableCaching注解(开启AOP方式的缓存配置),例如:

@EnableCaching //启动AOP方式的缓存配置
@SpringBootApplication
public class RedisApplication {
 ....
}

第二步:重构TagServiceImpl中的selectTags()方法,方法上使用@Cacheable注解,例如:

@Cacheable(value = "tagCache")
@Override
public List<Tag> selectTags() {
    return tagMapper.selectList(null);
}

其中,@Cacheable描述的方法为AOP中的一个切入点方法,访问这个方法时,系统底层会通过一个拦截器,检查缓存中是否有你要的数据,假如有则直接返回,没有则执行方法从数据库查询数据.

我们还可以定义Redis中key和value的序列化方式,修改key的生成策略,例如:

package com.jt.blog;
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.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;

@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
    /**
     * 定义缓存key生成器,不定义也可以使用默认的。
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
        return (o, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName()); // 类目
            sb.append("::");
            sb.append(method.getName()); // 方法名
            for (Object param : params) {
                sb.append(param.toString());
            }
            return sb.toString();
        };
    }
    /**
     * 自定义Cache管理器对象,不定义也可以,有默认的,但假如希望基于AOP
     * 方式实现Redis的操作时,按照指定的序列化方式进行序列化,
     * 可以对CacheManager进行自定义。
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60)) // 60s缓存失效
                // 设置key的序列化方式
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                                      .fromSerializer(new StringRedisSerializer()))
                // 设置value的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                                      .fromSerializer(
                   new Jackson2JsonRedisSerializer<Object>(Object.class)))
                // 不缓存null值
                .disableCachingNullValues();
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }
    
    .....
    
}

第三步:进行单元测试,检测redis中数据的存储.

package com.jt.blog.service;

import com.jt.blog.domain.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class TagServiceTests {
    @Autowired
    private TagService tagService;
    @Test
    void testSelectTags(){
        List<Tag> tags=
        tagService.selectTags();
        System.out.println(tags);
    }
}

4.3 Service中缓存一致性分析

当我们从数据库查询数据以后,假如将数据存入到了缓存,后续更新了数据库的数据,但假如没有更新缓存就会出现缓存数据与数据库数据不一致的这样的现象,对于这样问题,有时允许在一定时间范围之内存在。假如我们希望在更新了数据库数据以后要更新缓存,如何实现呢?接下来通过一个案例,来演示和解决一下这个问题.

第一步:修改TagService接口,添加相关方法,例如:

package com.jt.blog.service;
import com.jt.blog.domain.Tag;
import java.util.List;
public interface TagService {
    /**
     * 查询所有的标签
     * @return
     */
    List<Tag> selectTags();
    /**
     * 创建一个新的tag对象
     * @param tag
     */
    void insertTag(Tag tag);

    /**
     * 更新tag对象
     * @param tag
     * @return
     */
    Tag updateTag(Tag tag);

    /**
     * 基于id查询tag信息
     * @param id
     * @return
     */
    Tag selectById(Long id);
}

第二步:修改TagServiceImpl类,在类中重写TagService接口方法,例如:

package com.jt.blog.service.impl;

import com.jt.blog.dao.TagMapper;
import com.jt.blog.domain.Tag;
import com.jt.blog.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;

@Service
public class TagServiceImpl implements TagService {

    @Autowired
    private TagMapper tagMapper;

    /*@Cacheable注解描述的方法为缓存切入点方法
     *访问此方法时系统底层会先从缓存查找数据,假如缓存缓存没有,
     *会查询mysql数据库,这个注解假如想生效需要在启动类或者配置
     *类上添加@EnableCaching注解.
     *其中,这里的value用于指定一个key前缀,
     *没有指定key属性,则默认会使用 KeyGenerator对象创建key
     */
    @Cacheable(value = "tagCache")
    @Override
    public List<Tag> selectTags() {
        return tagMapper.selectList(null);
    }
    
    /**
     * @CacheEvict注解的作用是定义缓存切入点方法,执行此注解描述的方法
     * 时,底层通过AOP方式执行缓存数据的清除操作.
     * 其中,allEntries表示清除指定key所有数据,beforeInvocation用于定义
     * 在何时清除缓存数据,是更新数据库之后还是之前,false表示之后
     */
    @CacheEvict(value = "tagCache",allEntries = true,beforeInvocation = false)
    @Override
    public void insertTag(Tag tag) {
          tagMapper.insert(tag);
    }
    /**
     * 缓存数据时,可以自己指定key,key的值为spring中的el表达式,语法可以打开@Cacheable注解源码进行查看,
     * 这里的#id表示基于id的值作为key
     */
     @Cacheable(value="tagCache",key="#id")
     @Override
     public Tag selectById(Long id){
          return tagMapper.selectById(id);
     }

   /** @CachePut注解描述的方法为缓存切入点方法,系统底层会在执行此方法后,更新缓存数据,
     * 这里更新完数据以后,key为tag对象的id值,值为方法的返回值.
     */
     @CachePut(value = "tagCache",key="#tag.id")
     @Override
      public Tag updateTag(Tag tag){
          tagMapper.updateById(tag);
          return tag;
      }
}

第三步:修改单元测试类,测试缓存数据一致性.

package com.jt.blog.service;

import com.jt.blog.domain.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class TagServiceTests {
    @Autowired
    private TagService tagService;
    @Test
    void testSelectTags(){
        List<Tag> tags=
        tagService.selectTags();
        System.out.println(tags);
    }
    @Test
     void testInsertTag(){
        Tag tag=new Tag();
        tag.setName("Oracle1");
        tagService.insertTag(tag);
    }
        @Test
    void testSelectById(){
        Tag tag = tagService.selectById(1L);
        System.out.println(tag);
    }
      @Test
    void testUpdateTag(){
        Tag tag=new Tag();
        tag.setId(1L);
        tag.setName("mysql8.0");
        tagService.updateTag(tag);
    }
}

4.4 Controller中添加本地缓存

在Controller中添加一个本地缓存,减少对远程redis缓存的访问,例如:

package com.jt.blog.controller;

import com.jt.blog.domain.Tag;
import com.jt.blog.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

@RestController
@RequestMapping("/tag")
public class TagController {
    @Autowired
    private TagService tagService;
    //此对象存哪了?(JVM)
    private List<Tag> tags=new CopyOnWriteArrayList<>();//本地 cache
    @GetMapping
    public  List<Tag> doSelectTags(){
       if(tags.isEmpty()) {
           synchronized (tags) {
               if(tags.isEmpty()) {
                   tags.addAll(tagService.selectTags());//1.redis,2.mysql
               }
           }
       }
       return tags;
    }
}

4.5 Controller中本地缓存一致性分析

此次项目案例中,我们在Controller层添加了本地缓存,这个缓存我们也需要考虑其缓存一致性,其相关代码实现如下:

package com.jt.blog.controller;

import com.jt.blog.domain.Tag;
import com.jt.blog.service.TagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledExecutorService;

@RestController
@RequestMapping("/tag")
public class TagController {
    @Autowired
    private TagService tagService;
    //private List<Tag> tags=new ArrayList<>();
    private List<Tag> tags=new CopyOnWriteArrayList<>();//本地 cache


    @GetMapping("/{id}")
    public Tag doSelectById(@PathVariable("id") Long id){
        //查询本地缓存
        for(Tag t:tags){
            if(t.getId().equals(id)) return t;
        }
        //查询redis,mysql
        return tagService.selectById(id);
    }

    @PostMapping
    public String doInsertTag(Tag tag){
        //向数据库写入数据
        tagService.insertTag(tag);//A
        //更新本地缓存
        tags.add(tag);
        return "insert ok";
    }

    @PutMapping
    public String doUpdateTag(Tag tag){//id=1,name=mysql 8.9
        //向数据库写入数据
        tagService.updateTag(tag);
        //更新本地缓存
        for(Tag t:tags){
            if(t.getId().equals(tag.getId())){
                t.setName(tag.getName());
            }
        }
        return "update ok";
    }


    @GetMapping
    public  List<Tag> doSelectTags(){//B
       if(tags.isEmpty()) {
           synchronized (tags) {
               if(tags.isEmpty()) {
                   tags.addAll(tagService.selectTags());//1.redis,2.mysql
               }
           }
       }
       return tags;
    }
    /**Spring中Bean对象的生命周期方法,对象初始化时执行此方法*/
    @PostConstruct
    public void doInit(){
        doTimerRefreshTask();
    }
    /**Spring中Bean对象的生命周期方法,Bean对象初始化时执行此方法*/
    @PreDestroy
    public void doDestory(){
        //退出定时任务
        timer.cancel();
    }
    private Timer timer;
    //定义刷新任务
    private void doTimerRefreshTask(){
        //构建一个定时任务调度对象
        timer=new Timer();
        //构建一个任务对象
        TimerTask task=new TimerTask() {
            @Override
            public void run() {
                System.out.println("refresh cache");
                tags.clear();
            }
        };
        //执行任务对象(每隔5秒执行一次)
        timer.schedule(task, 5000, 5000);
    }

}

4.6 Redis集群链接配置实践

修改项目中的application.yml配置文件,修改redis配置,采用集群方式进行实现,例如:

spring:
  datasource: #默认配置的是HikariDataSource,应用的是HikariCP链接池(HikariPool)
    url: jdbc:mysql:///blog?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    username: root
    password: root
#redis 集群配置
  redis:
    cluster: #redis 集群配置
      nodes: 192.168.126.129:8010,192.168.126.129:8011,192.168.126.129:8012,192.168.126.129:8013,192.168.126.129:8014,192.168.126.129:8015
      max-redirects: 3 #最大跳转次数
    timeout: 5000 #超时时间
    database: 0
    jedis: #连接池
      pool:
        max-idle: 8
        max-wait: 0
#日志配置
logging:
  level:
    com.jt: debug
Redis存储对象类型丢失?一文搞懂activateDefaultTyping的配置与原理 本文深入解析Redis存储对象类型丢失问题,重点介绍activateDefaultTyping的配置与原理。通过Jackson的泛型处理机制和RedisTemplate的配置细节,提供完整的类型安全存储解决方案,帮助开发者避免反序列化过程中的类型擦除问题。 阅读详情

相关推荐

【SpringBoot3】Spring Boot 3.0 集成 Redis 缓存

Redis缓存是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它主要用于作为数据库、缓存和消息中间件,以快速读写和丰富的数据结构支持而著称。在应用程序和数据库之间,Redis缓存作为一个中间层起着关键作用。通过将常用的数据存储在Redis内存中,可以快速读取,从而避免了从数据库进行复杂的查询操作,减轻了数据库服务器的压力,并提高了应用程序的性能和响应速度。

不积跬步无以至千里,不积小流无以成江海。一个喜欢学习分享的程序员 1万+

Spring boot 整合 Redis 之 序列化配置

Redis是一种支持key-value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。使用C语言编写,支持网络,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。 废话不多说,直接开干! 教程是基于spring boot,spring项目的也是差不多的道理 第一步依旧是最简单的导入依赖 <!--此处省略其他代码--> <dependency> <groupId>org.springframework..

我真的不想继续爆零了啊的博客 5564

springboot集成redis主-从模式+哨兵机制

springboot集成redis主-从模式 一、环境准备 (硬件资源:Linux+redis-5.0.3+springboot project) 需redis 3.0以上安装包,这里面以redis-5.0.3为示例。 下面讲解在一台linux上模拟1主、2从、3哨兵的主从模式(实际应用中应需3台服务器,其中一台放1主和1哨兵,另外两台放1从1哨兵)。 redis安装这里不做介绍,解压tar...

u012105362的博客 9647

Jackson ObjectMapper activateDefaultTyping 中 JsonTypeInfo 的作用

主要用于将Java Class 以指定的格式序列化到Json字符串中,反序列化时就不需要显示的指定转换类型了。以属性的的方式,在Json字符串中体现为:"@class"为key,Class Type 为value。以数组的方式,Class Type 为第一个元素,Json字符串为第二个元素。以对象的形式,Class Type 为key, Json字符串为value。反序列化时需要显示指定读取的Java Class 类型。序列化后的字符串不包含Class Type。准备一个Person类,用于后续测试。

i余数的博客 4740

Drupal远程代码执行漏洞(CVE-2018-7600)实战复现:从检测到写入WebShell的全过程

本文详细解析了Drupal远程代码执行漏洞(CVE-2018-7600)的实战复现过程,从环境搭建、漏洞检测到WebShell写入的全流程。该高危漏洞影响Drupal 6.x至8.x版本,允许攻击者未经授权执行任意代码。文章提供了多种POC构造方法和高级利用技巧,帮助安全研究人员深入理解漏洞原理并掌握验证技术。

weixin_29322553的博客 272

SAP-PP MD04详解一

MD04是SAP运行MRP后的结果查询事务码,其他功能强大,标准功能比如:查询/更改物料主数据、转换计划订单到生产订单/采购申请、查询/更改各个MRP元素对应的单据等等。而且还可以附加标准的菜单或者自定义添加事务码上去。老铁将做一个MD04详细说明系列,本篇为第一篇之MRP元素说明。 我们来看看MD04基本界面 可以看到主要的数据显示界面,有日期、MRP元素、MRP元素数据、再计划日期、收货/需求、可用数量、工厂、库存地点等栏位。光标定位到MRP元素列,按F1即可看到各类MRP元素的说...

weixin_52158549的博客 1万+

ObjectMapper

在这个例子中,我们激活了默认的类型信息处理功能,并指定了类型信息的存储方式为非最终(NON_FINAL),这意味着类型信息将被包含在序列化的 JSON 数据中。综上所述,选择合适的序列化器取决于具体的业务需求和数据存储的特点。根据需求选择适当的序列化器可以提高数据的存储效率和操作的便利性。提供了丰富的功能和配置选项,可以根据需求进行定制化设置,用于处理各种复杂的 JSON 数据和对象转换场景。的行为进行灵活的定制,以满足特定的序列化和反序列化需求。类中的方法,用于配置对象序列化和反序列化时的行为。

m0_71814756的博客 1457

NVIDIA显卡在Linux下的功率限制问题:RTX 3060 Laptop的解决方案与避坑指南

本文针对NVIDIA RTX 3060 Laptop显卡在Linux系统下遭遇功率限制的问题,提供了详细的诊断与解决方案。通过使用nvidia-smi工具监控显卡状态,并重点介绍了通过升级驱动、启用nvidia-powerd服务来激活Dynamic Boost 2.0功能,从而安全、智能地解锁额外显卡功率,有效提升性能。

weixin_29290947的博客 631

2106-Redis在SpringBoot工程中的综合应用

业务描述 从一个博客数据库中查询所有的文章标签,然后存储到缓存(Cache),后续查询时可从缓存获取。提高其查询性能。 准备工作 初始化数据库数据 初始化数据库中数据,SQL脚本如下: DROP DATABASE IF EXISTS `blog`; CREATE DATABASE `blog` DEFAULT character set utf8mb4; SET names utf8mb4; SET FOREIGN_KEY_CHECKS = 0; USE `blog`; CREATE TABLE `tb_

雨田说码 8127

Altium Designer操作技巧(4)——如何利用嘉立创EDA来输出3D封装到AD封装库?

本文介绍了利用嘉立创EDA和FreeCAD软件为PCB元件添加3D模型的方法。首先在嘉立创EDA中搜索元件编号并导出3D模型,然后使用FreeCAD删除3D模型中的PCB基板部分,最后将处理后的3D模型导入PCB封装库并更新到PCB设计中。文章详细讲解了每个步骤的操作流程,包括模型导出、基板删除、位置调整等关键环节,并提供了解决常见问题的方法。通过这种方法,电子工程师可以快速获得元器件的3D模型,提高PCB设计的准确性。

u012526819的博客 2558

DeepSeek+Coze扣子搭建智能体(保姆级教程)

DeepSeek+Coze扣子搭建智能体(保姆级教程)

yizhen91y的博客 1万+

Redis在SpringBoot中的简单应用

本文作为作者学习的日志,仅供学习使用

jingjiaohuan的博客 6197

BeautyGAN: Instance-level Facial Makeup Transfer with Deep Generative Adversarial Network(2018,妆容迁移)

论文下载地址:BeautyGAN | Proceedings of the 26th ACM international conference on Multimedia 目的:在实例级实现化妆图像和非化妆图像的妆容迁移 方法:全局域集损失和本地实例级损失合并到一个双输入/输出对抗生成网络。步骤:使用一对鉴别器将非化妆面传输到化妆域,鉴别器从域的真实样本中分辨出生成的图像。在域集迁移的基础上,采用基于不同人脸区域的像素级直方图损失来实现实例级迁移。为了保持人脸的一致性和消除伪影,在总体目标函数中加入了.

weixin_53965540的博客 659

Redis在SpringBoot工程中的综合应用

Redis在SpringBoot工程中的综合应用 准备初始数据 1、初始化数据库 DROP DATABASE IF EXISTS `blog`; CREATE DATABASE `blog` DEFAULT character set utf8mb4; SET names utf8mb4; SET FOREIGN_KEY_CHECKS = 0; USE `blog`; CREATE TABLE `tb_tag` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COM

qq_44194174的博客 354

isaaclab无法识别NVIDIA显卡,如何解决?

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!    📌 特别说明: 文中部分技术问题来源于真实生产环境及网络公开案例,均经过精挑细选与系统化整理,并结合多位一线资深架构师和工程师多年实战经验沉淀,提

**My Coding Family** 1140

VMware如何安装img镜像,VMware如何安装openwrt软路由(含相关工具镜像)

VMware如何安装img镜像,VMware如何安装openwrt软路由(含相关工具镜像)

XiaoqiangClub的博客 3989

Day——10 Redis在SpringBoot工程中的综合应用

项目中缓存(Cache)的一种应用思想,

qq_40388903的博客 489

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

【Redis】06-RedisTemplate及在SpringBoot工程中的综合应用

RedisTemplate应用 简介 RedisTemplate为SpringBoot工程中操作redis数据库的一个Java对象,此对象封装了对redis的一些基本操作。 准备工作 第一步:创建工程配置文件application.yml,其内容如下: 单机(非集群)模式配置 spring: redis: host: 192.168.64.128 #写自己的ip port: 6379 #写自己的port 第二步:创建工程启动类,例如: package com.jt; impor

李鲤驴的博客 451

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

08-Redis在SpringBoot工程中的综合应用

从一个博客数据库中查询所有的文章标签,然后存储到缓存(Cache),后续查询时可从缓存获取。提高其查询性能。初始化数据库中数据,SQL脚本如下: 添加项目依赖 在jt-template工程的原有依赖基础上添加mysql数据库访问依赖,例如: 添加数据库访问配置 在项目的配置文件(例如application.yml)中添加数据库访问配置,例如: 业务逻辑代码设计及实现 Domain对象设计 创建一个Tag类,基于此类型的对象存储Tag(标签信息),代码如下: Dao 逻辑对象设计 创建Tag信息的数据访问接口

qq_54781283的博客 169

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

07-Redis在SpringBoot工程中的综合应用

RedisTemplate应用 简介 RedisTemplate为SpringBoot工程中操作redis数据库的一个Java对象,此对象封装了对redis的一些基本操作。 准备工作 添加依赖 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</gr

PengK_aha的博客 280

Springboot整合Redis

首先我们要知道Redis是什么:Redis一个NoSQL数据库,常用作缓存使用( cache ),用户访问应用,如果找数据的话会先访问redis,如果redis中有数据的话就直接返回发给用户,如果redis中没有的话我们再去查数据库,找到了数据之后一方面存到redis中,另一方面再反馈给用户,目的是说如果用户下次再访问类似的数据的话,redis中就有了。Redis如何启动:双击;客户端双击exit就退出了。

桂林电子科技大学 -芳 5042

SpringBoot3.0 集成 Redis2.6

SpringBoot3.0 集成 Redis2.6

long13102053862的博客 2080

07-Redis在SpringBoot工程中的综合应用(2106)

小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!程序员代码面试指南 IT名企算法与数据结构题目最优解这是” 本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近200道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。“刷”完本书后,你就是“题王”!

2401_83977689的博客 674

SpringBoot3集成Redis

Redis典型的应用场景就是数据缓存能力,用来解决业务中最容易出现的查询性能问题,提升系统的响应效率;其次就是分布式锁机制,用来解决分布式系统中多线程并发处理资源的安全问题;

【积累】,是一个长期持续的过程。 1523
上一篇: 3.5 Redis 事务处理及架构应用实践
下一篇: 3.7 Java中本地缓存实现
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值