springboot利用AOP自定义注解的方式提供redis缓存

 

  1. 添加pom文件支持

    
    
      4.0.0
    ​
      com.sg
      cristina
      0.0.1-SNAPSHOT
      cristina
      El psy congroo
    ​
      
        org.springframework.boot
        spring-boot-starter-parent
        2.0.3.RELEASE
         
      
    ​
    ​
      
        UTF-8
        UTF-8
        1.8
      
    ​
      
        
          commons-io
          commons-io
          2.4
        
    ​
    ​
        
          commons-lang
          commons-lang
          2.5
        
    ​
        
          com.alibaba
          druid
          1.1.9
        
    ​
        
          com.alibaba
          fastjson
          1.2.46
        
    ​
        
          org.springframework.boot
          spring-boot-starter-web
        
    ​
        
          com.github.pagehelper
          pagehelper
          4.0.0
        
    ​
        
        
        
          org.apache.zookeeper
          zookeeper
          3.4.8
        
        
          com.github.sgroschupf
          zkclient
          0.1
        
        
        
          com.alibaba
          dubbo
          2.8.4
        
        
        
          org.springframework.boot
          spring-boot-configuration-processor
          true
        
    ​
        
          org.springframework.boot
          spring-boot-starter-test
          test
        
    ​
    ​
        
          org.mybatis.spring.boot
          mybatis-spring-boot-starter
          1.3.2
        
    ​
        
          mysql
          mysql-connector-java
          
        
    ​
        
          org.projectlombok
          lombok
          1.16.22
        
    ​
        
          io.springfox
          springfox-swagger2
          2.7.0
        
    ​
        
          io.springfox
          springfox-swagger-ui
          2.7.0
        
    ​
        
          org.aspectj
          aspectjweaver
          1.8.13
        
    ​
    ​
        
          org.springframework.boot
          spring-boot-starter-data-redis
        
    ​
        
          redis.clients
          jedis
          2.8.1
        
    ​
    ​
    ​
        
          org.apache.httpcomponents
          httpclient
          4.5
        
    ​
      
    ​
    ​
      
        
          
            org.springframework.boot
            spring-boot-maven-plugin
          
          
          
            org.mybatis.generator
            mybatis-generator-maven-plugin
            1.3.2
            
              ${basedir}/src/main/resources/generator/generatorConfig.xml
              true
              true
            
          
        
      
  2. springboot开启AOP

  3. @EnableAspectJAutoProxy
    @SpringBootApplication
    @MapperScan("com.sg.cristina.dao")
    public class CristinaApplication {
    ​
      public static void main(String[] args) {
        SpringApplication.run(CristinaApplication.class, args);
      }
    ​
    }
  4. 自定义用于添加缓存的注解和用户删除缓存的注解

    /**
     * Created by jw on 19/3/29.
     * @usage  缓存注解类
     */
    ​
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Documented
    public @interface Cacheable {
            String key();
            String fieldKey() ;
            int expireTime() default 3600;
    ​
    ​
    }
    ​
    ​
    /**
     * Created by jw on 19/3/29.
     *
     * @usage 清除过期缓存注解,放置于update delete insert 类型逻辑之上
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface CacheEvict {
        String key();
        String fieldKey() ;
        int expireTime() default 3600;
    ​
    }
    ​

     

  5. 切面逻辑

    import java.lang.reflect.Method;
    ​
    /**
     * Created by jw on 19/3/29.
     */
    @Aspect
    @EnableAspectJAutoProxy
    @Component
    public class RedisCacheAspect {
    ​
        private static final Logger logger = Logger.getLogger(RedisCacheAspect.class);
        /**
         * 分隔符
         **/
        private static final String DELIMITER = "|";
    ​
    ​
        @Autowired
        RedisUtil redisUtil;
    ​
        /**
         * Service层切点 使用到了我们定义的 Cacheable 作为切点表达式。
         * 而且我们可以看出此表达式基于 annotation。
         * 并且用于内建属性为查询的方法之上
         */
        @Pointcut("@annotation(com.sg.cristina.config.aspect.Cacheable)")
        public void queryCache() {
        }
    ​
        /**
         * Service层切点 使用到了我们定义的 RedisEvict 作为切点表达式。
         * 而且我们可以看出此表达式是基于 annotation 的。
         * 并且用于内建属性为非查询的方法之上,用于更新表
         */
        @Pointcut("@annotation(com.sg.cristina.config.aspect.CacheEvict)")
        public void ClearCache() {
        }
    ​
    ​
        @Around("queryCache()")
        public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {
            Object result = null;
            System.out.println("query cache -------------------------------");
            Method method = getMethod(pjp);
            //获取被切方法的注解
            Cacheable cacheable = method.getAnnotation(Cacheable.class);
            //key的value
            String fieldKey = parseKey(cacheable.fieldKey(), method, pjp.getArgs());
            //使用redis 的hash进行存取,类似于一张表
            result = redisUtil.hget(cacheable.key(), fieldKey);
            if (result == null) {
            //如果缓存没有则执行原本逻辑
                System.out.println("cache is empty ,query db--------");
                try {
                    result = pjp.proceed();
                    System.out.println("get result from db ----------");
                    //从db查询到了则再加入缓存
                    if (result != null) {
                        redisUtil.hset(cacheable.key(), fieldKey, result);
                        System.out.println("set result to cache ----------");
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
            return result;
        }
    ​
        /*** 定义清除缓存逻辑*/
        @Around(value = "ClearCache()")
        public Object evict(ProceedingJoinPoint pjp) throws Throwable {
            Object result = null;
            System.out.println("clear cache ---------------");
            Method method = getMethod(pjp);
            CacheEvict cacheEvict = method.getAnnotation(CacheEvict.class);
            String fieldKey = parseKey(cacheEvict.fieldKey(), method, pjp.getArgs());
            //先删除缓存
            redisUtil.hdel(cacheEvict.key(), fieldKey);
            //然后操作db
            result = pjp.proceed();
    ​
            return result;
        }
    ​
        /**
         * 获取被拦截方法对象
         * 

        * MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象     * 而缓存的注解在实现类的方法上     * 所以应该使用反射获取当前对象的方法对象     */    public Method getMethod(ProceedingJoinPoint pjp) {        //获取参数的类型        Object[] args = pjp.getArgs();        Class[] argTypes = new Class[pjp.getArgs().length];        for (int i = 0; i < args.length; i++) {            argTypes[i] = args[i].getClass();       }        Method method = null;        try { ​            Object target = pjp.getTarget();            Class aClass = target.getClass();            Signature signature = pjp.getSignature();            method = aClass.getMethod(signature.getName(), argTypes);       } catch (NoSuchMethodException e) {            e.printStackTrace();            logger.error(e.getMessage());       } catch (SecurityException e) {            e.printStackTrace();            logger.error(e.getMessage());       }        return method; ​   } ​    /**     * 获取缓存的key     * key 定义在注解上,支持SPEL表达式     *     * @param     * @return     */    private String parseKey(String key, Method method, Object[] args) { ​        //获取被拦截方法参数名列表(使用Spring支持类库)        LocalVariableTableParameterNameDiscoverer u =                new LocalVariableTableParameterNameDiscoverer();        String[] paraNameArr = u.getParameterNames(method); ​        //使用SPEL进行key的解析        ExpressionParser parser = new SpelExpressionParser();        //SPEL上下文        StandardEvaluationContext context = new StandardEvaluationContext();        //把方法参数放入SPEL上下文中        for (int i = 0; i < paraNameArr.length; i++) {            context.setVariable(paraNameArr[i], args[i]);       }        return parser.parseExpression(key).getValue(context, String.class);   } ​ ​ }

     

  6. 注解使用方式及注意事项

    1. 这种注解方式适用于单表的增删改查,一般以id(唯一标示)作为缓存的要素,如果将连表查询的结果缓存到redis那么对缓存的对象有一定的要求,如:读的频率高,且改动少。

    2. 本次采用的缓存逻辑是在service层对select类方法进行先查缓存再查db处理,如果缓存没查到,那么执行db查询,然后将查到的结果缓存到redis,对update的方法和delete的方法统一采用先删除缓存然后修改db

    3. 使用方法参考:

      ​
      /**
       * @Author: jiangwei
       * @Date: 2019/4/20
       * @Desc:
       */
      ​
      @Service
      public class UserServiceImpl implements UserService {
      ​
      ​
          @Autowired
          SgUserMapper userMapper ;
          /**
           * @param sgId
           * @mbggenerated
           */
          @CacheEvict(key = "SgUser",fieldKey = "#sgId")
          @Override
          public int deleteByPrimaryKey(Integer sgId) {
              return userMapper.deleteByPrimaryKey(sgId);
          }
      ​
          /**
           * @param record
           * @mbggenerated
           */
          @Override
          public int insert(SgUser record) {
              return userMapper.insert(record);
          }
      ​
          /**
           * @param record
           * @mbggenerated
           */
          @CacheEvict(key = "SgUser",fieldKey = "#record.sgId")
          @Override
          public int insertSelective(SgUser record) {
              return userMapper.insertSelective(record);
          }
      ​
          /**
           * @param sgId
           * @mbggenerated
           */
          @Cacheable(key = "SgUser",fieldKey = "#sgId")
          @Override
          public SgUser selectByPrimaryKey(Integer sgId) {
              return userMapper.selectByPrimaryKey(sgId);
          }
      ​
          /**
           * @param record
           * @mbggenerated
           */
          @CacheEvict(key = "SgUser",fieldKey = "#record.sgId")
          @Override
          public int updateByPrimaryKeySelective(SgUser record) {
              return userMapper.updateByPrimaryKeySelective(record);
          }
      ​
          /**
           * @param record
           * @mbggenerated
           */
          @CacheEvict(key = "SgUser",fieldKey = "#record.sgId")
          @Override
          public int updateByPrimaryKey(SgUser record) {
              return userMapper.updateByPrimaryKey(record);
          }
      ​
          @Override
          public SgUser selectByGitId(String gitId) {
              return userMapper.selectByGitId(gitId);
          }
      }
      ​

       

 

你可能感兴趣的:(技术类,工具类,redis,aop,springboot,缓存)