Spring中的各种Utils(六):AopProxyUtils详解

原创文章,转载请注明出处

本节介绍AopProxyUtils,这个工具类方法本身很少,但是涉及到很多AOP相关重要接口,所以本节主要会对涉及到的接口进行初步的介绍;

Class<?> ultimateTargetClass(Object candidate)
获取一个代理对象的最终对象类型,先做一个测试(测试的准备工作参考https://www.jianshu.com/p/a4a815f42cd2):

@Test
public void testUltimateTargetClass() {
    // class cn.wolfcode.springboot.utilstest.MyComponent
    System.out.println(AopProxyUtils.ultimateTargetClass(component));
    // class cn.wolfcode.springboot.utilstest.EmployeeServiceImpl
    System.out.println(AopProxyUtils.ultimateTargetClass(service));
}

分别获取了代理对象的最终对象类型;我们来看看该方法的实现:

public static Class<?> ultimateTargetClass(Object candidate) {
    Assert.notNull(candidate, "Candidate object must not be null");
    //current用于判断;
    Object current = candidate;
    Class<?> result = null;
    //直到当前获得的对象不是TargetClassAware类型,TargetClassAware后面介绍;
    while (current instanceof TargetClassAware) {
        //获得当前对象(一个代理对象)代理的目标对象(这个对象可能还是一个代理对象)类型;
        result = ((TargetClassAware) current).getTargetClass();
        Object nested = null;
        //如果当前获取的目标对象是一个Advised类型对象;
        if (current instanceof Advised) {
            //通过getTargetSource方法获取代理目标;
            TargetSource targetSource = ((Advised) current).getTargetSource();
            //如果代理目标对象是一个SingletonTargetSource;
            if (targetSource instanceof SingletonTargetSource) {
                //获取当前代理对象的真正目标对象(可能还是一个代理对象)
                nested = ((SingletonTargetSource) targetSource).getTarget();
            }
        }
        current = nested;
    }
    //如果获取到的目标对象是一个cglib代理对象,获取父类类型(才是目标类型)
    if (result == null) {
        result = (AopUtils.isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass());
    }
    return result;
}

其实这段代码本身不难,但是这里面涉及到了很多SpringAOP中的接口,比如TargetClassAware,Advised,TargetSource,SingletonTargetSource等;这些接口都是SpringAOP中的一些重要接口,在这里我们做一个简单介绍,更多的内容后面单开文章介绍;

我们先看下TargetClassAware,Advised和TargetSource三个接口:
TargetClassAware:所有的Aop代理对象或者代理工厂(proxy factory)都要实现的接口,该接口用于暴露出被代理目标对象类型;

TargetSource:该接口代表一个目标对象,在aop调用目标对象的时候,使用该接口返回真实的对象。比如该接口的一个实现:PrototypeTargetSource,那就是每次调用都返回一个全新的对象实例;

Advised:该接口用于保存一个代理的相关配置(简单理解为这个对象保存了怎么创建一个代理对象的信息),比如这个代理配置相关的拦截器,建议(advisor)或者增强器(advice);所有的代理对象都实现了该接口(我们就能够通过一个代理对象获取这个代理对象怎么被代理出来的相关信息);

我们可以来做个测试:

@Test
public void testUltimateTargetClass2() {
    /**
     * interface cn.wolfcode.springboot.utilstest.IEmployeeService interface
     * cn.wolfcode.springboot.utilstest.IAddition interface
     * org.springframework.aop.SpringProxy interface
     * org.springframework.aop.framework.Advised interface
     * java.io.Serializable
     */
    System.out.println(StringUtils.collectionToDelimitedString(
            ClassUtils.getAllInterfacesAsSet(service), "\r\n"));
    /**
     * interface org.springframework.aop.SpringProxy interface
     * org.springframework.aop.framework.Advised interface
     * org.springframework.cglib.proxy.Factory
     */
    System.out.println(StringUtils.collectionToDelimitedString(
            ClassUtils.getAllInterfacesAsSet(component), "\r\n"));
}

可以看到,不管是JDKproxy,还是cglib proxy,代理出来的对象都实现了org.springframework.aop.framework.Advised接口;

三个接口的关系为:

image.png

接口中的方法我们先不研究,我们明确一个点来辅助我们理解这个关系,那就是Advised接口中的getTargetSource返回的就是TargetSource。意思就是Advised和TargetSource接口虽然在继承关系上,都是继承了TargetClassAware接口,看似平级关系,实际上确实组合关系:

@Test
public void testUltimateTargetClass3() {
    if (service instanceof Advised) {
        TargetSource ts = ((Advised) service).getTargetSource();
        //class org.springframework.aop.target.SingletonTargetSource
        System.out.println(ts.getClass());
    }
}

可以看到,得到的最终是一个SingletonTargetSource,那么就是说这种TargetSource就是每次调用,都返回相同对象;

而Advised里面包含的Advisor和Advice,我们后面再详细介绍;
希望通过这个介绍,对这三个接口有更深入的理解;


Class<?>[] completeProxiedInterfaces(AdvisedSupport advised)
很牛逼的方法来了,判断一个advised真正需要代理的目标接口列表。简单理解,比如在spring使用JDK proxy做代理的时候,这个方法返回的类型列表就是真正需要交给Proxy.newProxyInstance方法的接口列表,我们先来简单看看这个流程在Spring中的实现:

public Object getProxy(ClassLoader classLoader) {
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

这段代码摘取自JdkDynamicAopProxy,这个就是我们常常说的Spring中针对实现了接口的对象,使用JDK的动态代理完成的真正代码,可以看到,传给newProxyInstance方法第二个参数的接口列表,就是通过completeProxiedInterfaces方法获取的。足见该方法的重要性;

该方法具体的实现我们这里先不介绍,因为涉及到另一个AOP中重要的类:AdvisedSupport,该类是Spring中用于搜集代理配置的对象(是不是感觉这句话很熟,是的,这个类就是实现了上面介绍的Advised接口,但是注意,代理出来的类可以转成Advised,但是不是AdvisedSupport);


Class<?>[] proxiedUserInterfaces(Object proxy) 
该方法用于获取一个代理对象中的用户定义的接口,即非(Advised接口体系)之外的其他接口;

@Test
public void testProxiedUserInterfaces() {
    /**
     * interface cn.wolfcode.springboot.utilstest.IEmployeeService interface
     * cn.wolfcode.springboot.utilstest.IAddition interface
     * org.springframework.aop.SpringProxy interface
     * org.springframework.aop.framework.Advised interface
     * java.io.Serializable
     */
    System.out.println(StringUtils.collectionToDelimitedString(
            ClassUtils.getAllInterfacesAsSet(service), "\r\n"));

    /**
     * interface cn.wolfcode.springboot.utilstest.IEmployeeService interface
     * cn.wolfcode.springboot.utilstest.IAddition
     */
    System.out.println(StringUtils.arrayToDelimitedString(
            AopProxyUtils.proxiedUserInterfaces(service), "\r\n"));

}

可以看到,只得到了用户定义的接口,Spring自动增加的系统接口没有包含;
这个方法的实现也很有意思:

public static Class<?>[] proxiedUserInterfaces(Object proxy) {
    //得到所有接口
    Class<?>[] proxyInterfaces = proxy.getClass().getInterfaces();
    int nonUserIfcCount = 0;
    //如果是代理,一定实现了SpringProxy;
    if (proxy instanceof SpringProxy) {
        nonUserIfcCount++;
    }
    //如果是代理,可能实现了Advised;
    if (proxy instanceof Advised) {
        nonUserIfcCount++;
    }
    Class<?>[] userInterfaces = new Class<?>[proxyInterfaces.length - nonUserIfcCount];
    //拷贝proxyInterfaces中从第0位~第proxyInterfaces.length - nonUserIfcCount个
    //去掉尾巴上的nonUserIfcCount个;
    System.arraycopy(proxyInterfaces, 0, userInterfaces, 0, userInterfaces.length);
    Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces");
    return userInterfaces;
}

boolean equalsProxiedInterfaces(AdvisedSupport a, AdvisedSupport b)
判断两个(即将)代理出来的对象是否拥有相同接口;

boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b)
判断两个(即将)代理出来的对象是否拥有相同的建议者(Advisor);

boolean equalsInProxy(AdvisedSupport a, AdvisedSupport b)
判断两个(即将)代理出来的对象是否相同;

小结

AopProxyUtils中的方法不多,但是其中的ultimateTargetClass和completeProxiedInterfaces方法确是Spring AOP中比较重要的方法,也给了我们一个入手观察Spring AOP真正实现过程的一个突破口;我认为这个,才是AopProxyUtils给我们的价值;


  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值