qiujiayu / AutoLoadCache

AutoLoadCache 是基于AOP+Annotation等技术实现的高效的缓存管理解决方案,实现缓存与业务逻辑的解耦,并增加异步刷新及“拿来主义机制”,以适应高并发环境下的使用。

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

整合异常

zhengmingliang opened this issue · comments

我几乎完全按照使用文档配置使用的,但总是报Caused by: com.jarvis.cache.exception.LoadDataTimeOutException的错误,想问下是哪儿配置的出问题了么

java.lang.reflect.UndeclaredThrowableException
	at com.sun.proxy.$Proxy60.queryByScheduleId(Unknown Source)
	at com.dtsz.scheduletask.service.impl.TriggerServiceImpl.queryByScheduleId(TriggerServiceImpl.java:77)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
	at com.sun.proxy.$Proxy70.queryByScheduleId(Unknown Source)
	at com.dtsz.scheduletask.controller.ScheduleController.queryTriggers(ScheduleController.java:518)
	at com.dtsz.scheduletask.controller.ScheduleController$$FastClassBySpringCGLIB$$f9d5bc5d.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
	at com.dtsz.scheduletask.aspect.SystemLogAspect.doAround(SystemLogAspect.java:110)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
	at com.dtsz.scheduletask.controller.ScheduleController$$EnhancerBySpringCGLIB$$209b7631.queryTriggers(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)
Caused by: com.jarvis.cache.exception.LoadDataTimeOutException: cache for key "schedule:trigger_scheduleId42e93ea3e5c346a285d5a01ee5a3633d" loaded 1 times.
	at com.jarvis.cache.DataLoader.doWaitRequest(DataLoader.java:195)
	at com.jarvis.cache.DataLoader.loadData(DataLoader.java:116)
	at com.jarvis.cache.DataLoader.doWaitRequest(DataLoader.java:193)
	at com.jarvis.cache.DataLoader.loadData(DataLoader.java:116)
	at com.jarvis.cache.CacheHandler.proceed(CacheHandler.java:194)
	at com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.proceed(AspectjAopInterceptor.java:69)
	at com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.checkAndProceed(AspectjAopInterceptor.java:33)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:97)
	at com.jarvis.cache.aop.aspectj.AspectjCacheAopProxyChain.doProxyChain(AspectjCacheAopProxyChain.java:45)
	at com.jarvis.cache.DataLoader.getData(DataLoader.java:210)
	at com.jarvis.cache.DataLoader.doFirstRequest(DataLoader.java:150)
	at com.jarvis.cache.DataLoader.loadData(DataLoader.java:102)
	at com.jarvis.cache.CacheHandler.proceed(CacheHandler.java:194)
	at com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.proceed(AspectjAopInterceptor.java:69)
	at com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.checkAndProceed(AspectjAopInterceptor.java:33)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)

@Cache(waitTimeOut=500)

默认值就是500ms,我改成了5000ms还是这个问题

下面是配置信息

  1. 这是mapper
public interface TriggerMapper extends Mapper<TriggerEntity> {

    @Cache(expire=600, autoload=true, key="'trigger_scheduleId'+#args[0]",waitTimeOut = 5000)
    List<TriggerVO> queryByScheduleId(@Param("scheduleId") String scheduleId);
}
  1. 这个配置文件

···xml

<bean id="autoLoadConfig" class="com.jarvis.cache.to.AutoLoadConfig">
    <!--命名空间,在多个模块共用一个缓存环境时,避免key冲突;-->
    <property name="namespace" value="schedule"/>
    <!--处理自动加载队列的线程数量-->
    <property name="threadCnt" value="10"/>
    <!--自动加载队列中允许存放的最大容量,默认值为20000;-->
    <property name="maxElement" value="20000"/>
    <!--是否打印比较耗时的请求,默认值:true;-->
    <property name="printSlowLog" value="true"/>
    <!--当请求耗时超过此值时,记录目录(printSlowLog=true 时才有效),单位:毫秒;默认值500;-->
    <property name="slowLoadTime" value="500"/>
    <!--自动加载队列排序算法, 0:按在Map中存储的顺序(即无序);1 :越接近过期时间,越耗时的排在最前;
    2:根据请求次数,倒序排序,请求次数越多,说明使用频率越高,造成并发的可能越大;
    更详细的说明,请查看代码com.jarvis.cache.type.AutoLoadQueueSortType
    -->
    <property name="sortType" value="1"/>
    <!--加载数据之前去缓存服务器中检查,数据是否快过期,
    如果应用程序部署的服务器数量比较少,设置为false, 如果部署的服务器比较多,可以考虑设置为true-->
    <property name="checkFromCacheBeforeLoad" value="false"/>
    <!--单个线程中执行自动加载的时间间隔, 此值越小,遍历自动加载队列频率起高,对CPU会越消耗CPU;-->
    <property name="autoLoadPeriod" value="50"/>
    <!--异步刷新缓存线程池的 corePoolSize,默认值:2-->
    <!--<property name="refreshThreadPoolSize " value="2"/>
    &lt;!&ndash;异步刷新缓存线程池的 maximumPoolSize ,默认值:20;&ndash;&gt;
    <property name="refreshThreadPoolMaxSize  " value="10"/>
    &lt;!&ndash;异步刷新缓存线程池的 keepAliveTime。默认值20,单位分钟;&ndash;&gt;
    <property name="refreshThreadPoolkeepAliveTime   " value="20"/>
    &lt;!&ndash;异步刷新缓存队列容量,默认值:2000;&ndash;&gt;
    <property name="refreshQueueCapacity" value="2000"/>-->
    <!--加载数据重试次数,默认值为1(即不尝试)-->
    <!--<property name="loadDataTryCnt" value="2"/>-->

    <property name="functions">
        <map>
            <entry key="isEmpty" value="com.jarvis.cache.CacheUtil" />
        </map>
    </property>
</bean>


<!--序列化工具:主要用于深度复杂,以及缓存中数据与Java对象的转换-->
<!--使用Fastjson时需要注意:返回值中如果是泛型的话,需要指明具体的类型,比如:List,如果是直接返回List则会出错-->
<bean id="fastjsonSerializer" class="com.jarvis.cache.serializer.FastjsonSerializer"/>

<!--表达式解析器:缓存Key及一些条件表达式,都是通过表达式与Java对象进行交互的-->
<bean id="springEl" class="com.jarvis.cache.script.SpringELParser"/>

<!-- Jedis 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="200"/>
    <property name="maxIdle" value="100"/>
    <property name="minIdle" value="50"/>
    <property name="maxWaitMillis" value="10000"/>
    <property name="testOnBorrow" value="false"/>
    <property name="testOnReturn" value="false"/>
    <property name="testWhileIdle" value="true"/>
</bean>

<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
    <constructor-arg ref="jedisPoolConfig"/>
    <constructor-arg>
        <list>
            <bean class="redis.clients.jedis.JedisShardInfo">
                <constructor-arg value="${redis1.host}"/>
                <constructor-arg type="int" value="${redis1.port}"/>
                <!--<constructor-arg value="instance:01"/>-->
            </bean>
        </list>
    </constructor-arg>
</bean>

<bean id="cacheManager" class="com.jarvis.cache.redis.ShardedJedisCacheManager">
    <constructor-arg ref="fastjsonSerializer"/>
    <property name="shardedJedisPool" ref="shardedJedisPool"/>
</bean>

<!--缓存处理器-->
<bean id="cacheHandler" class="com.jarvis.cache.CacheHandler" destroy-method="destroy">
    <constructor-arg ref="cacheManager"/>
    <constructor-arg ref="springEl"/>
    <constructor-arg ref="autoLoadConfig"/>
    <constructor-arg ref="fastjsonSerializer"/>
</bean>

<!--AOP配置-->
<bean id="cacheInterceptor" class="com.jarvis.cache.aop.aspectj.AspectjAopInterceptor">
    <constructor-arg ref="cacheHandler"/>
</bean>

<!--dao层AOP-->
<!-- proxy-target-class=false为jdk代理,为true的话,会导致拦截不了mybatis的mapper -->
<aop:config proxy-target-class="false">
    <!-- 拦截mybatis的mapper -->
    <aop:aspect ref="cacheInterceptor">
        <aop:pointcut id="daoCachePointcut"
                      expression="execution(public !void com.dtsz.scheduletask.dao..*.*(..))"/>
        <aop:around pointcut-ref="daoCachePointcut" method="checkAndProceed"/>
    </aop:aspect>

    <!-- 处理 @CacheDelete AOP-->
    <aop:aspect ref="cacheInterceptor"
                order="1000"><!-- order 参数控制 aop通知的优先级,值越小,优先级越高 ,在事务提交后删除缓存 -->
        <aop:pointcut id="deleteCachePointcut"
                      expression="execution(* com.dtsz.scheduletask.dao..*.*(..))"/>
        <aop:after-returning pointcut-ref="deleteCachePointcut" method="checkAndDeleteCache"
                             returning="retVal"/>
    </aop:aspect>

    <!-- 处理 @CacheDeleteTransactional AOP-->
    <!--<aop:aspect ref="cacheInterceptor">-->
        <!--<aop:pointcut id="cacheDeleteTransactional"-->
                      <!--expression="execution(* com.dtsz.scheduletask.service..*.*(..)) &amp;&amp; @annotation(cacheDeleteTransactional)"/>-->
        <!--<aop:around pointcut-ref="cacheDeleteTransactional" method="deleteCacheTransactional"/>-->
    <!--</aop:aspect>-->

</aop:config>

<!--service层AOP-->
<aop:config proxy-target-class="false">
    <!-- 拦截mybatis的mapper -->
    <aop:aspect ref="cacheInterceptor">
        <aop:pointcut id="daoCachePointcut2"
                      expression="execution(public !void com.dtsz.scheduletask.service..*.*(..))"/>
        <aop:around pointcut-ref="daoCachePointcut" method="checkAndProceed"/>
    </aop:aspect>

    <!-- 处理 @CacheDelete AOP-->
    <aop:aspect ref="cacheInterceptor"
                order="1000"><!-- order 参数控制 aop通知的优先级,值越小,优先级越高 ,在事务提交后删除缓存 -->
        <aop:pointcut id="deleteCachePointcut2"
                      expression="execution(* com.dtsz.scheduletask.service..*.*(..))"/>
        <aop:after-returning pointcut-ref="deleteCachePointcut" method="checkAndDeleteCache"
                             returning="retVal"/>
    </aop:aspect>
</aop:config>
···

你先把@Cache注释了,看看是不是耗时比较长。

哎,然而并没有……注释掉注解后,正常查询,查询时间几十毫秒

你是不是mapper和service都加了@Cache注解?

是的,但是Service那个应该是没有起作用,我刚刚写了单元测试,只测试mapper,结果还是那样……

把Service层的代码也贴出来看一下?

或者到 QQ群:429274886 里沟通。

调试的时候,这一块就没打印AutoLoadCache的日志,当然了也没抛什么异常,主要是内层的Mapper已经异常了
image

9、at com.jarvis.cache.DataLoader.doWaitRequest(DataLoader.java:195)
8、at com.jarvis.cache.DataLoader.loadData(DataLoader.java:116)
7、at com.jarvis.cache.DataLoader.doWaitRequest(DataLoader.java:193)
6、at com.jarvis.cache.DataLoader.loadData(DataLoader.java:116)
5、at com.jarvis.cache.CacheHandler.proceed(CacheHandler.java:194)

at com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.proceed(AspectjAopInterceptor.java:69)
at com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.checkAndProceed(AspectjAopInterceptor.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:97)
at com.jarvis.cache.aop.aspectj.AspectjCacheAopProxyChain.doProxyChain(AspectjCacheAopProxyChain.java:45)

4、at com.jarvis.cache.DataLoader.getData(DataLoader.java:210)
3、at com.jarvis.cache.DataLoader.doFirstRequest(DataLoader.java:150)
2、at com.jarvis.cache.DataLoader.loadData(DataLoader.java:102)
1、at com.jarvis.cache.CacheHandler.proceed(CacheHandler.java:194)

从异常情况来看,应该同一个执行过程中,使用了相同的缓存key。
执行第一个@Cache注解方法 proceed--> loadData-->doFirstRequest-->getData-->执行第二个@Cache注解方法 proceed--> loadData ->doWaitRequest(这里变成doWaitRequest,说明已有请求在请求加载相同的数据)。

嗯,是的,我调试的时候,发现走了一次doFirstRequest两次doWaitRequest,去缓存里拿了四次数据……也是醉了……这是我写的单元测试,(另外,已加群)
image

1

你mybatis有做什么特殊处理吗?

项目使用的是通用mapper(tk.mybatis.spring.mapper),我怀疑可能是冲突了,所以单元测试的时候就用的官方的mapper(org.mybatis.spring.mapper),一样的问题,我还发现文档中说proxy-target-class=false为jdk代理,为true的话,会导致拦截不了mybatis的mapper ,我设置为true或者false都是一样的这个问题

这个是mybatis的配置文件,我把通用mapper的部分(含插件)注释掉,换成官方配置,也是这个问题,把注解去掉,可以正常查询到库中数据

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- 配置数据源 -->
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc_url}"/>
        <property name="username" value="${jdbc_username}"/>
        <property name="password" value="${jdbc_password}"/>
        <!--<property name="driverClassName" value="${jdbc_driverClassName}"/>-->

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="5"/>
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20"/>
        <!-- 连接池最大空闲 -->
        <!--<property name="maxIdle" value="20"/>-->
        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="1"/>
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000"/>

        <!--如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。分库分表较多的数据库,建议配置为false。-->
        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="true"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="30"/>

        <property name="validationQuery" value="${jdbc_validationQuery}"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="testWhileIdle" value="true"/>

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="1800000"/>

        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true"/>
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800"/>
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true"/>

        <!-- 监控数据库 -->
        <!-- <property name="filters" value="stat" /> -->
        <property name="filters" value="mergeStat"/>
    </bean>


    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 自动扫描mapper目录, 省掉Configuration.xml里的手工配置 -->
        <property name="configLocation" value="classpath:spring/mybatis-config.xml"></property>
        <property name="mapperLocations" value="classpath*:com/dtsz/scheduletask/mapper/*.xml"/>
        <property name="plugins">
            <array>
                <!--PageHelper分页插件-->
                <bean class="com.github.pagehelper.PageInterceptor">

                    <property name="properties">
                        <value>
                            helperDialect=oracle
                            offsetAsPageNum=true
                            rowBoundsWithCount=true
                            pageSizeZero=true
                            reasonable=true
                            params=pageNum=pageNo
                        </value>
                    </property>
                </bean>

                <!-- 分页插件或其他插件,OrderBy 一定要在分页插件下面(主要是为了避免 count 也被增加排序) -->
                <!--<bean class="tk.mybatis.orderbyhelper.OrderByHelper"/>-->
            </array>

        </property>
    </bean>
    <!-- mybatis扫描配置 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.dtsz.scheduletask.dao" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>
    <bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dtsz.scheduletask.dao"/>
        <!-- 3.2.2版本新特性,markerInterface可以起到mappers配置的作用,详细情况需要看Marker接口类 -->
        <property name="markerInterface" value="tk.mybatis.mapper.common.Mapper"/>
        <!-- 通用Mapper通过属性注入进行配置,默认不配置时会注册Mapper<T>接口

     -->
    </bean>

    <!-- 自动扫描(自动注入) -->
    <context:component-scan base-package="com.dtsz.scheduletask.service.impl"/>

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" scope="prototype">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 注解方式配置事物 -->
    <!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->

    <!-- 拦截器方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>

            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="batchInsert*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="batchUpdate*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="batchDelete*" propagation="REQUIRED"/>

            <tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
            <tx:method name="select*" read-only="true" propagation="SUPPORTS"/>

            <tx:method name="*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut id="transactionPointcut"
                      expression="execution(* com.dtsz.scheduletask.service.*.*(..))"/>
        <aop:advisor pointcut-ref="transactionPointcut"
                     advice-ref="transactionAdvice"/>
    </aop:config>


    <!-- 配置druid监控spring jdbc -->
    <bean id="druid-stat-interceptor"
          class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
    </bean>
    <bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"
          scope="prototype">
        <property name="patterns">
            <list>
                <value>com.dtsz.scheduletask.service.*</value>
            </list>
        </property>
    </bean>


</beans>

这根redis的配置有关吗,我没有采用集群模式,暂时先用的单机的,单机redis也像文档中那么配置??

你可以把AutoloadCache的源码导入到你的eclipse中,然后
com.jarvis.cache.aop.aspectj.AspectjAopInterceptor.checkAndProceed 打印些当类的一些信息:类名,类的类型等。
可以参考:https://github.com/qiujiayu/autoload-cache-spring-boot-starter/blob/master/src/main/java/com/jarvis/cache/interceptor/CacheMethodInterceptor.java

我初步觉得是 tkMybatis也增加了一些代理功能,而我们这里没有做好判断处理造成的。

我单元测试的时候,把tk mybatis 的相应配置和插件已经注释掉了

你先调试一下再看看吧。

恩恩,好的,十分感谢您抽出时间来帮忙查看问题

abel533/MyBatis-Spring-Boot#39

看看是不是同样的问题?

不是...