java使用注解类实现功能小Demo

注解类

我们最常见的使用的是@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(obj == 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(obj));
        return obj;
	}
}
/**
* 方法如何使用
*/
public class Demo{

	@CacheDemo(prefix = "User:")
	public User getBaseCategoryView(Long id) {
        return UserMapper.selectById(id);
    }
}

你可能感兴趣的:(java,spring,spring,java)