首页 文章 精选 留言 我的

精选列表

搜索[c 14],共10000篇文章
优秀的个人博客,低调大师

《Spring AI 2.0实战》第14招 MCP把工具标准化

## 01、痛点 最近业务方提了个需求:能不能让智能助手直接查订单、读内部文件,顺便查一下外部天气再综合回复?我是老李,第一反应是这不就是让大模型调几个函数吗。结果真做起来,发现单应用里的 Tool Calling 只能调用本进程内写死的函数。订单服务在 A 系统,文件能力在 B 机器,天气接口在公网,每接一个能力都要写一套适配逻辑,模型一换,适配层还要重写。更麻烦的是,不同团队对工具、资源、模板的定义五花八门,集成成本越堆越高。 这个问题正在被 MCP 重新定义。MCP 把工具、资源、Prompt 这些能力标准化成一套客户端-服务端协议,Spring Boot 服务既可以作为 MCP Client 去消费外部能力,也可以作为 MCP Server 把自己已有的业务能力暴露出去。对 Java 团队来说,这条路不是靠推翻现有系统,而是靠 Spring AI 提供的启动器和注解,把已有的 Spring 服务平滑接入模型调用链。 为什么现在要讲?因为模型能力已经强到能推动企业真正落地集成,但很多团队还停留在把 API 复制进代码的阶段。MCP 很可能成为 AI 时代新的集成层,而 Spring 又是 MCP Java SDK 的早期关键贡献方,此时不把路线搞清楚,后面会踩更多坑。 ## 02、是什么 Tool Calling 是单应用内部的概念。模型在一次对话中根据上下文决定调用哪个函数,并给出参数,应用执行函数后把结果返回给模型,模型再继续生成回答。这个过程里,函数定义、执行逻辑、返回格式都写死在应用代码中。它适合演示和小工具,但一旦需要跨进程、跨团队、跨语言复用,适配成本就会指数级上升。 MCP 则是一种开放协议。它把模型可能用到的东西分成三类:Tool、Resource、Prompt。Tool 是可执行的函数,比如查询订单状态;Resource 是模型可以读取的上下文数据,比如客户基础信息;Prompt 是可复用的指令模板,比如订单异常排查流程。这三类能力通过 MCP Client 和 MCP Server 之间的协议来发现、描述和调用。客户端不需要关心服务端是 Java 写的还是 Node 写的,也不需要关心传输层是本地进程还是 HTTP。 在 Spring 体系里,Spring AI 把 MCP 的复杂度包了起来。当前 Spring AI 提供 Client/Server Starters,以及 @Tool、@Resource、@Prompt 等注解。对业务开发人员来说,暴露一个 Spring Bean 方法只需要加一个注解,消费一个外部 MCP Server 只需要加一段配置。官方说明 Spring 是 MCP Java SDK 的早期关键贡献方之一,这意味着后续的协议演进和 Java 生态适配大概率会保持同步。 ## 03、为什么 企业里最常见的现状是:大量内部 API 已经存在,但模型无法直接使用。要么字段太多,要么鉴权逻辑复杂,要么接口语义只适合人看,不适合模型调用。传统的做法是每个模型应用单独写工具适配类,造成重复建设。MCP 把这些适配层统一成一种协议,只要服务端实现了 MCP,任何客户端都能用标准方式发现和调用。 Spring Boot 关注 MCP 有三个直接理由。第一,Spring 生态本身是企业 Java 的事实标准,大量存量服务可以低成本改造成 MCP Server。第二,Spring AI 同时支持客户端和服务端启动器,一个应用既能消费外部 Filesystem、Weather 之类的公共 MCP Server,也能把自己的订单查询暴露出去。第三,MCP 是开放协议,不绑定某个模型厂商,可以避免被单一工具链锁死。 从时间点看,现在模型生成结构化调用参数的能力已经比较稳定,但真正卡住企业落地的不是模型本身,而是集成层的混乱。MCP 正在把模型调用企业能力这件事从临时拼接变成可治理的工程问题。早一步动手,团队可以积累一套可复用的接入规范,而不是等协议碎片化后再返工。 ## 04、架构 MCP 的典型结构是客户端-服务端。客户端一般是需要调用外部能力的一方,服务端是提供能力的一方。传输层可以走 stdio,也可以走 HTTP/SSE。对 Spring Boot 应用来说,架构上通常有两种角色。 第一种角色:Spring Boot 作为 MCP Client。应用启动时通过 Spring AI 的 starter 初始化多个 MCP Client,连接外部 Filesystem MCP Server 或 Weather MCP Server。模型需要读文件时,通过 MCP Client 发现并调用 filesystem 相关工具;需要查天气时,调用 weather 相关工具。整个过程对模型来说,工具列表是标准化的,不需要知道底层是 stdio 还是 HTTP。 第二种角色:Spring Boot 作为 MCP Server。已有的订单查询服务通过 @Tool 注解把方法暴露出去。MCP Server 启动后,外部客户端可以列出该服务提供的工具、资源、Prompt,并发起调用。Spring AI 的服务端 starter 会自动处理协议握手、工具发现、参数校验和结果序列化。 文字简化后的架构链路如下: 模型应用 -> Spring AI MCP Client -> MCP Server(Filesystem/Weather) 外部客户端 -> Spring AI MCP Server -> 订单查询服务 在协议层,Tool 表示可执行操作,Resource 表示可读取数据,Prompt 表示可复用模板。一个只读的订单查询服务,至少可以暴露一个 Tool,比如 getOrderStatus(orderId);如果还需要让模型知道订单状态字段含义,可以通过 Resource 提供字典说明。 ## 05、实战 下面分两步落地。 第一步:让 Spring AI 应用连接 Filesystem 和 Weather MCP Server。 先引入客户端 starter。Maven 依赖大致如下: ```xml org.springframework.ai spring-ai-starter-mcp-client ``` 在 application.yml 中配置两个外部服务。Filesystem 走 stdio,Weather 走 HTTP/SSE,配置示例: ```yaml spring: ai: mcp: client: filesystem: command: npx args: - -y - @modelcontextprotocol/server-filesystem - /tmp/workspace weather: url: https://weather-mcp.example.com/mcp ``` 业务代码里注入 MCP 工具集合,交给模型调用即可。此时模型会看到 filesystem 提供的读写工具,以及 weather 提供的查询工具。业务方让助手读文件、查天气,不再需要为每个外部服务单独写适配函数。 第二步:把自己的订单查询服务暴露成 MCP Server。 引入服务端 starter: ```xml org.springframework.ai spring-ai-starter-mcp-server ``` 假设已有 OrderService,我们只暴露一个只读查询方法。代码中不用写协议细节,只需要在方法上添加 @Tool 注解,用 description 说明该方法只返回订单状态和更新时间,再用 @ToolParam 说明参数是订单号。方法体内部调用原有的 OrderService.findByOrderId,然后手动裁剪返回字段,只拼接状态和更新时间。 服务端配置指定名称和版本: ```yaml spring: ai: mcp: server: name: order-query-mcp version: 1.0.0 ``` 启动后,服务就成为一个 MCP Server。可以在模型应用里新增一个 MCP Client 指向该服务,模型就能在适当场景调用 getOrderStatus。先用只读查询验证链路,再考虑扩展更多工具。 ## 06、洞见 这时候最常见的问题是:既然暴露一个方法这么简单,为什么企业不能把所有内部 API 都直接暴露给 MCP? 答案很直接:MCP 标准化了连接方式,但没有标准化信任边界。一个内部 API 原来只被 Web 前端通过登录态、菜单权限、参数校验、审计日志等层层保护。如果直接加个 @Tool 就暴露给模型,等于新增了一条没有经过完整安全设计的执行路径。 几个具体风险。第一,越权调用。模型可能被诱导调用不属于当前用户的接口,如果工具层没有做用户身份传递和权限校验,内部 API 等于裸奔。第二,数据过曝。内部接口经常返回完整实体,模型只使用其中一两个字段,但中间链路可能记录了完整响应,造成敏感数据扩散。第三,写操作失控。退款、修改地址、删除数据这类接口一旦暴露给模型,误调用和恶意引导的后果会非常严重。第四,治理缺失。没有统一的工具目录、调用审计、速率限制和下线机制,MCP Server 越多,企业越难知道模型到底能做什么、做了什么。 从更大的视角看,MCP 很可能成为 AI 时代新的 Integration Layer。过去企业用 ESB、API 网关、消息队列解决系统间集成,未来模型应用会通过 MCP 连接企业能力。但集成层解决的是能连上,认证、授权、数据分级、审计、限流、脱敏这些治理能力,仍然要企业自己建设。协议不会替你决定谁能调用、能调多少、调完留痕。 所以,MCP 是必要的连接标准,但绝不是安全网关。企业应该把 MCP Server 当作新的对外接口面,而不是内部 API 的简单映射。 ## 07、行动 落地节奏建议从保守到开放,分三步走。 第一步,先做一个只读 MCP Server。选择低风险、非敏感的数据查询能力,例如订单状态、库存余量、文档摘要。只暴露最小工具集,返回字段做裁剪,不提供任何写操作。这一步的目标是跑通协议、模型调用、参数校验、观测日志,而不是追求能力覆盖。 第二步,加入认证与授权策略。根据企业的安全现状,优先选择 API Key 或 OAuth 2.0。API Key 适合机器对机器的内部调用,简单直接;OAuth 适合需要绑定用户身份、做细粒度授权的场景。同时要把当前用户身份从客户端传递到服务端,在工具执行前做权限校验,而不是只验一个总 Key。 第三步,逐步开放更复杂能力。只有在前两步的日志、监控、限流、降级都验证通过后,才考虑加入写操作。写操作最好拆成预检查、执行、结果确认的闭环,模型只负责生成参数,最终执行由服务端二次校验。企业还可以建立内部 MCP 能力目录,明确哪些服务可以暴露、暴露哪些工具、谁在消费。 行动的核心原则是:先读后写,先内后外,安全前置。不要一上来把内部 API 批量暴露给模型,那样做的团队,往往会在下一次安全评审时付出更大代价。 ## 08、小结 Tool Calling 解决单应用里的函数调用,MCP 把 Tool、Resource、Prompt 等能力进一步标准化,使 Spring Boot 服务既能作为 MCP Client 消费外部能力,也能成为 MCP Server 暴露企业能力。Spring 是 MCP Java SDK 的早期关键贡献方,Spring AI 当前提供的 Client/Server Starters 和注解支持,让 Java 团队可以用较低成本接入这套协议。 但标准化协议不会替代认证、授权和治理。企业不能把所有内部 API 直接暴露给 MCP,因为开放协议解决的是连接格式,不是信任边界。先做一个只读 MCP Server,再逐步加入 API Key、OAuth、审计和限流,是更稳妥的落地路线。 ## 09、链接与概念说明 概念说明: - Tool Calling:模型根据上下文决定调用应用内函数并传参,应用执行后返回结果。 - MCP:Model Context Protocol,一种开放协议,标准化 Tool、Resource、Prompt 等能力的发现与调用。 - MCP Client:消费 MCP Server 能力的一方,负责发现工具、发起调用、接收结果。 - MCP Server:提供能力的一方,暴露工具、资源和 Prompt 供客户端调用。 - Spring AI:Spring 生态面向大模型应用提供的开发框架,包含 MCP Client/Server Starters 和注解支持。 - @Tool:Spring AI 中把 Spring Bean 方法暴露为 MCP 工具的注解。 参考链接: - Model Context Protocol 规范:https://modelcontextprotocol.io - Spring AI 项目主页:https://spring.io/projects/spring-ai - Spring Boot 官方文档:https://spring.io/projects/spring-boot

优秀的个人博客,低调大师

搞懂分布式技术14:Spring Boot使用注解集成Redis缓存

2 --> 为了提高性能,减少数据库的压力,使用缓存是非常好的手段之一。本文,讲解 Spring Boot 如何集成缓存管理。 Spring注解缓存 Spring 3.1之后,引入了注解缓存技术,其本质上不是一个具体的缓存实现方案,而是一个对缓存使用的抽象,通过在既有代码中添加少量自定义的各种annotation,即能够达到使用缓存对象和缓存方法的返回对象的效果。Spring的缓存技术具备相当的灵活性,不仅能够使用SpEL(Spring Expression Language)来定义缓存的key和各种condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存集成。其特点总结如下: 少量的配置annotation注释即可使得既有代码支持缓存; 支持开箱即用,不用安装和部署额外的第三方组件即可使用缓存; 支持Spring Express Language(SpEL),能使用对象的任何属性或者方法来定义缓存的key和使用规则条件; 支持自定义key和自定义缓存管理者,具有相当的灵活性和可扩展性。 和Spring的事务管理类似,Spring Cache的关键原理就是Spring AOP,通过Spring AOP实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。而Spring Cache利用了Spring AOP的动态代理技术,即当客户端尝试调用pojo的foo()方法的时候,给它的不是pojo自身的引用,而是一个动态生成的代理类。 如图12所示,实际客户端获取的是一个代理的引用,在调用foo()方法的时候,会首先调用proxy的foo()方法,这个时候proxy可以整体控制实际的pojo.foo()方法的入参和返回值,比如缓存结果,比如直接略过执行实际的foo()方法等,都是可以轻松做到的。Spring Cache主要使用三个注释标签,即@Cacheable、@CachePut和@CacheEvict,主要针对方法上注解使用,部分场景也可以直接类上注解使用,当在类上使用时,该类所有方法都将受影响。我们总结一下其作用和配置方法,如表1所示。 表1 标签类型 作用 主要配置参数说明 @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 value:缓存的名称,在 Spring 配置文件中定义,必须指定至少一个;key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则默认按照方法的所有参数进行组合;condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 @CachePut 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 value:缓存的名称,在 spring 配置文件中定义,必须指定至少一个;key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则默认按照方法的所有参数进行组合;condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 @CacheEvict 主要针对方法配置,能够根据一定的条件对缓存进行清空 value:缓存的名称,在 Spring 配置文件中定义,必须指定至少一个;key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则默认按照方法的所有参数进行组合;condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存;allEntries:是否清空所有缓存内容,默认为 false,如果指定为 true,则方法调用后将立即清空所有缓存;beforeInvocation:是否在方法执行前就清空,默认为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,默认情况下,如果方法执行抛出异常,则不会清空缓存 可扩展支持:Spring注解cache能够满足一般应用对缓存的需求,但随着应用服务的复杂化,大并发高可用性能要求下,需要进行一定的扩展,这时对其自身集成的缓存方案可能不太适用,该怎么办?Spring预先有考虑到这点,那么怎样利用Spring提供的扩展点实现我们自己的缓存,且在不改变原来已有代码的情况下进行扩展?是否在方法执行前就清空,默认为false,如果指定为true,则在方法还没有执行的时候就清空缓存,默认情况下,如果方法执行抛出异常,则不会清空缓存。 这基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持久化数据能力,这个时候,你就需要自定义你的缓存方案了,还好,Spring也想到了这一点。 我们先不考虑如何持久化缓存,毕竟这种第三方的实现方案很多,我们要考虑的是,怎么利用Spring提供的扩展点实现我们自己的缓存,且在不改原来已有代码的情况下进行扩展。这需要简单的三步骤,首先需要提供一个CacheManager接口的实现(继承至AbstractCacheManager),管理自身的cache实例;其次,实现自己的cache实例MyCache(继承至Cache),在这里面引入我们需要的第三方cache或自定义cache;最后就是对配置项进行声明,将MyCache实例注入CacheManager进行统一管理。 声明式缓存 Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。在使用 Spring 集成 Cache 的时候,我们需要注册实现的 CacheManager 的 Bean。 Spring Boot默认集成CacheManager Spring Boot 为我们自动配置了多个 CacheManager 的实现。Spring Boot 为我们自动配置了 JcacheCacheConfiguration、 EhCacheCacheConfiguration、HazelcastCacheConfiguration、GuavaCacheConfiguration、RedisCacheConfiguration、SimpleCacheConfiguration 等。 默认的 ConcurrenMapCacheManager Spring 从 Spring3.1 开始基于 java.util.concurrent.ConcurrentHashMap 实现的缓存管理器。所以, Spring Boot 默认使用 ConcurrentMapCacheManager 作为缓存技术。 以下是我们不引入其他缓存依赖情况下,控制台打印的日志信息。 Bean'cacheManager' of type [class org.springframework.cache.concurrent.ConcurrentMapCacheManager] 实战演练 Maven 依赖 首先,我们先创建一个 POM 文件。 <projectxmlns="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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </parent> <groupId>com.lianggzone.demo</groupId> <artifactId>springboot-action-cache</artifactId> <version>0.1</version> <packaging>jar</packaging> <name>springboot-action-cache</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <defaultLibBundleDir>lib</defaultLibBundleDir> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> <useDefaultDelimiters>false</useDefaultDelimiters> <escapeString>\</escapeString> <delimiters> <delimiter>${*}</delimiter> </delimiters> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 其中,最核心的是添加 spring-boot-starter-cache 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> 开启缓存支持 在 Spring Boot 中使用 @EnableCaching 开启缓存支持。 @Configuration @EnableCaching publicclassCacheConfiguration{} 服务层 创建一个服务类 @Service("concurrenmapcache.cacheService") publicclassCacheService{ } 首先,我们先来讲解下 @Cacheable 注解。@Cacheable 在方法执行前 Spring 先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放进缓存。有两个重要的值, value,返回的内容将存储在 value 定义的缓存的名字对象中。key,如果不指定将使用默认的 KeyGenerator 生成。 我们在查询方法上,添加 @Cacheable 注解,其中缓存名称为 concurrenmapcache。 @Cacheable(value ="concurrenmapcache") publiclong getByCache(){ try{ Thread.sleep(3*1000); }catch(InterruptedException e){ e.printStackTrace(); } returnnewTimestamp(System.currentTimeMillis()).getTime(); } @CachePut 与 @Cacheable 类似,但是它无论什么情况,都会将方法的返回值放到缓存中, 主要用于数据新增和修改方法。 @CachePut(value ="concurrenmapcache") publiclong save(){ long timestamp =newTimestamp(System.currentTimeMillis()).getTime(); System.out.println("进行缓存:"+ timestamp); return timestamp; } @CacheEvict 将一条或多条数据从缓存中删除, 主要用于删除方法,用来从缓存中移除相应数据。 @CacheEvict(value ="concurrenmapcache") publicvoiddelete(){ System.out.println("删除缓存"); } 控制层 为了展现效果,我们先定义一组简单的 RESTful API 接口进行测试。 @RestController("concurrenmapcache.cacheController") @RequestMapping(value ="/concurrenmapcache/cache") publicclassCacheController{ @Autowired privateCacheService cacheService; /** * 查询方法 */ @RequestMapping(value ="", method =RequestMethod.GET) publicString getByCache(){ Long startTime =System.currentTimeMillis(); long timestamp =this.cacheService.getByCache(); Long endTime =System.currentTimeMillis(); System.out.println("耗时: "+(endTime - startTime)); return timestamp+""; } /** * 保存方法 */ @RequestMapping(value ="", method =RequestMethod.POST) publicvoid save(){ this.cacheService.save(); } /** * 删除方法 */ @RequestMapping(value ="", method =RequestMethod.DELETE) publicvoiddelete(){ this.cacheService.delete(); } } 运行 @RestController @EnableAutoConfiguration @ComponentScan(basePackages ={"com.lianggzone.springboot"}) publicclassWebMain{ publicstaticvoid main(String[] args)throwsException{ SpringApplication.run(WebMain.class, args); } } 课后作业 我们分为几个场景进行测试。 多次调用查询接口,查看缓存信息是否变化,控制台日志是否如下?你得到的结论是什么? 调用保存接口,再调用查询接口,查看缓存信息是否变化?你得到的结论是什么? 调用删除接口,再调用查询接口,接口响应是否变慢了?你再看看控制台日志,你得到的结论是什么? 扩展阅读 如果想更深入理解 Spring 的 Cache 机制,这边推荐两篇不错的文章。 Spring Cache 抽象详解 Spring 4.1 新特性 - Spring 缓存框架增强 源代码 Spring Boot In Practice:Redis缓存实战 阅读本文需要对Spring和Redis比较熟悉。 Spring Framework 提供了Cache Abstraction对缓存层进行了抽象封装,通过几个annotation可以透明给您的应用增加缓存支持,而不用去关心底层缓存具体由谁实现。目前支持的缓存有java.util.concurrent.ConcurrentMap,Ehcache 2.x,Redis等。 一般我们使用最常用的Redis做为缓存实现(Spring Data Redis), 需要引入的starter:spring-boot-starter-data-redis,spring-boot-starter-cache; 自动配置生成的Beans:RedisConnectionFactory,StringRedisTemplate,RedisTemplate,RedisCacheManager,自动配置的Bean可以直接注入我们的代码中使用; I. 配置 application.properties # REDIS (RedisProperties) spring.redis.host=localhost # Redis server host. spring.redis.port=6379 # Redis server port. spring.redis.password= # Login password of the redis server. 具体对Redis cluster或者Sentinel的配置可以参考这里 开启缓存支持 @SpringBootApplication @EnableCaching//开启caching public class NewsWebServer { //省略内容 } 定制RedisTemplate 自动配置的RedisTemplate并不能满足大部分项目的需求,比如我们基本都需要设置特定的Serializer(RedisTemplate默认会使用JdkSerializationRedisSerializer)。 Redis底层中存储的数据只是字节。虽然Redis本身支持各种类型(List, Hash等),但在大多数情况下,这些指的是数据的存储方式,而不是它所代表的内容(内容都是byte)。用户自己来决定数据如何被转换成String或任何其他对象。用户(自定义)类型和原始数据类型之间的互相转换通过RedisSerializer接口(包org.springframework.data.redis.serializer)来处理,顾名思义,它负责处理序列化/反序列化过程。多个实现可以开箱即用,如:StringRedisSerializer和JdkSerializationRedisSerialize。Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer来处理JSON格式的数据。请注意,存储格式不仅限于value 它可以用于key,Hash的key和value。 声明自己的RedisTemplate覆盖掉自动配置的Bean: //通用的RedisTemplate @Bean public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(jedisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //template.setHashKeySerializer(template.getKeySerializer()); //template.setHashValueSerializer(template.getValueSerializer()); return template; } 这里我们使用GenericJackson2JsonRedisSerializer而不是Jackson2JsonRedisSerializer,后者的问题是你需要为每一个需要序列化进Redis的类指定一个Jackson2JsonRedisSerializer因为其构造函数中需要指定一个类型来做反序列化: redis.setValueSerializer(new Jackson2JsonRedisSerializer<Product>(Product.class)); 如果我们应用中有大量对象需要缓存,这显然是不合适的,而前者直接把类型信息序列化到了JSON格式中,让一个实例可以操作多个对象的反序列化。 定制RedisCacheManager 有时候Spring Boot自动给我们配置的RedisCacheManager也不能满足我们应用的需求,我看到很多用法都直接声明了一个自己的RedisCacheManager,其实使用CacheManagerCustomizer可以对自动配置的RedisCacheManager进行定制化: @Bean public CacheManagerCustomizer<RedisCacheManager> cacheManagerCustomizer() { return new CacheManagerCustomizer<RedisCacheManager>() { @Override public void customize(RedisCacheManager cacheManager) { cacheManager.setUsePrefix(true); //事实上这是Spring Boot的默认设置,为了避免key冲突 Map<String, Long> expires = new HashMap<>(); expires.put("myLittleCache", 12L*60*60); // 设置过期时间 key is cache-name expires.put("myBiggerCache", 24L*60*60); cacheManager.setExpires(expires); // expire per cache cacheManager.setDefaultExpiration(24*60*60);// 默认过期时间:24 hours } }; } II. 使用 缓存Key的生成 我们都知道Redis是一个key-value的存储系统,无论我们想要缓存什么值,都需要制定一个key。 @Cacheable(cacheNames = "user") public User findById(long id) { return userMapper.findById(id); } 上面的代码中,findById方法返回的对象会被缓存起来,key由默认的org.springframework.cache.interceptor.SimpleKeyGenerator生成,生成策略是根据被标注方法的参数生成一个SimpleKey对象,然后由RedisTemplate中定义的KeySerializer序列化后作为key(注意StringRedisSerializer只能序列化String类型,对SimpleKey对象无能为力,你只能定义其他Serializer)。 不过大多数情况下我们都会采用自己的key生成方案,方式有两种: 1.实现自己的KeyGenerator; @Configuration @EnableCaching public class CacheConfig extends CachingConfigurerSupport { @Bean public KeyGenerator customKeyGenerator() { return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); } }; } } 2.在@Cacheable标注中直接声明key: @Cacheable(cacheNames = "user", key="#id.toString()") ❶ public User findById(long id) { return userMapper.findById(id); } @Cacheable(cacheNames = "user", key="'admin'") ❷ public User findAdmin() { return userMapper.findAdminUser(); } @Cacheable(cacheNames = "user", key="#userId + ':address'") ❸ public List<Address> findUserAddress(long userId) { return userMapper.findUserAddress(userId); } key的声明形式支持SpEL。❶ 最终生成的Redis key为:user:100234,user部分是因为cacheManager.setUsePrefix(true),cacheName会被添加到key作为前缀避免引起key的冲突。之所以#id.toString()要long型转为String是因为我们设置的KeySerializer为StringRedisSerializer只能用来序列化String。❷ 如果被标注方法没有参数,我们可以用一个静态的key值,最终生成的key为user:admin。❸ 最终生成的key为user:100234:address。 这种方式更符合我们以前使用Redis的习惯,所以推荐。 直接使用RedisTemplate 有时候标注不能满足我们的使用场景,我们想要直接使用更底层的RedisTemplate。 @Service public class FeedService { @Resource(name="redisTemplate") ❶ private ZSetOperations<String, Feed> feedOp; public List<Feed> getFeed(int count, long maxId) { return new ArrayList<>(feedOp.reverseRangeByScore(FEED_CACHE_KEY, 0, maxId, offset, count)); } //省略 } ❶ 我们可以直接把RedisTemplate的实例注入为ZSetOperations、ListOperations、ValueOperations等类型(Spring IoC Container帮我们做了转化工作,可以参考org.springframework.data.redis.core.ZSetOperationsEditor)。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册