@Cacheable注解在spring3中的使用-实现缓存

本文介绍了Spring3.1如何使用@Cacheable注解实现缓存,以此降低业务逻辑与缓存的耦合。通过在方法上添加@Cacheable,方法的返回值会被缓存。@Cacheable的value参数指定缓存位置,key参数可自定义,通常使用方法签名或SpEL表达式来生成。示例中,#surname参数用于生成缓存key。


       在软件开发中使用缓存已经有一个非常久的历史了。缓存是一种很好的设计思想,一旦你用了他,你将会发现他确实很有用。Spring3.1版本的核心对缓存做了实现。在Java推出Annotation特性之前,实现缓存的一个难点在于它与业务逻辑代码的耦合性太强。
       然而,Spring3.1中使用@Cacheable 和@CacheEvict实现缓存在某种程度上解决了这个问题,基本思想是在方法加上@Cacheable注解,这个方法的返回值
将具有缓存特性。
       @Cacheable注解可以用在方法或者类级别。当他应用于方法级别的时候,就是如上所说的缓存返回值了。当应用在类级别的时候,这个类的所有方法的
返回值都将被缓存。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@Cacheable (value =  "employee" )
public class EmployeeDAO {
 
   public Person findEmployee(String firstName, String surname,  int age) {
 
     return new Person(firstName, surname, age);
   }
 
   public Person findAnotherEmployee(String firstName, String surname,  int age) {
 
     return new Person(firstName, surname, age);
   }
}

       @Cacheable注解有三个参数,value是必须的,还有key和condition。第一个参数,也就是value指明了缓存将被存到什么地方

?
1
2
3
4
5
@Cacheable (value =  "employee" )
  public Person findEmployee(String firstName, String surname,  int age) {
 
    return new Person(firstName, surname, age);
  }

       任何存储在缓存中的数据为了高速访问都需要一个key。Spring默认使用被@Cacheable注解的方法的签名来作为key,当然你可以重写key,自定义key可以使用SpEL表达式。

?
1
2
3
4
<span style= "font-size:14px;" > @Cacheable (value =  "employee" , key =  "#surname" )</span>    public Person findEmployeeBySurname(String firstName, String surname,  int age) {
 
     return new Person(firstName, surname, age);
   }

       在findEmployeeBySurname()的注解中"#surname"是一个SpEL表达式,他将使用findEmployeeBySurname()方法中的surname参数作为key。


       @Cacheable的最后一个参数是condition(可选),同样的,也是引用一个SpEL表达式。但是这个参数将指明方法的返回结果是否被缓存。

?
1
2
3
4
5
@Cacheable (value =  "employee" , condition =  "#age < 25" )
  public Person findEmployeeByAge(String firstName, String surname,  int age) {
 
    return new Person(firstName, surname, age);
  }

上面的例子中,只有年龄小于25的时候才被缓存。

在快速看完了如何使用缓存后,我们接下来看看缓存带来的效果。
?
1
2
3
4
5
6
7
8
@Test
   public void testCache() {
 
     Person employee1 = instance.findEmployee( "John" "Smith" 33 );
     Person employee2 = instance.findEmployee( "John" "Smith" 33 );
 
     assertEquals(employee1, employee2);
   }

       上面的例子很简单,第一次调用findEmployee,findEmployee方法将被执行,Spring将他的返回值一个person对象存入缓存。第二次调用findEmployee的时候findEmployee将不被执行,Spring直接将缓存中的数据作为返回值返回。所以employee1 和employee2引用了同样的对象。

       而下面的例子中,我们将年龄小于25作为缓存条件,就将得到不同的结果。
?
1
2
3
4
5
6
7
8
@Test
  public void testCacheWithAgeAsCondition() {
 
    Person employee1 = instance.findEmployeeByAge( "John" "Smith" 33 );
    Person employee2 = instance.findEmployeeByAge( "John" "Smith" 33 );
 
    assertEquals(employee1, employee2);
  }

       下面的例子我们在findEmployeeBySurname的方法的注解中自定义了key,我们使用了自定义的key生成方式,以确保不同的surname将会指向不同的人。看下面的程序:

?
1
2
3
4
5
6
7
8
@Test
  public void testCacheOnSurnameAsKey() {
 
    Person employee1 = instance.findEmployeeBySurname( "John" "Smith" 22 );
    Person employee2 = instance.findEmployeeBySurname( "Jack" "Smith" 55 );
 
    assertEquals(employee1, employee2);
  }

       我们想找到两个不同的人,但是两个人的surname是相同的,你将发现两次调用返回了相同的结果,这不是Spring的问题,而是我们的cache key的生成方式有问题。所以在我们定义key的时候要小心注意key的生成策略,避免造成这种问题。

        最后总结一下流程,当执行到一个被@Cacheable注解的方法时,Spring首先检查condition条件是否满足,如果不满足,执行方法,返回;如果满足,在value所命名的缓存空间中查找使用key存储的对象,如果找到,将找到的结果返回,如果没有找到执行方法,将方法的返回值以key-对象的方式存入value缓存中,然后方法返回。

上文仅仅是@Cacheable的使用方法,但是我们怎么使用@CacheEvict注解来清除缓存呢?另外,还有一个问题,如何选择一个缓存的实现,并配置Spring的缓存呢?欲知后事如何,且听下回分解。
<think>首先,用户的问题是关于Java的@Cacheable注解缓存的对象是否需要实现Serializable接口。我需要基于提供的引用内容来回答。 回顾引用内容: - 引用[1]:提到“缓存的对象必须实现Serializable接口”,并给出了一个WorkerDTO类实现Serializable的例子。 - 引用[2]:解释了@Cacheable注解的基本行为,检查缓存是否存在相同key的元素,避免重复执行方法。 - 引用[3]:讨论了@Cacheable使用,但指出在实际项目中可能不会直接这样使用,而是通过工具类生成缓存- 引用[4]:展示了使用@Cacheable注解的例子,value和key的用法,但未直接提到Serializable。 关键点是引用[1]明确说“缓存的对象必须实现Serializable接口”,这直接回答了用户的问题。 用户的问题:使用@Cacheable注解时,缓存的对象是否需要实现Serializable接口。 根据引用[1],是的,缓存的对象必须实现Serializable接口。 现在,我需要构建回答: 1. **回答结构清晰**:逐步解决问题。先直接回答问题,然后解释原因,最后给出建议或示例。 2. **使用中文**:整个回答必须用中文。 3. **引用标识**:在引用的内容末尾自然地添加引用标识,如[^1]。 4. **生成相关问题**:在回答结束后,生成相关问题。 回答框架: - **开头**:直接回答问题。 - **解释**:基于引用解释为什么需要实现Serializable接口。 - **示例**:提供代码示例或参考引用中的例子。 - **总结**:简要总结。 然后,生成相关问题。 直接回答:是的,缓存的对象必须实现Serializable接口。因为缓存通常涉及序列化和反序列化,尤其是在使用Redis等外部缓存时。 引用[1]提供了例子:WorkerDTO实现了Serializable接口。 引用[4]展示了@Cacheable使用,但没有提到Serializable,所以主要依赖引用[1]。 其他引用:引用[2]解释了@Cacheable的行为,引用[3]讨论了实际使用,但不直接相关。 所以,核心是引用[1]。 回答内容: - 是的,使用@Cacheable注解时,缓存的对象必须实现Serializable接口。这是因为缓存机制需要将对象序列化存储到缓存中,例如使用Redis时,对象必须可序列化[^1]。 - 例如,在引用[1]中,WorkerDTO类实现了Serializable接口。 - 如果不实现Serializable接口,在尝试缓存对象时可能会抛出异常,因为序列化失败。 - 建议:在定义DTO或其他缓存对象时,总是实现Serializable接口以确保兼容性。 生成相关问题: 基于主题,生成相关问题。例如: 1. 如何优化@Cacheable的性能? 2. 在Spring中,有哪些其他缓存注解3. 如何处理缓存失效? 参考用户问题:用户问的是Serializable接口,所以相关问题可以围绕缓存实现、序列化问题等。 系统级指令: - 行内数学表达式用$...$,但这里没有数学内容,所以可能不需要。 - 独立公式用$$...$$,但这里没有。 - LaTeX语法正确:确保如果使用数学表达式,格式正确。但本问题不涉及数学。 - 使用中文回答。 - 生成相关问题:在回答结束后添加。 - 引用标识:在引用内容末尾添加,如[^1]。 在回答中,引用[1]时添加[^1]。 最终回答结构: 1. 回答问题。 2. 解释。 3. 示例或参考。 4. 总结。 5. 相关问题。 相关问题格式: §§相关问题§§ 1. ... 2. ... 3. ...</think>### Java @Cacheable 注解使用缓存对象是否需要实现 Serializable 接口 是的,在使用 Spring 的 `@Cacheable` 注解时,**缓存的对象必须实现 `Serializable` 接口**。这是因为缓存机制通常需要将对象序列化后存储到外部缓存系统(如 Redis),以便在读取时反序列化回对象形式。如果对象未实现 `Serializable` 接口,尝试缓存时会抛出序列化异常,导致缓存失败[^1]。 #### 为什么需要实现 Serializable 接口? - **序列化要求**:Spring缓存抽象层(如 Redis 实现)在存储对象前会执行序列化操作。序列化是将对象转换为字节流的过程,只有实现了 `Serializable` 接口的类才能被 Java 序列化机制处理。如果对象不可序列化,缓存操作会失败并抛出 `SerializationException` 或类似错误[^1]。 - **实际应用场景**:例如,在使用 Redis 作为缓存后端时,对象必须可序列化才能被存储和检索。引用中的示例展示了 `WorkerDTO` 类实现了 `Serializable` 接口,以确保缓存正常工作: ```java @Data public class WorkerDTO implements Serializable { @Schema(description = "司机工号") private String workNo; @Schema(description = "司机姓名") private String workName; } ``` 这里,`WorkerDTO` 被标记为可序列化,使其适用于 `@Cacheable` 缓存操作[^1]。 #### 使用 `@Cacheable` 的注意事项 - **注解配置**:`@Cacheable` 的 `value` 和 `key` 属性用于指定缓存名称和键值,但对象序列化是底层要求,与注解配置无关。例如: ```java @Cacheable(value = "product:seckill", key = "&#39;list&#39;") public List<PromotionProductVo> findPromotionSeckill() { // 方法逻辑 } ``` 即使正确配置了 `value` 和 `key`,如果 `PromotionProductVo` 未实现 `Serializable` 接口,缓存仍会失败[^4]。 - **性能与兼容性**:实现 `Serializable` 接口是基本要求,但在实际项目中,缓存对象的设计应考虑序列化性能(如避免大对象或循环引用)。此外,Spring 缓存抽象支持多种后端(如 Ehcache 或 Caffeine),但大多数外部缓存(如 Redis)都依赖序列化[^2][^3]。 #### 建议 - **最佳实践**:在定义任何可能被缓存的 DTO 或实体类时,始终实现 `Serializable` 接口。这可以避免运行时错误,并确保缓存一致性。 - **异常处理**:如果遇到序列化问题,检查缓存对象是否遗漏了 `implements Serializable`,或使用工具(如 `SerializableUtils`)测试序列化兼容性。 - **替代方案**:如果对象无法实现 `Serializable`(如第三方库类),可以考虑使用自定义序列化器或转换器,但这会增加复杂性,通常不推荐[^3]。 总之,实现 `Serializable` 接口是使用 `@Cacheable` 注解的必要条件,以确保缓存操作的正确性和可靠性[^1][^4]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值