Java设计模式之修饰模式 基于APO实现

  • 基于装饰模式实现多级缓存策略
  • 装饰模式应用场景
  • 二、代码实现 (+AOP 实现多级缓存)
  • 1 Redis 工具类 RedisUtils
  • 2基于jvm缓存 JvmMapCacheUtils
  • 3.抽象组件:定义一个抽象接口,来规范准备附加功能的类 ComponentCache
  • 4.将要被附加功能的类,实现抽象构件角色接口 AbstractDecorate
  • 5.持有对具体构件角色的引用并定义与抽象构件角色一致的接口 L1CacheService
  • 6.持有对具体构件角色的引用并定义与抽象构件角色一致的接口 RedisDecorateService
  • 6.基于aop拦截目标方法
  • 7.数据库
  • 8.接口测试
  • 8.测试结果 测试一级二级都没有数据
  • 总结
  • Link [design-decoration-pattern](https://gitee.com/wu_xiaoxi/springboot-family.git)


基于装饰模式实现多级缓存策略

重点:不改变原有代码的基础之上,新增附加功能

在实际开发项目,为了减少数据库的访问压力,我们都会将数据缓存到内存中
比如:Redis(分布式缓存)、EHCHE(JVM内置缓存).
例如在早起中,项目比较小可能不会使用Redis做为缓存,使用JVM内置的缓存框架,
项目比较大的时候开始采用Redis分布式缓存框架,这时候需要设计一级与二级缓存。

java 缓存如何做分页 java缓存设计_aop

装饰模式应用场景

**多级缓存设计、mybatis中一级与二级缓存、IO流**

二、代码实现 (+AOP 实现多级缓存)

1 Redis 工具类 RedisUtils

@Component
public class RedisUtils {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 如果key存在的话返回fasle 不存在的话返回true
    public Boolean setNx(String key, String value, Long timeout) {
        Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
        return setIfAbsent;
    }

    /**
     * 存放string类型
     *
     * @param key     key
     * @param data    数据
     * @param timeout 超时间
     */
    public void setString(String key, String data, Long timeout) {
        stringRedisTemplate.opsForValue().set(key, data);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
        }
    }

    /**
     * 存放string类型
     *
     * @param key  key
     * @param data 数据
     */
    public void setString(String key, String data) {
        setString(key, data, null);
    }

    /**
     * 根据key查询string类型
     *
     * @param key
     * @return
     */
    public String getString(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return value;
    }

    public <T> T getEntity(String key, Class<T> t) {
        String json = getString(key);
        return JSONObject.parseObject(json, t);
    }

    public void putEntity(String key, Object object) {
        String json = JSONObject.toJSONString(object);
        setString(key, json);
    }

    /**
     * 根据对应的key删除key
     *
     * @param key
     */
    public boolean delKey(String key) {
        return stringRedisTemplate.delete(key);
    }


    public void setList(String key, List<String> listToken) {
        stringRedisTemplate.opsForList().leftPushAll(key, listToken);
    }

    public StringRedisTemplate getStringRedisTemplate() {
        return stringRedisTemplate;
    }
}

2基于jvm缓存 JvmMapCacheUtils

/**
 * @Author : sean
 * @DateTime : 2021/8/3、22:27
 * @Description : 缓存基础缓存
 **/
public class JvmMapCacheUtils {

    /**
     * 缓存容器
     */
    private static Map<String, String> caches = new ConcurrentHashMap<>();

    public static  <T> T getEntity(String key, Class<T> t) {
        // 缓存存放对象的情况
        String json = caches.get(key);
        return JSONObject.parseObject(json, t);
    }

    public static void putEntity(String key, Object o) {
        String json = JSONObject.toJSONString(o);
        caches.put(key, json);
    }
}

3.抽象组件:定义一个抽象接口,来规范准备附加功能的类 ComponentCache

/**
 * @author: Sean
 * @time: 2021/8/4
 * @description: 定义共同规范,最初开始定义好
 */
public abstract class ComponentCache {

    /**
     * 共同方法
     * @param <T>
     * @return
     */
    public abstract  <T> T getCacheEntity(String key,Class<T> t, ProceedingJoinPoint joinPoint);
}

4.将要被附加功能的类,实现抽象构件角色接口 AbstractDecorate

/**
 * @author: Sean
 * @time: 2021/8/4
 * @description: 抽象装饰类,针对一级缓存增强
 */
public abstract class AbstractDecorate extends ComponentCache {

}

5.持有对具体构件角色的引用并定义与抽象构件角色一致的接口 L1CacheService

/**
 * @author: Sean
 * @time: 2021/8/4
 * @description: 一级缓存 装饰类
 */
@Component
public class L1CacheService extends ComponentCache {

    @Override
    public <T> T getCacheEntity(String key,Class<T> t, ProceedingJoinPoint joinPoint) {

        //从以一级缓存jvm(new Map)中找,是否有,
        T gatewayHandlerEntity = JvmMapCacheUtils.getEntity(key, t);

        if (gatewayHandlerEntity != null) {
            return gatewayHandlerEntity;
        }

        try {
              //一级缓存没有直接执行目标方法 查数据库 方法: gatewayHandlerDbService.getHandlerId(apiHandler

            Object result = joinPoint.proceed();

            JvmMapCacheUtils.putEntity(key, result);

            return (T) result;

        } catch (Throwable throwable) {

            throwable.printStackTrace();
            return null;
        }
    }
}

6.持有对具体构件角色的引用并定义与抽象构件角色一致的接口 RedisDecorateService

/**
 * @author: Sean
 * @time: 2021/8/4
 * @description: 二级缓存拓展 ,对最开始基本功能实现增强,二级缓存装饰类
 */
@Component
public class RedisDecorateService extends AbstractDecorate {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private L1CacheService l1CacheService;

    @Override
    public <T> T getCacheEntity(String key, Class<T> t, ProceedingJoinPoint joinPoint) {

        //查询耳机缓存
        T gatewayHandlerEntity = redisUtils.getEntity(key, t);

        if (gatewayHandlerEntity != null) {
            return gatewayHandlerEntity;
        }

        T handlerEntity = l1CacheService.getCacheEntity(key, t, joinPoint);
        
		//如果一级缓存查回来为空,执行aop目标方法,并把查回来的值保存到二级缓存中
        if (handlerEntity != null) {

            redisUtils.putEntity(key, handlerEntity);

            return handlerEntity;
        }

        return null;
    }
}

6.基于aop拦截目标方法

/**
 * 定义注解接口 缓存注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExitCache {
}
/**
 * @Author : sean
 * @DateTime : 2021/8/4、21:37
 * @Description : AOP 拦截注解
 **/
@Component
@Aspect
public class ExitAsyncAop {

    @Autowired
    private RedisDecorateService redisDecorateService;

    /**
     * 拦截方法上是否有缓存注解
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(com.sean.springboot.aop.ExitCache)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        //获取目标方法
        Method method = methodSignature.getMethod();

        String key = method.getName() + Arrays.toString(method.getParameterTypes()) + Arrays.toString(joinPoint.getArgs());

        //泛型:api 返回结果 method.getReturnType()
        return redisDecorateService.getCacheEntity(key, method.getReturnType(), joinPoint);
    }
}

7.数据库

java 缓存如何做分页 java缓存设计_java_02

8.接口测试

@RestController
@RequestMapping("/")
public class ModificationController {


    @Autowired
    private GatewayHandlerDbService gatewayHandlerDbService;

    @ExitCache
    @GetMapping("mod/{apiHandler}")
    public GatewayHandlerEntity modification(@PathVariable("apiHandler") String apiHandler) {

        return gatewayHandlerDbService.getHandlerId(apiHandler);
    }
}

8.测试结果 测试一级二级都没有数据

java 缓存如何做分页 java缓存设计_java 缓存如何做分页_03

1、调用接口,首先进入aop – 查询二级缓存 RedisDecorateService,
2、 如果有二级缓存没有 则取查询一级缓存 L1CacheService,
3、如果一级缓存没有则目标方法查询数据库 gatewayHandlerDbService.getHandlerId(apiHandler)
4、查询完数据库并set返回到一级缓存,set完后返回到 RedisDecorateService
5、把从数据库查回来的结果 set到二级缓存,最后直接return

总结

Link design-decoration-pattern