1. 程式人生 > >Spring AOP學習筆記04:AOP核心實現之建立代理

Spring AOP學習筆記04:AOP核心實現之建立代理

  上文中,我們分析了對所有增強器的獲取以及獲取匹配的增強器,在本文中我們就來分析一下Spring AOP中另一部分核心邏輯--代理的建立。這部分邏輯的入口是在wrapIfNecessary()方法中緊接著增強器的獲取之後的createProxy():

protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    ProxyFactory proxyFactory = new ProxyFactory();
    // 獲取當前類中相關屬性
    proxyFactory.copyFrom(this);
    // 決定對於給定的bean是否應該使用targetClass而不是它的介面進行代理
    if (!shouldProxyTargetClass(beanClass, beanName)) {
        // Must allow for introductions; can't just set interfaces to
        // the target's interfaces only.
        Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
        for (Class<?> targetInterface : targetInterfaces) {
            // 新增代理介面
            proxyFactory.addInterface(targetInterface);
        }
    }

    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    for (Advisor advisor : advisors) {
        // 加入增強器
        proxyFactory.addAdvisor(advisor);
    }
    // 設定要代理的類
    proxyFactory.setTargetSource(targetSource);
    // 定製代理
    customizeProxyFactory(proxyFactory);
    // 用來控制代理工廠被配置之後,是否還允許修改通知
    // 預設值為false(即在代理被配置之後,不允許修改代理的配置)
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(this.proxyClassLoader);
}

  對於代理類的建立及處理,Spring委託給了ProxyFactory進行處理,而在上面的函式中主要是對ProxyFactory的初始化操作,為真正的代理建立做準備,初始化包括如下內容:

  • 獲取當前中的屬性;
  • 新增代理介面;
  • 封裝Advisor並加入到ProxyFactory中;
  • 設定要代理的類;
  • 對代理工廠進行定製化處理,供子類實現;
  • 進行獲取代理操作;

  其中封裝Advisor並加入到ProxyFactory中以及建立代理是兩個相對繁瑣的過程,可以通過ProxyFactory提供的addAdvisor方法直接將增強器置入代理建立工廠中,但是將攔截器封裝為增強器還是需要一定的邏輯的。

protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
    // 解析註冊的所有interceptorName
    Advisor[] commonInterceptors = resolveInterceptorNames();

    List<Object> allInterceptors = new ArrayList<Object>();
    if (specificInterceptors != null) {
        // 加入攔截器
        allInterceptors.addAll(Arrays.asList(specificInterceptors));
        if (commonInterceptors != null) {
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }
    if (logger.isDebugEnabled()) {
        int nrOfCommonInterceptors = (commonInterceptors != null ? commonInterceptors.length : 0);
        int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
        logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
    }

    Advisor[] advisors = new Advisor[allInterceptors.size()];
    for (int i = 0; i < allInterceptors.size(); i++) {
        // 將攔截器進行包裝轉化為Advisor
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
}

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    // 如果要封裝的物件本身就是Advisor型別的那麼無需再做過多處理
    if (adviceObject instanceof Advisor) {
        return (Advisor) adviceObject;
    }
    // 如果不是Advisor與Advice兩種型別,則丟擲異常
    if (!(adviceObject instanceof Advice)) {
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    if (advice instanceof MethodInterceptor) {
        // 如果是MethodInterceptor型別則使用DefaultPointcutAdvisor封裝
        return new DefaultPointcutAdvisor(advice);
    }
    // 如果存在Advisor的介面卡那麼也同樣需要進行封裝
    for (AdvisorAdapter adapter : this.adapters) {
        // Check that it is supported.
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

  因為Spring中涉及過多的攔截器、增強器、增強方法等方式來對邏輯進行增強,所以非常有必要將增強器封裝成Advisor來進行代理的建立,完成了增強的封裝過程,那麼接下來就是最重要的一步--代理的建立與獲取。

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

1. 建立代理

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    // 建立代理
    return getAopProxyFactory().createAopProxy(this);
}

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface()) {
            return new JdkDynamicAopProxy(config);
        }
        return CglibProxyFactory.createCglibProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

  到這裡已經完成了代理的建立了,不管我們之前是否有閱讀過Spring的原始碼,但是應該或多或少都聽說過對於Spring的代理中JDKProxy的實現CglibProxy的實現。Spring是如果選取的呢?現在我們就從原始碼的角度分析,看看到底Spring是如何選擇代理方式的。

  從上面程式碼中的判斷條件可以看到3個方面影響著Spring的判斷:

  • optimize:用來控制通過CGLIB建立的代理是否使用激進的優化策略。除非完全瞭解AOP代理如何處理優化,否則不推薦使用者使用這個設定。目前這個屬性僅用於CGLIB代理,對於JDK動態代理(預設代理)無效。
  • proxyTargetClass:這個屬性為true時,目標類本身被代理而不是目標類的介面,並且使用CGLIB方式建立代理,xml檔案配置方式為:<aop:aspectj-autoproxy proxy-target-class="true"/>。
  • hasNoUserSuppliedProxyInterfaces:是否存在代理介面。

  下面是對JDK與Cglib方式的總結:

  • 如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP;
  • 如果目標物件實現了介面,可以強制使用CGLIB實現AOP;
  • 如果目標物件沒有實現介面,則必須採用CGLIB方式實現AOP,Spring會自動切換;

如何強制使用CGLIB實現AOP?

  • 新增CGLIB庫,Spring_HOME/cglib/*.jar
  • 在Spring配置檔案中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

JDK動態代理和CGLIB位元組碼生成的區別?

  • JDK動態代理只能對實現了介面的類生成代理,而不能針對類。
  • CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,因為是繼承,所以該類或方法最好不要宣告成final。

 

2. 獲取代理

  確定了使用哪種代理方式之後便可以進行代理的建立了,Spring中主要使用了兩種方式來實現代理的建立:JDK動態代理、cglib,我們一一來解析。

2.1 JDK動態代理方式

  這裡直接定位到JdkDynamicAopProxy中的getProxy():

public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

  JDK動態代理的使用關鍵是建立自定義的InvocationHandler,而InvocationHandler中包含了需要覆蓋的函式getProxy,這裡其實JdkDynamicAopProxy就是繼承了InvocationHandler的,所以上面的方法正是完成了這個操作,並且我們還可以推斷出,在JdkDynamicAopProxy中一定會有一個invoke函式,並且JdkDynamicAopProxy會把AOP的核心邏輯寫在其中,找一下,一定會有這樣一個函式的:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Class<?> targetClass = null;
    Object target = null;

    try {
        // 處理equals方法
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        }
        // 處理hash方法
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            return hashCode();
        }
        if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
        // 有時候目標物件內部的自我呼叫將無法實施切面中的增強,則需要通過此屬性暴露代理
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }

        // 獲取當前方法的攔截器鏈
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            // 如果沒有任何攔截器鏈則直接呼叫切點方法
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
            // 將攔截器封裝在ReflectiveMethodInvocation,以便於使用其proceed進行鏈式呼叫攔截器
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 執行攔截器鏈
            retVal = invocation.proceed();
        }

        // 返回結果
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                    "Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

  上面的invoke()函式最主要的工作就是建立了一個攔截器鏈,並使用ReflectiveMethodInvocation類進行了鏈的封裝,而在ReflectiveMethodInvocation類的proceed方法中實現了攔截器的逐一呼叫,那麼我們就繼續來探究,在proceed方法中是怎麼實現諸如前置增強在目標方法前呼叫以及後置增強在目標方法後呼叫的邏輯的。

public Object proceed() throws Throwable {
    //    執行完所有增強後執行切點方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    // 獲取下一個要執行的攔截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // 動態匹配
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 若未匹配則不執行攔截器,呼叫攔截器鏈中下一個
            return proceed();
        }
    }
    else {
        // 普通攔截器,直接呼叫。將this作為引數傳入以保證當前例項中呼叫鏈的執行
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

  ReflectiveMethodInvocation的主要職責是維護一個鏈式呼叫的計數器,記錄著當前呼叫鏈的位置,以便鏈可以有序地進行下去。其實在這個方法中並沒有我們設想的維護各種增強的順序,但是細心的讀者可能會發現,這部分工作其實是委託給了各個增強器來實現,前面有說到。

2.2 Cglib方式

  完成CGLIB代理的類是委託給CglibAopProxy類去實現的,我們來一探究竟。根據前面的分析,我們容易判斷出來,CglibAopProxy的入口應該也是在getProxy():

public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating CGLIB proxy: target source is " + 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);
            }
        }

        // 驗證Class
        validateClassIfNecessary(proxySuperClass);

        // 建立及配置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 MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
        enhancer.setInterceptDuringConstruction(false);
        // 設定攔截器
        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);
        enhancer.setCallbacks(callbacks);

        // 生成代理類及建立代理物件
        Object proxy;
        if (this.constructorArgs != null) {
            proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
        }
        else {
            proxy = enhancer.create();
        }

        return proxy;
    }
    catch (CodeGenerationException ex) {
        catch若干異常。。。
    }
}

  上面的函式中就是一個完整建立Enhancer的過程,詳細可以參考Enhancer的文件,這裡最重要的是通過getCallbacks()方法設定攔截器鏈。

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    // 對於expose-proxy屬性的處理
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isFrozen = this.advised.isFrozen();
    boolean isStatic = this.advised.getTargetSource().isStatic();

    // 將攔截器封裝在DynamicAdvisedInterceptor中
    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());
    }

    // 將攔截器加入到callback中
    Callback targetDispatcher = isStatic ?
            new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();

    Callback[] mainCallbacks = new Callback[] {
            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 optimisations 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<String, Integer>(methods.length);

        // TODO: small memory optimisation 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;
}

  在getCallback()中Spring考慮了很多情況,有很多的細節,但是我們閱讀原始碼是沒有必要也沒有那麼多精力把每一個細節都弄明白的,重點是抓住主幹即可。這裡只需要理解最常用的,比如將advised屬性封裝在DynamicAdvisedInterceptor並加入在callbacks中,這麼做的目的是什麼呢?在CGLIB中對於方法的攔截是通過將自定義的攔截器(實現了MethodInterceptor介面的類)加入Callback中並在呼叫代理時直接啟用攔截器中的intercept()方法來實現的,而在getCallback()方法中正好有這一部分功能的實現,DynamicAdvisedInterceptor繼承自MethodInterceptor,加入Callback中後,在再次呼叫代理時會直接呼叫其intercept()方法,由此推斷,對於CGLIB方式實現的代理,其核心邏輯應該是在DynamicAdvisedInterceptor中的intercept()方法中的:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Class<?> targetClass = null;
    Object target = null;
    try {
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // May be null. Get as late as possible to minimize the time we
        // "own" the target, in case it comes from a pool...
        target = getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        // 獲取攔截器鏈
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // 如果攔截器鏈為空則直接啟用原方法
            retVal = methodProxy.invoke(target, args);
        }
        else {
            // 鏈式呼叫
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        if (target != null) {
            releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

  這裡的實現與JDK動態代理方式實現代理中的invoke方法大同小異,都是首先構造攔截器鏈,然後封裝此鏈進行串聯呼叫,不同的是在JDK動態代理的方式中是直接構造ReflectiveMethodInvocation,而在cglib中則是使用CglibMethodInvocation,其是繼承自ReflectiveMethodInvocation,但是proceed()方法並沒有重寫。

 

3. 總結

  本文著重分析了Spring AOP實現原理中代理物件的建立過程,在bean的初始化過程中會執行Spring的後置處理器,這裡會去判斷這個bean是否需要增強,如果需要則會根據Aspect中定義的增強資訊,對指定bean進行增強,也就是建立一個代理物件。對代理物件的建立有兩種方式,一種是通過JDK動態代理的方式,另一種是通過cglib的方式。