如何在spring代理中实现自我调用(self-invocation)

问题

在spring中如果在方法上添加了诸如@Transactional@Cacheable或是切面之类的注解,那么这个类就将由spring生成其代理对象,对指定方法进行相关的包装。当该方法在其他对象中被调用时是可以正常触发代理方法的,然而在本类的方法中进行内部调用时却不会,最终调用的还是原始方法。

class Service {
   
    public void methodA(){
   
        methodB();
    }
    @Transactional
    public void methodB(){
   
        // do something
    }
}

也就是说通过methodA调用methodB不会走代理方法,这是为什么呢?

原因分析

我们知道spring生成代理对象常见的有两种方式,一种是基于接口的JDK动态代理,一种是基于子类的CGLIB风格代理,可通过proxyTargetClass属性来控制。JDK文档中的一段话:

Users can control the type of proxy that gets created for FooService using the proxyTargetClass() attribute. The following enables CGLIB-style ‘subclass’ proxies as opposed to the default interface-based JDK proxy approach.

当为其配置了false时强制使用JDK动态代理,代理对象必须实现接口;当配置为true时强制使用CGLIB风格代理,代理方法不可使用final修饰;当没有配置该项时spring将自动选择有效的代理方式来实现。

JDK动态代理情况

JDK动态代理大家应该都比较熟悉了,这儿列出大致实现步骤,进而说明为什么无法触发代理方法。

public interface Subject
{
   
    public void methodA();
    public void methodB();
}

public class RealSubject implements Subject
{
   
    public void methodA()
    {
   
        // do something
    }
    public void methodB()
    {
   
        // do something
    }
}

public class InvocationHandlerImpl implements InvocationHandler
{
   
 
    /**
     * real proxied object
     */
    private Object subject;
 
    public InvocationHandlerImpl(Object subject)
    {
   
        this.subject = subject;
    }
 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
   
        // do something before invocation
 
        Object returnValue = method.invoke(subject, args);
 
        // do something after invocation
 
        return returnValue;
    }
}
// generate proxy step
Subject realSubject = new RealSubject();

InvocationHandler handler = new InvocationHandlerImpl(realSubject);

ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();

Subject subject = (Subject) Proxy
  • 12
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Cache ,我们可以通过使用注解来声明缓存操作。例如,@Cacheable 注解用于表示方法的结果应该被缓存,@CacheEvict 注解用于表示缓存应该被清除,@CachePut 注解用于表示方法的结果应该被缓存,等等。这些注解都是在运行时被解析的,Spring 通过解析这些注解来确定缓存操作应该被执行的方式。 在 Spring ,注解解析器是用于解析注解的组件。在 Spring Cache ,注解解析器的作用是解析 @Cacheable、@CachePut、@CacheEvict 和 @Caching 注解,并将它们转换为缓存操作的定义。Spring Cache 提供了 AnnotationCacheOperationSource 类来实现这个功能。 AnnotationCacheOperationSource 类实现了 CacheOperationSource 接口,它的主要作用是解析注解并返回缓存操作的定义。在 Spring Cache ,每个缓存操作的定义都表示为一个 CacheOperation 对象。AnnotationCacheOperationSource 类的 getCacheOperations() 方法返回一个 List<CacheOperation> 对象,表示指定方法上的所有缓存操作的定义。 AnnotationCacheOperationSource 类的核心方法是 buildCacheOperations() 方法。这个方法接收一个 MethodInvocation 对象,它描述了要执行的方法及其参数。buildCacheOperations() 方法首先检查给定的方法是否具有 @Cacheable、@CachePut、@CacheEvict 或 @Caching 注解。如果有这些注解,它将解析它们并返回一个包含缓存操作定义的 List<CacheOperation> 对象。如果没有这些注解,它将返回一个空的 List<CacheOperation> 对象。 解析注解的过程非常复杂,AnnotationCacheOperationSource 类使用了多个辅助类来完成这个过程。这些辅助类包括 AnnotationCacheAspect、CacheAnnotationParser、CacheAnnotationParserUtils 和 CacheAspectSupport。AnnotationCacheAspect 是一个切面,它拦截所有具有 @Cacheable、@CachePut、@CacheEvict 或 @Caching 注解的方法。CacheAnnotationParser 类用于解析注解并返回相应的缓存操作定义。CacheAnnotationParserUtils 类包含一些通用的注解解析方法。CacheAspectSupport 类是一个抽象类,它提供了一些通用的缓存操作方法,它的子类可以继承这些方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值