AOP实战
2019-03-06 本文已影响0人
packet
想用AOP做一个缓存。虽然看起来简单,但真正做完还是有些收获。
@RestController
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping("/query")
public long query(@RequestParam(required = false) String userId) {
return accountService.queryMoney(userId);
}
}
@Service
public class AccountService {
@Cache
public long queryMoney(String userId) {
try {
System.out.println("start query money of " + userId);
Thread.sleep(5000); // 模拟数据库查询
long money = ThreadLocalRandom.current().nextLong(1000);
System.out.println(userId + "->" + money);
return money;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
}
读操作不能每次都查询,所以需要缓存。
先看下基础类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Cache {
}
public class Money {
private long num;
private long ttl;
public Money(long num, long ttl) {
this.num = num;
this.ttl = ttl;
}
public long getNum() {
return num;
}
public void setNum(long num) {
this.num = num;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
}
再看最重要的切面类:
@Component
@Aspect
public class WorkAspect {
private Map<String, Money> cache = new ConcurrentHashMap<>();
@Pointcut("@annotation(com.example.aop.Cache)")
public void point() {
}
@Around("point()")
public long query(ProceedingJoinPoint joinPoint) {
String userId = (String) joinPoint.getArgs()[0]; // 检查参数
if (StringUtils.isEmpty(userId)) {
return -1;
}
Money money = cache.get(userId);
if (money != null && System.currentTimeMillis() < money.getTtl()) {
return money.getNum();
}
long num = -1;
try {
num = (long) joinPoint.proceed();
cache.put(userId, new Money(num, System.currentTimeMillis() + 60000));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return num;
}
}
这个业务背景需要注意,因为查询的是钱,所以要求当金钱数量变化的时候,缓存中的数据需要淘汰或者更新。