注解类
我们最常见的使用的是@Transactional,这个注解可以在代码出异常的时候进行事务回滚的操作,我们也可以写一个注解类搭配AOP切面帮助我们实现一些特定的业务需求,如日志的打印,记录用户的操作,搭配缓存解决一些寻常的查询业务等,下面我使用一个实例来整一个简单的注解搭配AOP切面实现的缓存查询方法
/**
* 准备自定义注解类 参照@Transactional
* Target:表示注解所能标注的地方,通常是标注在方法上的
* Retention:表示这个注解其效果的位置,一般我们让他在编译后也启效果
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheDemo {
String prefix() default "cache";
}
/**
* 编写我们自定义的AOP切面实现类
* Aspect:当前类为切面实现类
* Component:当前类加入到SpeingIOC容器中 实例化
* Slf4j:常用的日志打印lombok包
* 缓存我们采用的为redis 分布式锁使用redissonClient
*/
@Aspect
@Component
@Slf4j
public class CacheAspect{
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
/**
* Around()环绕式通知
* 一般方法我们execution(public * *.*(..)) 包+类+方法+形参
* 但是我们今天使用注解使其生效采用annotation 看哪个包类方法的上面打有次注解 就执行这个切面
* ProceedingJoinPoint切点 方法和切面来回切换
*/
@Around(value = "@annotation()")
public Object cacheAspectMethod(ProceedingJoinPoint proceedingJoinPoint){
//准备工作 因为我们这个切面是服务任何方法的 所以当有一个方法执行切面时我们得的到这个方法的一些东西
//1)获取到方法
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
//2)获得方法上的注解
//获取方法对象
Method method = signature.getMethod();
//3)方法获取注解 指定注解 为了获取前缀
CacheDemo cache = method.getAnnotation(CacheDemo.class);
//4)方法返回值类型
Class returnType = signature.getReturnType();
//形参
Object[] args = proceedingJoinPoint.getArgs();
//注解写的前缀和形参拼接成的key
String cacheKey = gmallCache.prefix() + Arrays.asList(args);
//1.先查询缓存
Object obj = redisTimplate.opsForValue().get(cacheKey);
if(o == null){
//2.缓存为空,查询DB
String cacheLockKey = cache.prefix() + Arrays.asList(args) + ":inif";
//防止缓存击穿上分布式锁
RLock rLock = redissonClient.getLock(cacheLockKey);
//过期不候锁
try {
boolean tryLock = rLock.tryLock(1,6, TimeUnit.SECONDS);
if (tryLock){
//获取到锁
try {
//执行主方法查询DB
Object o1 = proceedingJoinPoint.proceed(args);
//3.保存缓存一份
if (null != o1){
//查询DB不为空
redisTemplate.opsForValue().set(cacheKey,
o1,RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(300)
,TimeUnit.SECONDS);
}else {
//缓存穿透 查询DB为空
o1 = returnType.getDeclaredConstructor().newInstance();
redisTemplate.opsForValue().set(cacheKey,
o1,5,TimeUnit.SECONDS);
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}finally {
rLock.unlock();
}
}else {
//未获取到锁
try {
TimeUnit.SECONDS.sleep(2);
return redisTemplate.opsForValue().get(cacheKey);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
//4.返回
log.info("返回了:cacheKey:{} :缓存中有数据.", JSONObject.toJSONString(o));
return o;
}
}
/**
* 方法如何使用
*/
public class Demo{
@CacheDemo(prefix = "User:")
public User getBaseCategoryView(Long id) {
return UserMapper.selectById(id);
}
}
版权声明:本文为h15835161250原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。