今天看啥  ›  专栏  ›  涣涣虚心0215

Spring AOP(7)代理类的调用基于CGLIB

涣涣虚心0215  · 简书  ·  · 2021-01-20 01:34

文章预览

听说 Spring AOP 有坑?那就来踩一踩
今天看到这篇文章,我觉得有必要继续分析一下基于CGLIB的代理。

CGLIB的原理

同样使用RealSubject,但是将接口去掉,在CGLIB中,重要条件是生成一个子类,然后重写要代理类的方法。

public class RealSubject {
    //public 方法,可以被子类继承重写
    public void sayName() {
        System.out.println("say my name");
    }
    //private方法,子类会继承,但是不能访问
    private void myPrivate() {
        System.out.println("this is private method");
    }
}

RealSubject realSubject = new RealSubject();
Enhancer enhancer = new Enhancer();
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
    System.out.println("before invoke...");
    Object result = methodProxy.invoke(realSubject, objects);
    System.out.println("after invoke...");
    return result;
});
enhancer.setSuperclass(realSubject.getClass());
//通过Enhancer创建代理类
Subject subjectProxy = (Subject) enhancer.create();
subjectProxy.sayName();

========
before invoke...
say my name
after invoke...

关于继承,有两个tips:
(1): 子类覆写父类的方法,方法权限必须比父类的权限相同或者更大,如果小于父类的方法权限,则编译报错。
(2): 如果父类的访问权限是private,子类中同名方法不会覆盖父类的方法,而是相当于子类新定义的方法。

这里代理类subjectProxy是RealSubject的子类,但是类中的private, final方法不能被代理, static方法不生成代理方法。
从下面截图中也能看到,代理类的declaredMethods中只有sayName()。

declaredMethods

CGLIB代理类方法调用

一般spring框架中都是通过method.invoke反射方式来调用方法,那么在存在代理proxy的情况下,method的调用会有以下不同的结果。

这个是真实对象的方法,通过invoke调用真实的对象
Method sayName = realSubject.getClass().getDeclaredMethod("sayName");
sayName.setAccessible(true);
sayName.invoke(realSubject);
=========
say my name

这个是真实对象的方法,通过invoke调用代理对象,代理生效
Method sayName = realSubject.getClass().getDeclaredMethod("sayName");
sayName.setAccessible(true);
sayName.invoke(subjectProxy);
=========
before invoke...
say my name
after invoke...


这个是代理对象的方法,通过invoke调用真实的对象
Method sayName = subjectProxy.getClass().getDeclaredMethod("sayName");
sayName.setAccessible(true);
sayName.invoke(realSubject);
=========
报错:Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
    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:498)
    at com.gary.spring.proxy.TestCglib.main(TestCglib.java:27)

CGLIBAopProxy

Spring AOP(3)基于XML理解代理类的创建 描述了AbstractAutoProxyCreator创建代理类的主要步骤,最后一行proxyFactory.getProxy(getProxyClassLoader())就是决定是 CGLIBAopProxy 还是 JdkDynamicAopProxy 来创建代理对象。

JdkDynamicAopProxy的getProxy()比较简单,直接通过Proxy.newProxyInstance()创建代理对象。

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

CGLIBAopProxy的getProxy()稍微复杂一点,跟上面CGLIB原理的示例代码一样,需要通过Enhancer来创建代理类,这里主要就是主装callback(即MethodInteceptor)

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
    }

    try {
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class<?> proxySuperClass = rootClass;
        if (ClassUtils.isCglibProxyClass(rootClass)) {
            proxySuperClass = rootClass.getSuperclass();
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        enhancer.setSuperclass(proxySuperClass);
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
        //这里是核心方法,组装callback
        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    catch (CodeGenerationException | IllegalArgumentException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                ": Common causes of this problem include using a final class or a non-visible class",
                ex);
    }
    catch (Throwable ex) {
        // TargetSource.getTarget() failed
        throw new AopConfigException("Unexpected AOP exception", ex);
    }
}

callback就是代理类增强的地方,类似InvocationHandler的invoke()方法。

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    // Parameters used for optimization choices...
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isFrozen = this.advised.isFrozen();
    boolean isStatic = this.advised.getTargetSource().isStatic();

    // Choose an "aop" interceptor (used for AOP calls).
    //这个类似InvocationHandler,提供Invoke()方法
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

    // Choose a "straight to target" interceptor. (used for calls that are
    // unadvised but can return this). May be required to expose the proxy.
    Callback targetInterceptor;
    if (exposeProxy) {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
    }
    else {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
    }

    // Choose a "direct to target" dispatcher (used for
    // unadvised calls to static targets that cannot return this).
    Callback targetDispatcher = (isStatic ?
            new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
    //组装mainCallBacks
    Callback[] mainCallbacks = new Callback[] {
            //aopInterceptor和targetInterceptor是固定一直有的
            aopInterceptor,  // for normal advice
            targetInterceptor,  // invoke target without considering advice, if optimized
            new SerializableNoOp(),  // no override for methods mapped to this
            targetDispatcher, this.advisedDispatcher,
            new EqualsInterceptor(this.advised),
            new HashCodeInterceptor(this.advised)
    };

    Callback[] callbacks;

    // If the target is a static one and the advice chain is frozen,
    // then we can make some optimizations by sending the AOP calls
    // direct to the target using the fixed chain for that method.
    if (isStatic && isFrozen) {
        Method[] methods = rootClass.getMethods();
        Callback[] fixedCallbacks = new Callback[methods.length];
        this.fixedInterceptorMap = new HashMap<>(methods.length);

        // TODO: small memory optimization here (can skip creation for methods with no advice)
        for (int x = 0; x < methods.length; x++) {
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
            fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                    chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
            this.fixedInterceptorMap.put(methods[x].toString(), x);
        }

        // Now copy both the callbacks from mainCallbacks
        // and fixedCallbacks into the callbacks array.
        callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
        System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
        System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
        this.fixedInterceptorOffset = mainCallbacks.length;
    }
    else {
        callbacks = mainCallbacks;
    }
    return callbacks;
}

那么方法调用的时候,肯定是走DynamicAdvisedInterceptor的intercept()。跟JdkDynamicAopProxy的invoke()方法类似。

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

    private final AdvisedSupport advised;

    public DynamicAdvisedInterceptor(AdvisedSupport advised) {
        this.advised = advised;
    }

    @Override
    @Nullable
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        Object target = null;
        TargetSource targetSource = this.advised.getTargetSource();
        try {
            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
            // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
            target = targetSource.getTarget();
            Class<?> targetClass = (target != null ? target.getClass() : null);
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            Object retVal;
            // Check whether we only have one InvokerInterceptor: that is,
            // no real advice, but just reflective invocation of the target.
            if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                // We can skip creating a MethodInvocation: just invoke the target directly.
                // Note that the final invoker must be an InvokerInterceptor, so we know
                // it does nothing but a reflective operation on the target, and no hot
                // swapping or fancy proxying.
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                //直接调用真实对象
                retVal = methodProxy.invoke(target, argsToUse);
            }
            else {
                // We need to create a method invocation...
                //  通过CglibMethodInvocation来调用proceed()
                retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
            }
            retVal = processReturnType(proxy, target, method, retVal);
            return retVal;
        }
        finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }
………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览