问题描述

在使用Spring AOP进行方法缓存的时候,使用AopContext.currentProxy()获取当前代理对象,但发现缓存不生效。

问题分析

缓存不生效的原因是AopContext.currentProxy()获取的并不是代理对象,而是目标对象本身。因此,不能直接在目标对象的方法内部调用被代理的方法,这样缓存并不会起作用。

解决方案

为了使方法缓存生效,可以通过以下几种方式解决:

方式一:使用代理对象调用方法

直接使用代理对象调用方法,而不是在目标对象的方法内部调用。

public class MyService {
    // 使用代理对象调用方法
    public void doSomething() {
        ((MyService) AopContext.currentProxy()).cachedMethod();
    }
    
    // 需要被代理的方法,添加缓存注解
    @Cacheable("myCache")
    public void cachedMethod() {
        // 方法体
    }
}

// 配置AOP
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
    // 需要开启ExposeProxy,以便使用AopContext.currentProxy()
    @Bean
    public AspectJProxyFactory aspectJProxyFactory() {
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setExposeProxy(true);
        return proxyFactory;
    }

    // 注入AOP切面
    @Bean
    public MyAspect myAspect() {
        return new MyAspect();
    }
}

// 缓存切面
@Aspect
@Component
public class MyAspect {
    @Around("execution(* com.example.MyService.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 方法执行前的逻辑
        Object result = joinPoint.proceed();
        // 方法执行后的逻辑
        return result;
    }
}

方式二:使用代理对象调用方法并传递参数

将参数传递给代理对象,然后在代理对象的方法内部调用被代理的方法。

public class MyService {
    // 使用代理对象调用方法并传递参数
    public void doSomething() {
        ((MyService) AopContext.currentProxy()).cachedMethod("param");
    }
    
    // 需要被代理的方法,添加缓存注解
    @Cacheable("myCache")
    public void cachedMethod(String param) {
        // 方法体
    }
}

// 配置AOP与缓存
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableCaching
public class AopConfig {
    // 需开启ExposeProxy,以便使用AopContext.currentProxy()
    @Bean
    public AspectJProxyFactory aspectJProxyFactory() {
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setExposeProxy(true);
        return proxyFactory;
    }

    // 注入AOP切面
    @Bean
    public MyAspect myAspect() {
        return new MyAspect();
    }

    // 缓存管理器
    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager();
    }
}

// 缓存切面
@Aspect
@Component
public class MyAspect {
    // 配置缓存切点
    @Pointcut("@annotation(org.springframework.cache.annotation.Cacheable)")
    public void cacheableMethod() {}

    // 缓存环绕通知
    @Around("cacheableMethod()")
    public Object aroundCacheable(ProceedingJoinPoint joinPoint) throws Throwable {
        // 方法执行前的逻辑
        Object result = joinPoint.proceed();
        // 方法执行后的逻辑
        return result;
    }
}

总结

使用AopContext.currentProxy()获取当前代理对象的方式可以解决方法缓存不生效的问题。通过使用代理对象调用方法,或将参数传递给代理对象,然后在代理对象的方法内部调用被代理的方法,可以保证缓存的正常使用。另外,还需要注意配置AOP时开启ExposeProxy,以便使用AopContext.currentProxy()