JetCache中使用Cache注解缓存到远程redis以及踩过的坑

目前网上关于jetcache的使用大多是基于官网的解释,给初学者造成很大的困扰,这里就将我使用的过程中遇到的坑总结一下。

项目是一个springboot项目,目前需要在一个接口方法上加@cache注解,希望将方法返回的结果连同自定义的key一起存到远程redis中。在实现的过程中遇到了如下的问题:

1. 希望将方法中传入的参数经过处理后做为缓存的key,但是不知道jetcache中spel表达式如何调用方法。

2. 运行过程报错:org.springframework.expression.spel.SpelEvaluationException: EL1072E: An exception occurred whilst evaluating a compiled expression

最后运行成功的项目代码

1. pom.xml



    4.0.0

    com.example
    demo
    0.0.1-SNAPSHOT
    jar

    springboot-demo
    Demo project for Spring Boot

    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.6.RELEASE
         
    
    
        
            dev
            
                true
            
            
                com.mysql.jdbc.Driver
                jdbc:mysql://localhost:3306/test?allowMultiQueries=true
                root
                root
            
        
    

    
        UTF-8
        UTF-8
        1.8
        Finchley.SR1
    

    
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            1.3.2
        
        
            com.alibaba
            druid-spring-boot-starter
            1.1.10
        
        
            org.springframework.boot
            spring-boot-devtools
            true
        
        
            com.alicp.jetcache
            jetcache-starter-redis
            2.5.9
        
        
            org.apache.commons
            commons-lang3
            3.6
        
        
            org.apache.commons
            commons-collections4
            4.1
        
        
            mysql
            mysql-connector-java
            runtime
        
        
            org.apache.commons
            commons-lang3
            3.6
        
        
            commons-codec
            commons-codec
            1.10
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                        
            
                maven-resources-plugin
                3.0.2
                
                    
                        $
                    
                    false
                
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                    ${project.build.sourceEncoding}
                    
                    
                        -parameters
                    
                
            
        
    


2. application.yml

spring:
  datasource:
    druid:
      url: $db.url$
      username: $db.username$
      password: $db.password$
      driver-class-name: $db.driver$
# jetcache使用
jetcache: 
  statIntervalMinutes: 60
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      timeout: 5000
      poolConfig:
        minIdle: 5
        maxIdle: 10
        maxTotal: 20
        testOnBorrow: false
        testOnReturn: true
      host: 47.93.**.**
      port: 6378
mybatis:
  mapper-locations:
  - classpath:conf/mapper/*.xml
  config-location: classpath:conf/mybatis/mybatis.xml

2. application启动类

@SpringBootApplication
@EnableCreateCacheAnnotation
@MapperScan("com.example.demo.dao")
@EnableMethodCache(basePackages = "com.example.demo")
@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
public class SpringbootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}

@EnableCreateCacheAnnotation注解用于开启jetcache中@CreateCache注解,
@EnableMethodCache(basePackages = “com.example.demo”)注解用于开启@cache注解

3. 接口及实现类

public interface IUserService {

    String addRecord(String mobile);
    
    String addIntRecord(String mobile);
    
    String addUser(User user);
    
    String processMobile(User user);
    
}
@Service
public class UserServiceImpl implements IUserService{

    @Override
    public String addRecord(String mobile) {
        
        User user = new User();
        user.setAge("22");
        user.setCity("上海");
        user.setMobile(mobile);
        
        IUserService userService = (IUserService) AopContext.currentProxy();
        userService.processMobile(user);
        
        return "test liu";
    }

    @Override
    @Cached(name="ls:dlc:int",key="targetObject.processMobile(#mobile)",cacheType=CacheType.REMOTE)
    public String addIntRecord(String mobile) {
        return "processMobile";
    }

    @Cached(name="ls:dlc:scoring:",key="#user.name",cacheType=CacheType.REMOTE)
    @Override
    public String addUser(User user) {

        return user.getAge();
    }
    
    @Cached(name="ls:dlc:scoring:",key="#user.name",cacheType=CacheType.REMOTE)
    @Override
    public String processMobile(User user) {
        
        System.out.println("调用processMobile方法。。。");
        
        return "user:"+user.getMobile();
    }
    
    public String processMobile(String mobile){
        
        System.out.println(mobile);
        
        return "processMobile"+mobile;
    }

}

这里注意两个processMobile方法的不同,一个是string字符作为参数,一个是对象作为参数;一个是实现接口方法,一个是实现类中自己的方法。

4. controller测试代码

@RestController
public class JetcacheController {

    @Autowired
    private IUserService userService;
    
    @RequestMapping("jet_spel_test")
    public String JetSpelTest(){
        
        String addIntRecord = userService.addIntRecord("22222");
        
        return addIntRecord;
    }
    
    @RequestMapping("jet_mobile_test")
    public String JetMobileTest(String mobile){
        
        String addRecord = userService.addRecord("123456789");
        
        return addRecord;
    }
    
    @RequestMapping("jet_object_test")
    public String JetObjectTest(){
        
        User user = new User();
        user.setAge("23");
        user.setCity("南京");
        user.setName("zhaosi");
        
        String result = userService.addUser(user);
        
        System.out.println("result:"+result);
        
        return result;
    }
    
}

遇到的问题

1. spEL表达式的使用

最初的需求是当调用userservice.addRecord(string mobile),希望对mobile进行特殊处理,比如md5加密后作为缓存key,所以在实现类中还定义了一个processMobile方法,用于处理手机号。不要说什么可以加密之后再传入方法,这里只是举例说明如果有这种需要。

但是试了很多,spEL的表达式都找不到当前类中的processMobile方法,在spring cache中这种情况是使用:

SpEL表达式可基于上下文并通过使用缓存抽象,提供与root独享相关联的缓存特定的内置参数。

名称 位置 描述 示例
methodName root对象 当前被调用的方法名 #root.methodname
method root对象 当前被调用的方法 #root.method.name
target root对象 当前被调用的目标对象实例 #root.target
targetClass root对象 当前被调用的目标对象的类 #root.targetClass
args root对象 当前被调用的方法的参数列表 #root.args[0]
caches root对象 当前方法调用使用的缓存列表 #root.caches[0].name
Argument Name 执行上下文 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 #artsian.id
result 执行上下文 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) #result

但是这种方式在jetcache中并不好用,最后还是在github的issues中找到了解决方案:

JetCache中使用Cache注解缓存到远程redis以及踩过的坑_第1张图片

正如上面接口实现类中的addIntRecord所示,使用targetObject获取当前类中的方法。这里需要注意的是调用的processMobile方法需要是public修饰,需要传参使用#+参数的方式传递。

2. spEL表达式不可用时候使用的替代方法

这一步是在没有找到spEL表达式的时候,所想到的替代方法,因为官网给的教程是通过对象获取参数,或者直接传递参数。

这里如addRecord方法所示,addRecord方法我并没有加@cache注解,而是使用user对象存放mobile属性,通过调用@cache注解修饰的processMobile(User user)方法去实现将结果加入远程缓存中,即将需要进行的处理逻辑和最终的返回结果放入processMobile(User user)方法中,这时候通过#user.mobile就可以获取到手机号了。

这里遇到的问题是通过一个方法调用当前类中的另一个方法的时候,不能保持事务的一致性,即所调用的方法不是spring所管理的,最终也会报错。

解决方式有两种:

1) 通过方法类调用自己方法类的方式实现

即在方法上通过@autowired或者@resource注解引入userservice对象,在此中使用此对象调用service中的方法。

2) 通过代理获取spring中的对象

IUserService userService = (IUserService) AopContext.currentProxy();
userService.processMobile(user);

使用aopContext获取当前代理对象,需要在启动类上加上如下注解,即需要手动暴露代理

@EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)

3. devtools与jetcache冲突 EL1072E: An exception occurred whilst evaluating a compiled expression

在使用的过程中,最初存到远程redis缓存成功,但是从缓存中取的时候就会报错的问题。错误信息大致如下:

org.springframework.expression.spel.SpelEvaluationException: EL1072E: An exception occurred whilst evaluating a compiled expression

Caused by: java.lang.ClassCastException: com.example.entity.User cannot be cast to com.example.entity.User
at spel.Ex3.getValue(Unknown Source)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:254)
... 42 common frames omitted

最开始看到这个错误是蒙蔽的,怎么user不能转成user呢,网上还是查不到任何有用的信息,最终还是在github的issues中找到了解释:

JetCache中使用Cache注解缓存到远程redis以及踩过的坑_第2张图片

测试结果的时候,我将pom文件中关于devtools的依赖取消了,果然运行没有报错,至于-parameters参数的设置,我也修改了,但是并没有解决实质性的问题。

这里还是附一下-parameter参数的设置方式:

  1. 最简单的方式,pom文件中修改

                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                    ${project.build.sourceEncoding}
                    
                    
                        -parameters
                    
                
            
  1. eclipse中默认设置是关闭的,需要手动开启
    JetCache中使用Cache注解缓存到远程redis以及踩过的坑_第3张图片

总结

jetcache的文档还是太少,网上有的教程一般都是网上能够找到的,但是一些特别的坑还是要取issues中才能找到解决方法,这也算是给自己增加了使用开源框架的经验。

你可能感兴趣的:(java后台,java,springboot,接口,缓存)