如何在项目中使用 aop 技术? [学习核心包系列一]

项目的核心代码都封装在核心包中. 本系列文章记录笔者阅读源码的过程和相应的实践.

1. 问题: 项目的本地缓存该如何设计?

项目的本地缓存使用了 aop + 自定义注解 方式实现.

原理如下:

  1. 通过一个注解 加上aop 把所有的自定义注解 全部拦截到一个方法里头
  2. 在这个方法里面 先进行查询 可以去redis 项目采用的caffeine
  3. 如果查到了 就直接返回数据 不执行数据库sql
  4. 如果查不到 就继续执行sql
  5. caffeine 相当于是一个高级map

2. 如何实现上述设计?

1
2
3
需求: 

定义一个方法根据小写字母生成大写字母. 定义一个 Map 保存已生成的数据, 如果 Map 已存在则直接打印, 不存在则调用方法获取.

2.1.1 在 pom.xml 中引入依赖, 生成一个项目

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

${springboot.version} 表示一个常量属性一般定义在 pom.xml. 多个依赖使用同一版本号避免冲突.

2.1.2 定义一个controller接口

1
2
3
4
@RequestMapping(value = "/getUpperCase/{str}")
public String getUpperCase(@PathVariable("str") String str) {
return userService.getUpperCase(str);
}

2.1.3 定义一个serive方法

1
2
3
4
5
6

public String getUpperCase(String str) {
System.out.println("进入方法了");
return str.toUpperCase(Locale.ROOT);
}

2.1.4 测试

2.1.5 增加自定义注解

1
2
public @interface CacheZJ {
}

2.1.6 增加缓存处理接口

1
2
3
4
5
6
7
8
9
10
11
12
public interface YxCacheHandle {
Object get(String var1);

void put(String var1, Object var2);

boolean delete(String var1);

String getCacheName();

Map<Object, Object> getCacheAll();
}

2.1.7 增加缓存接口实现

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
30
31
32
@Component
public class MapHandleImpl implements YxCacheHandle {
Map<String,Object> cacheMap = new HashMap<>();

@Override
public Object get(String var1) {
String result = (String)cacheMap.get(var1);
System.out.println("缓存取的数据:" + result);
return result;
}

@Override
public void put(String var1, Object var2) {
cacheMap.put(var1, var2);
}

@Override
public boolean delete(String var1) {
cacheMap.remove(var1);
return true;
}

@Override
public String getCacheName() {
return null;
}

@Override
public Map<Object, Object> getCacheAll() {
return null;
}
}

2.1.8 增加注解拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Aspect
@Component
public class CacheZJAspect {
@Resource
YxCacheHandle yxCacheHandle;

@Pointcut("@annotation(com.annotation.CacheZJ)")
public void cacheOpr() {
}

@Around("cacheOpr()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
String args = (String)pjp.getArgs()[0];
String result ;
if (yxCacheHandle.get(args) == null) {
result = (String) pjp.proceed();
yxCacheHandle.put(args,result);
}
return yxCacheHandle.get(args);
}

}

2.1.9 把注解加到 serive 方法上, 并测试

1
2
3
4
5
@CacheZJ
public String getUpperCase(String str) {
System.out.println("进入方法了");
return str.toUpperCase(Locale.ROOT);
}