Spring之路(47)–Spring编程式缓存管理实例

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文目录1. 编程式缓存管理2. 实现方式3. 项目准备4. 不使用缓存的情况4.1 定义数据DO、数据访问DAO、数据服务Service4.2 编写Spring配置类SpringConfig4.3 测试5. 编程式管理管理的情况5.1 修改配置类启用缓存并注册缓存管理器5.2 调用BlogService.getById时添加缓存逻辑5.3 测试

1. 编程式缓存管理

还记得之前讲过的编程式事务管理与声明式事务管理吗,编程式管理说白了就是自己手工编程去管理。


因为手工编程式的管理方式,更加基础,更加容易理解,所以我们从编程式缓存管理说起。


2. 实现方式

其实思路非常简单,缓存是针对方法的,我们将对方法的请求加入缓存中,如果再次对该方法发起同样请求(同一方法且参数相同),则不执行该方法,直接取出缓存即可。


3. 项目准备

为了可以使用AOP,再进行本文内容时,除了前面一直提到Spring的jar包,还需要引入aspectjweaver-1.8.1.jar,这个包是Spring AOP所需要的。


4. 不使用缓存的情况

4.1 定义数据DO、数据访问DAO、数据服务Service

首先定义BlogDao用来访问数据库,根据博客表的id获取博客信息


@Repository // 注册为bean

public class BlogDao {

@Autowired // 自动注入

private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

/**

 * 查询数据库

 */

public BlogDo getById(Long id) {

 System.out.println("执行getById:" + id);

 Map<String, Object> map = new HashMap<>();

 map.put("id", id);

 return namedParameterJdbcTemplate.queryForObject("select * from blog where id=:id", map,

   new RowMapper<BlogDo>() {

    @Override

    public BlogDo mapRow(ResultSet rs, int rowNum) throws SQLException {

     BlogDo blog = new BlogDo();

     blog.setAuthor(rs.getString("author"));

     blog.setContent(rs.getString("content"));

     blog.setId(rs.getLong("id"));

     blog.setTitle(rs.getString("title"));

     return blog;

    }

   });

}

}


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

然后定义BlogService提供操作封装。


@Service//注册bean

public class BlogService {

@Autowired//自动注入

private BlogDao blogDao;


public BlogDo getById(Long id) {

 return blogDao.getById(id);

}

}

1

2

3

4

5

6

7

8

9

数据对象BlogDo就不用多说了:


public class BlogDo {

private Long id;

private String title;

private String author;

private String content;

//省略get set

}

1

2

3

4

5

6

7

4.2 编写Spring配置类SpringConfig

在配置类中开启bean扫描,以便将BlogService和BlogDao注册bean,同时注册数据源bean和namedParameterJdbcTemplate用于操作数据库。


@Configuration // 配置类

@ComponentScan(basePackages = { "org.maoge.cachedemo.mannual" }) // 扫描包以便发现注解配置的bean

public class SpringConfig {

// 配置数据源

@Bean

public DataSource dataSource() {

 DruidDataSource dataSource = new DruidDataSource();

 dataSource.setDriverClassName("com.mysql.jdbc.Driver");

 dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8");

 dataSource.setUsername("root");

 dataSource.setPassword("Easy@0122");

 return dataSource;

}

// 配置namedParameterJdbcTemplate组件

@Bean

public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {

 NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource());// 注入dataSource

 return template;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

4.3 测试

从容器中取出BlogService,然后调用其getList方法进行测试:


public class Main {

public static void main(String[] args) throws SQLException {

 // 获取容器

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

 // 获取blogService组件

 BlogService blogService = context.getBean("blogService", BlogService.class);

 // 测试获取列表

 for(int i=0;i<10;i++) {

  blogService.getById(1L);

 }

}

}

1

2

3

4

5

6

7

8

9

10

11

12

输出结果如下,可见真实的调用了BlogDao的getById方法10次。


执行getById:1

执行getById:1

执行getById:1

执行getById:1

执行getById:1

执行getById:1

执行getById:1

执行getById:1

执行getById:1

执行getById:1

1

2

3

4

5

6

7

8

9

10

5. 编程式管理管理的情况

5.1 修改配置类启用缓存并注册缓存管理器

首先通过@EnableCaching 启用缓存功能。然后注册缓存管理器bean,我们通过缓存管理器对缓存进行管理。


@Configuration // 配置类

@ComponentScan(basePackages = { "org.maoge.cachedemo.mannual" }) // 扫描包以便发现注解配置的bean

@EnableCaching // 启用缓存

public class SpringConfig {

// 配置数据源

@Bean

public DataSource dataSource() {

 DruidDataSource dataSource = new DruidDataSource();

 dataSource.setDriverClassName("com.mysql.jdbc.Driver");

 dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/myblog?useUnicode=true&characterEncoding=utf-8");

 dataSource.setUsername("root");

 dataSource.setPassword("Easy@0122");

 return dataSource;

}

// 配置namedParameterJdbcTemplate组件

@Bean

public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {

 NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSource());// 注入dataSource

 return template;

}

// 配置缓存管理器

@Bean

public CacheManager cacheManager() {

 SimpleCacheManager cacheManager = new SimpleCacheManager();

 //缓存管理器中有很多缓存caches,其中一个名字为blogs

 cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("blogs")));

 return cacheManager;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

务必注意,我们像缓存管理其中添加了一个名字为blogs的ConcurrentMapCache类型对象,该对象是个字典形式的缓存,也就是说在blogs这个缓存,参数只要确定了,其缓存的值就确定了。


5.2 调用BlogService.getById时添加缓存逻辑

不存在缓存时,将结果放入缓存;存在缓存,则不再执行具体方法,直接返回缓存。


@Service//注册bean

public class BlogService {

@Autowired//自动注入

private BlogDao blogDao;

@Autowired//自欧东注入

private CacheManager cacheManager;


public BlogDo getById(Long id) {

 //取出名字为blogs的缓存

 Cache cache=cacheManager.getCache("blogs");

 //判断针对参数id的值,是否有缓存存在

 if(cache.get(id)==null) {//不存在,则查询数据库,并将结果纳入缓存

  BlogDo result=blogDao.getById(id);

  cache.put(id, result);

  return result;

 }else {//存在则直接返回缓存即可

  return (BlogDo)cache.get(id).get();//这个取值方式稍微有点绕

 }

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

看明白了吗,首先缓存管理器可能要管理很多个缓存,比如用户缓存、角色缓存、机构信息缓存等等,所以需要先定义有几个缓存Cache,此处只有一个就是cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("blogs")));。


然后,一个缓存其实就是一个线程安全的键值对,将方法的参数作为键,方法的返回值作为值,存储起来。当新的请求到达时,如果请求参数都相同,则不必再去查数据库了,直接返回缓存就行,反正请求的是一个东西。


5.3 测试

同样代码进行测试


public class Main {

public static void main(String[] args) throws SQLException {

 // 获取容器

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

 // 获取blogService组件

 BlogService blogService = context.getBean("blogService", BlogService.class);

 // 测试获取列表

 for(int i=0;i<10;i++) {

  blogService.getById(1L);

 }

}

}

1

2

3

4

5

6

7

8

9

10

11

12

结果输出如下:


执行getById:1

1

这意味着什么,我们成功的使用了缓存,对数据库的访问得到了极大的减少。如果使用得当的话,系统性能会得到一个跃进!


如果你不放心的话,可以下个断点,看看是不是返回的值是正确的。


上面的缓存方式太复杂了,你应该看到了模板代码的痕迹,凡是重复必能抽象优化,这种模板代码用AOP解决,SOEASY啊。


然后缓存其实还有不少注意点,我将在接下来给大家详细演示和解释。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
2月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
403 0
|
3月前
|
Java API 开发者
Spring 控制反转与依赖注入:从玄学编程到科学管理
在传统开发中,手动`new`对象导致紧耦合、难以维护和测试。控制反转(IoC)将对象创建交给框架,实现解耦。Spring通过IOC容器自动管理对象生命周期,开发者只需声明依赖,无需关心创建细节。依赖注入(DI)是IoC的具体实现方式,支持构造器、Setter和字段注入。构造器注入推荐使用,保证依赖不可变且易于测试。对于多个同类型Bean,可用`@Qualifier`或`@Primary`解决冲突。此外,Spring还支持依赖查找(DL),开发者主动从容器获取Bean,适用于动态场景,但侵入性强。掌握IoC与DI,有助于构建灵活、可维护的Spring应用。
|
1月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
2月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
517 5
|
2月前
|
存储 缓存 Java
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
缓存是提升应用性能的重要技术,Spring框架提供了丰富的缓存注解,如`@Cacheable`、`@CacheEvict`等,帮助开发者简化缓存管理。本文介绍了如何在Spring中配置缓存管理器,使用缓存注解优化数据访问,并探讨了缓存的最佳实践,以提升系统响应速度与可扩展性。
305 0
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
|
3月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。
|
6月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
237 32
|
4月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
422 0
|
缓存 JavaScript 搜索推荐
vue中的一个内置组件Keep-Alive的作用及使用方法介绍——缓存不活动的组件实例
vue中的一个内置组件Keep-Alive的作用及使用方法介绍——缓存不活动的组件实例
882 1