Spring/Boot/Cloud系列知識(4)— — 代理模式(下)

本文轉自:https://blog.csdn.net/yinwenjie/article/details/78048671

============================================= 
(接上文《Spring/Boot/Cloud系列知識(3)——代理模式(中)》)

3.3 Proxy.newProxyInstance內部如何完成工作的

我們來看看org.mockito.cglib.proxy.Proxy.newProxyInstance這個方法內部的代碼:

public class Proxy implements Serializable {
    ......

    // 這個常量BAD_OBJECT_METHOD_FILTER(代理選擇器)的設定,等一下會用到
    // 實際上代碼也清楚,一般情況下執行第0個代理器,如果是代理執行java.lang.Object中的方法
    // 且這些方法又不是hashCode()、equals()、toString(),則執行第1個代理器
    private static final CallbackFilter BAD_OBJECT_METHOD_FILTER = new CallbackFilter() {
        public int accept(Method method) {
            if (method.getDeclaringClass().getName().equals("java.lang.Object")) {
                String name = method.getName();
                if (!(name.equals("hashCode") ||
                      name.equals("equals") ||
                      name.equals("toString"))) {
                    return 1;
                }
            }
            return 0;
        }
    };

    ......

    // private for security of isProxyClass
    private static class ProxyImpl extends Proxy {
        protected ProxyImpl(InvocationHandler h) {
            super(h);
        }
    }

    ......

    // 該方法中使用了Cglib中對ASM freamework的封裝,動態創建一個class定義
    public static Class getProxyClass(ClassLoader loader, Class[] interfaces) {
        Enhancer e = new Enhancer();
        // 爲這個class設置一個父類,這個父類名叫ProxyImpl,其中定義了一個構造函數
        // 那個構造函數需要傳入個代理器對象
        e.setSuperclass(ProxyImpl.class);
        // 然後爲這個class設置接口,請注意,可以設置多個接口哦
        e.setInterfaces(interfaces);
        // 爲這個動態class設置代理器類型,設定了這個方法就應該使用CallbackFilter設定代理選擇器(過濾器)
        e.setCallbackTypes(new Class[]{
            InvocationHandler.class,
            NoOp.class,
        });
        // 這個我們使用了Proxy中,在前文定義好的BAD_OBJECT_METHOD_FILTER常量,請參見
        //  
        e.setCallbackFilter(BAD_OBJECT_METHOD_FILTER);
        e.setUseFactory(false);
        // 最後創建這個動態class(注意是創建class,並不是這個class的實例)
        return e.createClass();
    }

    ......

    // newProxyInstance方法中實際上就是兩句話,重要的代碼都在getProxyClass這個方法中。
    public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) {
        try {
            // 該方法用來動態創建一個class,請參考方法中的內容
            Class clazz = getProxyClass(loader, interfaces);
            // 很顯然,通過以上方法我們拿到了一個動態class,這個class有三個重要特點,
            // 1、這個class有一個帶有參數的構造函數,這個參數就是需要我們傳入代理器接口的一個實現實例。
            // 2、這個class實現了我們需要它實現的一個或者多個接口——沒有實現代碼,但是有這樣的類結構
            // 3、這個class設定了兩個代理器,通常執行第0個代理器,就是我們傳入的InvocationHandler h對象
            // 另外,如果是代理執行java.lang.Object中的方法
            // 且這些方法又不是hashCode()、equals()、toString(),則執行第1個代理器
            //
            // 接着,我們執行第二句代碼,這句代碼初始化一個這個動態類的示例,並傳入代理器實例對象
            return clazz.getConstructor(new Class[]{ InvocationHandler.class }).newInstance(new Object[]{ h });
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

    ......
}

要閱讀本小節以上代碼片段,請從上篇文章中newProxyInstance()方法開始看。

4 Cglib和JDK動態代理在Spring中的應用

4.1、再次說明JDK動態代理的實例

Spring中使用JDK動態代理的情況已將在前文中介紹過一次(請參見前文《Spring/Boot/Cloud系列知識(3)——代理模式(中)》),這裏再舉一個實際應用——Spring Data JPA組件。我們通過實現了Java JPA規範的Hibernate組件,定義了一個AgentRepository接口,注意這個接口沒有任何實現,只有接口、接口方法和接口方法註解上的HQL/SQL操作語句(如果您的接口方法遵循了JPA規範標準定義,甚至沒有操作語句的註解也成)。接着我們在某個Service中依賴注入這個AgentRepository接口對象。

  • 以下是AgentRepository接口的定義。請注意,這個接口遵循JPA規範,其下並沒有任何實現類:
     
@Repository("agentRepository")
public interface AgentRepository extends JpaRepository<AgentEntity, String>, JpaSpecificationExecutor<AgentEntity> {

  AgentEntity findByAccountAndStatus(String account , UseStatus status);

  AgentEntity findByAccount(String account);

  @Modifying
  @Query(value="insert into role_agent(agent_id , role_id) value (:agentId , '2bb')" , nativeQuery=true)
  void bindAgentRole(@Param("agentId") String agentId);
}
  • 接着我們在其它代碼中,注入這個對象,並通過debug模式觀察AgentRepository接口實例的對象引用情況(關於Spring-data-JPA這個組件本專題後續文章還會詳細講解):

4.2、Cglib代理在Spring中的應用

(1)Cglib的封裝

實際上Spring組件並沒有直接使用Cglib的原生類/工具,而是通過Spring Core組件對Cglib組件進行了封裝/重寫,在Spring Core組件中的org.springframework.cglib包中:
 

在介紹Cglib在Spring生態中的具體應用前,我們先來看看在以上的代碼示例中Cglib代理的應用位置:

通過MyService接口實例在Debug模式下顯示的內容可以看到,myService是一個Spring通過Cglib組件動態生成的代理實例,這裏有7個代理器對象,負責在不同的方法被調用時進行代理。

(2)Cglib的代理器原理

Spring AOP組件中,默認使用Cglib動態代理。它對Cglib動態代理的支持,主要由Spring-cor組件提供,而Spring AOP組件依賴於Spring Core組件。前者Spring AOP組件中的org.springframework.aop.framework.DefaultAopProxyFactory類將決定和生成具體的代理器——JdkDynamicAopProxy或者ObjenesisCglibAopProxy(這是CglibAopProxy類的子類)。我們來看看org.springframework.aop.framework.CglibAopProxy這個類中的一些重要代碼——主要是其中如何組裝各種代理器的:
 

public Object getProxy(ClassLoader classLoader) {
    ......

    // 這是Spring Core中封裝的org.springframework.cglib.proxy.Enhancer類
    // 可見Spring aop組件中依賴於spring core組件。
    // Configure CGLIB Enhancer... 
    Enhancer enhancer = createEnhancer();
    // ========以下代碼用於動態生成被代理的proxySuperClass的代理類
    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[] 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);

    ......
}

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    // Parameters used for optimisation choices...
    // advised是AOP配置信息的一個對象(AdvisedSupport extends ProxyConfig implements Advised)
    // 從內部代碼看,exposeProxy屬性和frozen屬性來自於它的父類ProxyConfig,默認值都爲false
    boolean exposeProxy = this.advised.isExposeProxy();
    boolean isFrozen = this.advised.isFrozen();
    // targetSource是org.springframework.aop.TargetSource接口的一個實現,
    // 它描述了被代理/被切面的實際對象。其下有很多實現,包括但不限於:PrototypeTargetSource、ThreadLocalTargetSource、
    // EmptyTargetSource、SimpleBeanTargetSource、CommonsPool2TargetSource等
    // 視不同的代理目標、環境配置,使用的實現類不一樣。
    // 其中SingletonTargetSource類是TargetSourcede的默認實現,當被代理的目標是來自於Spring IOC容器的簡單對象時,就使用此代理類實現代理
    // SingletonTargetSource類實例中的isStatic屬性,默認返回true
    boolean isStatic = this.advised.getTargetSource().isStatic();

    ......

    // 這個數組是實現了Callback接口的多個對象,就是在上圖中看到的7個代理器對象。
    // 其中aopInterceptor對象是DynamicAdvisedInterceptor代理器的實現
    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(isStatic && isFrozen) {
        ......
    } eles {
        callbacks = mainCallbacks;
    }
    return callbacks;
}

<==== 上一篇  =====>

Spring/Boot/Cloud系列知識(3)— — 代理模式(中)

<==== 下一篇  =====>

Spring/Boot/Cloud系列知識(5)— — Spring EL(1)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章