成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

web中動態(tài)代理模式是什么

本篇內(nèi)容介紹了“web中動態(tài)代理模式是什么”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)專注于臨城網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供臨城營銷型網(wǎng)站建設(shè),臨城網(wǎng)站制作、臨城網(wǎng)頁設(shè)計、臨城網(wǎng)站官網(wǎng)定制、微信小程序開發(fā)服務(wù),打造臨城網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供臨城網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

一、什么是代理模式

代理模式的定義:代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。

web中動態(tài)代理模式是什么

代理模式的分類:代理模式分為靜態(tài)代理和動態(tài)代理

二、靜態(tài)代理

以簡單的事務(wù)處理為例

interface
public interface UserDao{
     void save();
}
實現(xiàn)類
public class UserDaoImpl implements UserDao{
      public void save(){//保存}
}
事務(wù)處理代理類
public class TransactionHandler implements UserDao{
     private UserDaoImpl userDao;//目標代理對象
     public TransactionHandler(UserDao userDao){
         this.useDao = userDao;
     }
    
     public void save(){
          //開啟事務(wù)
          userDao.save();
         //結(jié)束事務(wù)
     }
}

靜態(tài)代理很簡單,但是有一個缺點,如果要對很多類、方法進行代理只能一個一個寫多個代理類,無法做到代碼重用的目的

三、動態(tài)代理

以JDK動態(tài)代理實現(xiàn)為例

//事務(wù)處理代理類
public class TransactionHandler implements InvocationHandler{
     private Object target;//目標代理對象
     public TransactionHandler(Object target){
         this.target= target;
     }
     
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         //開啟事務(wù)
         Object result = method.invoke(target,args);
         //結(jié)束事務(wù)
         return result
     }
}
//調(diào)用方法
public class Main {
    public static void main(String[] args) {
        Object target = new UserDaoImpl();
        TransactionHandler handler = new TransactionHandler(target);
        UserDao userDao = (UserDao)Proxy.newProxyInstance(
           target.getClass().getClassLoader(),
           target.getClass().getInterfaces(),
           handler);
        userDao.save();
    }

在這里有兩個疑問,我們在后面慢慢解開它們的面紗

1.如何生成代理類?2.如何執(zhí)行代理方法?

四、JDK動態(tài)代理原理

我們先看Proxy.newProxyInstance方法原碼

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
                                                               InvocationHandler h) throws IllegalArgumentException {
.....
Class<?> cl = getProxyClass0(loader, intfs);//獲取或生成代理類
....
final Constructor<?> cons = cl.getConstructor(constructorParams);//得到參數(shù)類型是InvocationHandler.class構(gòu)造函數(shù)
....
return cons.newInstance(new Object[]{h});//生成代理實例
....
}

這個方法主要就做三件事

1.獲取或生成代理類

2.得到參數(shù)類型是InvocationHandler.class構(gòu)造函數(shù)

3.生成代理實例

我們現(xiàn)在來看看它是如何生成代理類的??磄etProxyClass0源碼

private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {
....
   return proxyClassCache.get(loader, interfaces);//proxyClassCache->WeakCache
}
public V get(K key, P parameter) {
....
    Object cacheKey = CacheKey.valueOf(key, refQueue);//將ClassLoader包裝成CacheKey, 作為一級緩存的key
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);//獲得classload緩存
    if (valuesMap == null) {
         ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, 
                 valuesMap = new ConcurrentHashMap<>());//以CAS方式放入, 如果不存在則put,否則返回舊值
         //問題:為什么要用二級緩存?為什么要map.putIfAbsent?
    }
    Supplier<V> supplier = valuesMap.get(subKey);
    while (true) {
        if (supplier != null) {
            V value = supplier.get();//得到代理類
            if (value != null) {
                 return value;//取到了直接返回
            }
        }
        //如果為null,生成代理類工廠
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }
        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                supplier = factory;//如果沒有舊值,將新值賦給引用對象,循環(huán)退出
            }//如果替換失敗,說明已經(jīng)有值,重新循環(huán)
        }else{//其它線程修改了值, 那么就將原先的值替換
             if (valuesMap.replace(subKey, supplier, factory)) {
                   supplier = factory;
             } else {
                   supplier = valuesMap.get(subKey);//替換失敗, 繼續(xù)使用原先的值
             }
        } 
    }
}

這個方法主要就是看緩存里是否存在代理工廠類,如果存在直接調(diào)用get()返回,這里緩存用的是WeakCache,新生代回收時就會被回收,不會占用內(nèi)存

如果緩存中沒有就通過new Factory生成一個代理工廠。這里有一些線程安全方面的處理。

這里返回的是Supperlier.get(),現(xiàn)在看這個方法中做了些什么事情

private final class Factory implements Supplier<V> {
....
    public synchronized V get() {
        Supplier<V> supplier = valuesMap.get(subKey);
        if (supplier != this) {//在這里驗證supplier是否是Factory實例本身,因為可能被其它線程修改了
            return null;
        }
        V value = null;
        try{
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            //交給ProxyClassFactory去生成代理類
        }finally{
            if (value == null) {//如果生成代理類失敗, 就將這個二級緩存刪除
                 valuesMap.remove(subKey, this);
            }
        }
       ....
       return value;
    }
}

這個方法比較簡單主要是交給valueFactory.apply生成返回,valueFactory是ProxyClassFactory類

我們再看看這個方法里在做什么

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
.... 
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;//生成代理類默認是public final
    String proxyName = proxyPkg + proxyClassNamePrefix + num;//包名+前綴+序號
    //用ProxyGenerator來生成字節(jié)碼
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces, accessFlags);
    try{
        //根據(jù)字節(jié)碼生成代理類
        return defineClass0(loader, proxyName, proxyClassFile,0, proxyClassFile.length);
    } catch (ClassFormatError e) {....}
}

主要是一些規(guī)范定義,然后根據(jù)ProxyGenerator.generateProxyClass生成字節(jié)碼

再這個方法里做了些什么

public static byte[] generateProxyClass(String var0, Class<?>[] var1) {
   return generateProxyClass(var0, var1, 49);
}

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
   ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
   final byte[] var4 = var3.generateClassFile();
   ....
   return var4;
}

private byte[] generateClassFile() {
    //首先為代理類生成toString, hashCode, equals等代理方法
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);
    //遍歷每一個接口的每一個方法, 并且為其生成ProxyMethod對象
    for (int i = 0; i < interfaces.length; i++) {
        Method[] methods = interfaces[i].getMethods();
        for (int j = 0; j < methods.length; j++) {
             addProxyMethod(methods[j], interfaces[i]);
        }
     }
     ....//將類的屬性寫入bout流中
     return bout.toByteArray();
}

可以看出主要是分多步將被代理類的方法屬性等寫到字節(jié)流中

生成的代理類就如下

public class Proxy0 extends Proxy implements UserDao {
    //第一步, 生成構(gòu)造器
    protected Proxy0(InvocationHandler h) {
        super(h);
    }
    //第二步, 生成靜態(tài)域
    private static Method m1;   //hashCode方法
    private static Method m2;   //equals方法
    private static Method m3;   //toString方法
    private static Method m4;   //...
    
    //第三步, 生成代理方法
    @Override
    public int hashCode() {
        try {
            return (int) h.invoke(this, m1, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    
    @Override
    public boolean equals(Object obj) {
    }
    
    @Override
    public String toString() {
    }

    @Override
    public void save(User user) {
        try {
            //構(gòu)造參數(shù)數(shù)組, 如果有多個參數(shù)往后面添加就行了
            Object[] args = new Object[] {user};
            h.invoke(this, m4, args);//代理類增強方法
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    
    //第四步, 生成靜態(tài)初始化方法
    static {
        try {
            Class c1 = Class.forName(Object.class.getName());
            Class c2 = Class.forName(UserDao.class.getName());    
            m1 = c1.getMethod("hashCode", null);
            m2 = c1.getMethod("equals", new Class[]{Object.class});
            m3 = c1.getMethod("toString", null);
            m4 = c2.getMethod("save", new Class[]{User.class});
            //...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

五、CGLIB動態(tài)代理

先看純CGLIB是如何使用

//攔截類
public class Interceptor implements MethodInterceptor {
    public Object intercept(Object obj, Method method, Object[] args,    MethodProxy proxy) throws Throwable {
        //invokeSuper調(diào)用原方法,invoke對原類實例生效,調(diào)用代理方法
        //前置處理
        Object retVal =  proxy.invokeSuper(obj, args);
        //后置處理
        return retVal;
    }
}

public static void main(String[] args) {
    //實例化一個增強器,也就是cglib中的一個class generator
    Enhancer eh = new Enhancer();
    //設(shè)置目標類
    eh.setSuperclass(Target.class);
    // 設(shè)置攔截對象
    eh.setCallback(new Interceptor());
    // 生成代理類并返回一個實例
    Target t = (Target) eh.create();
    t.method();
}

具體如何生成代理類的源碼就不展開了,大體思路和JDK一致,最后生成代理類的方式有所不同。

先看生成的代理類

public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
    ....//equal tostring就不寫了,拿一個典型    
    final void CGLIB$g$0()//invokeSuper調(diào)用這個方法
    {
      super.g();
    }
    
    public final void g()//invoke調(diào)用這個方法
    {
      MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
      if (tmp4_1 == null)
      {
          CGLIB$BIND_CALLBACKS(this);
          tmp4_1 = this.CGLIB$CALLBACK_0;
      }
      if (this.CGLIB$CALLBACK_0 != null) {
          tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
      }
      else{
          super.g();
      }
    }
}

可以看出和JDK不同的是JDK是implements 原接口,CGLIB是extends原類,而且會生成兩個對應(yīng)的方法,供不同的調(diào)用。

CGLIB與JDK動態(tài)代理的區(qū)別 1、CGLIB代理類不需要實現(xiàn)接口,但是也不能是final型 2、CGLIB回調(diào)方法不再通過method.invoke反射來處理,采用效率更高的FastClass機制     FastClass機制就是對一個類的方法建立索引,通過索引來直接調(diào)用相應(yīng)的方法

例如:

//先通過獲得方法索引
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }

    //然后通過索引得到方法
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }

六、SPRING AOP

Spring Aop默認使用JDK動態(tài)代理,除非指定使用CGLIB動態(tài)代理,但對接口類只能使用JDK動態(tài)代理 具體代碼在DefaultAopProxyFactory

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);
        }
        if (!cglibAvailable) {
            throw new AopConfigException(
                    "Cannot proxy target class because CGLIB2 is not available. " +
                    "Add CGLIB to the class path or specify proxy interfaces.");
        }
        return CglibProxyFactory.createCglibProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

以JdkDynamicAopProxy為例我們來看spring是如何對方法進行有選擇性的增強的

1、生成代理類

@Override
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);//hanler傳入的是自己
}

可以看出增強的方法不是我們自己定義的邏輯,而是JdkDynamicAopProxy 那么JdkDynamicAopProxy 必然有一個invoke方法,再看這個方法怎么處理

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
.....
     //獲取攔截鏈
     List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
     if (chain.isEmpty()) {
         //直接調(diào)原方法返回
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
    }else{
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        //執(zhí)行攔截鏈
        retVal = invocation.proceed();
    }
....
}

“web中動態(tài)代理模式是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

本文題目:web中動態(tài)代理模式是什么
URL地址:http://jinyejixie.com/article32/pggipc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、電子商務(wù)、定制網(wǎng)站、響應(yīng)式網(wǎng)站外貿(mào)網(wǎng)站建設(shè)、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
桐柏县| 灵武市| 万全县| 广河县| 鸡泽县| 定陶县| 江门市| 武城县| 梓潼县| 恩施市| 麻城市| 河间市| 凤庆县| 茌平县| 镶黄旗| 盐亭县| 大洼县| 汨罗市| 宁夏| 天气| 夏邑县| 迭部县| 汾阳市| 十堰市| 平远县| 民丰县| 平谷区| 灵石县| 长春市| 汨罗市| 大新县| 博客| 石家庄市| 嘉善县| 天峨县| 普兰县| 定日县| 吴川市| 当阳市| 张家港市| 扬中市|