代理模式、策略模式

靜態代理:
代理前,所有東西都已知
動態代理:
在代理之前,都是未知的

package jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKmeipo implements InvocationHandler {
    private Person xiaoluo;
    public Object getInstance(Person xiaoluo){
        this.xiaoluo=xiaoluo;
        Class<?> clazz=xiaoluo.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this::invoke);
    }
//method就是測試時,調用方法的反射對象;


//  1拿到被代理類對象的引用,並獲取到所有接口,通過反射獲取,method就是測試時,調用方法的反射對象;
//        2JDK的Proxy通過newProxyInstance自動生成一個新類,該類實現被代理類所有方法
//        3動態生成JAVA代碼,把新加的業務邏輯通過一定的方式去調用
//        Xiaoluo obj=(Xiaoluo) new Cglib58().getInstance(Xiaoluo.class);
//        obj.findLove();
//        4編譯新生成的JAVA代碼.class
//        5再重新加載到JVM中
//                上述過程叫字節碼重組
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("==========");
            method.invoke(this.xiaoluo,args);
            System.out.println("==========");
            return null;
        }
    }

自動生成的新類

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jdk.Person;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m5;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void findLove() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buyHouse() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findJob() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2

) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
static {
    try {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m4 = Class.forName("jdk.Person").getMethod("findLove");
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m3 = Class.forName("jdk.Person").getMethod("buyHouse");
        m5 = Class.forName("jdk.Person").getMethod("findJob");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
        throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
        throw new NoClassDefFoundError(var3.getMessage());
    }
    }
}

newInstance、invoke參數詳解
method就是測試時,調用方法的反射對象;

https://www.cnblogs.com/fengmingyue/p/6092151.html

JDK中動態代理,只能代理一個接口 InvocationHandler(cglib代理對象靈活,若代理接口,則使用代理其實現類的方式實現),否者報錯proxy0nJDKproxy0-n JDK中有規範,開頭的,通常是自動生成的

CGLIB

CGLib採用了非常底層的字節碼技術,其原理是通過字節碼技術爲一個類創建子類,並在子類中採用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎。
調用過程:代理對象調用this.setPerson方法->調用攔截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理對象setPerson方法
cglib代理中需要的包
asm,cglib
asm框架解析
https://www.cnblogs.com/clds/p/4985893.html
代理模式作用:
AOP實現,攔截器,自己不想做但又不得不做的事的增強
**代理:**靜態、動態;代理角色、被代理的角色(目標對象)

性能比較

CGLib創建的動態代理對象性能比JDK創建的動態代理對象的性能高不少,Cglib動態代理執行代理方法效率之所以比JDK的高是因爲Cglib採用了FastClass機制,它的原理簡單來說就是:爲代理類和被代理類各生成一個Class,這個Class會爲代理類或被代理類的方法分配一個index(這裏的返回值,通過該返回值,查找對應的方法,即通過一個int參數,找到方法進行,最終在methodProxy.invokeSuper()執行方法

 public int getIndex(Signature var1){
     String var10000 = var1.toString();
            switch(var10000.hashCode()) {

        case -2071771415:
            if (var10000.equals("CGLIB$clone$6()Ljava/lang/Object;")) {
                return 23;
            }
            break;
        case -2055565910:
            if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 12;
            }
            break; 
return -1;
}

)。這個index當做一個入參,FastClass就可以直接定位要調用的方法直接進行調用,這樣省去了反射調用,所以調用效率比JDK動態代理通過反射調用高。
參考:https://blog.csdn.net/weixin_33883178/article/details/92375100
但是CGLib在創建代理對象時所花費的時間卻比JDK多得多(JDK和Cglib都是在運行期生成字節碼,JDK是直接寫Class字節碼,Cglib(適用於單例模式)使用ASM框架寫Class字節碼,Cglib代理實現更復雜,生成代理類比JDK效率低。),所以對於單例的對象,因爲無需頻繁創建對象,用CGLib合適,反之,使用JDK方式要更爲合適一些。同時,由於CGLib由於是採用動態創建子類的方法,對於final方法,無法進行代理。

FastClass並不是跟代理類一塊生成的,而是在第一次執行MethodProxy invoke/invokeSuper時生成的並放在了緩存中。

如下可獲得反編譯獲得生成的類

package cglib;


import jdk.Person;
import net.sf.cglib.core.DebuggingClassWriter;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class CglibProxyTest {
    public static void main(String[] args) {
        try {
            //只能實現接口的實現類
//獲得生成類                System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\IDEAWorkSpace\\proxy\\src\\$Cglib0.class");
                Mingjie obj=(Mingjie) new Cglib58().getInstance(Mingjie.class);
                obj.findLove();
    }catch (Exception e){
        e.printStackTrace();
    }
//
    }
}

總結

1.JDK動態代理是實現了被代理對象的接口,Cglib是繼承了被代理對象。

2.JDK和Cglib都是在運行期生成字節碼,JDK是直接寫Class字節碼,Cglib(適用於單例模式)使用ASM框架寫Class字節碼,Cglib代理實現更復雜,生成代理類比JDK效率低。

3.JDK調用代理方法,是通過反射機制調用,Cglib是通過FastClass機制直接調用方法,Cglib執行效率更高。

Cglib執行流程:

通過Hnhancer創建新生成的代理類(asm框架字節碼創建類)->
代理類繼承被代理類方法,當測試類中調用

Mingjie obj=(Mingjie) new Cglib58().getInstance(Mingjie.class);
            obj.findLove();

類似下面的方法時,實際上調用的是新生成的代理類中的方法(即被代理類的子類的方法),子類方法中對應的findlove()內部,通過創建一個MethodIntercept對象,來調用intercept(…)方法->
這時intercept(…)中的methodProxy.invokeSuper(…)會被執行

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            //FastClass中有一個getIndex方法,參數是代理類和被代理類的signature簽名,會返回一個int值,通過int值,FastClass中的invoke方法,可以找到對應代理類方法
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }



private static class FastClassInfo {
    FastClass f1;//被代理類FastClass
    FastClass f2;//代理類FastClass
    int i1; //被代理類的方法簽名(index)
    int i2;//代理類的方法簽名
 
    private FastClassInfo() {
    }
}


//MethodProxy invoke/invokeSuper都調用了init()
private void init() {
        if(this.fastClassInfo == null) {
            Object var1 = this.initLock;
            synchronized(this.initLock) {
                if(this.fastClassInfo == null) {
       MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);//如果緩存中就取出,沒有就生成新的FastClass
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);//獲取方法的index
                  	fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
 
    }

,且同時生成兩個FastClass,與新生成的代理類和被代理類對應,FastClass中有一個getIndex方法,參數是代理類和被代理類的signature簽名,會返回一個int值,通過int值,得到對應的新生成的代理類方法。這時候,被代理類的方法就會被執行了。

策略模式

舉例

購物付款流程圖

在這裏插入圖片描述
目錄結構
在這裏插入圖片描述
創建訂單類:
Order.java

import pay.PayState;
import pay.PayType;
import pay.Payment;

public class Order {
    private String uid;
    private String orderId;
    private double amount;

    public Order(String uid,String orderId,double amount){
        this.amount=amount;
        this.orderId=orderId;
        this.uid=uid;
    }
    public PayState pay(PayType payType){
        return payType.get().pay(this.uid,this.amount);
    }
}

數據持久類
Paystate.java

package pay;

public class PayState {
    private int code;
    private Object data;
    private String msg;
    public PayState(int code, Object data, String msg){
        this.code=code;
        this.data=data;
        this.msg=msg;
    }
    @Override
    public String toString() {
        return ("支付狀態{" +
                 code +"}," +
                 msg +
                ", 交易詳情" + data);
    }
}

定義接口

Payment.java

package pay;

public interface Payment {
    public PayState pay(String uid, double amount);
}

實現接口
JdPay.java

package pay;
public class Jdpay implements Payment{
@Override
public PayState pay(String uid, double amount) {
    System.out.println("京	
    東,開始扣款");
    return new PayState(200,amount,"支付成功");
}

}
枚舉類:讓每一種支付方式可一直使用
Paytype.java

package pay;

public enum PayType {
    Ali_Pay(new AliPay()),Jd_Pay(new Jdpay());
    private Payment payment;
    PayType(Payment payment){
        this.payment=payment;
    }
    public Payment get(){
        return this.payment;
    }
}

測試類

import pay.Jdpay;
import pay.PayType;

public class Paytest {
    public static void main(String[] args) {
        Order order=new Order("1","1111111",333.3);
        System.out.println(order.pay(PayType.Ali_Pay));
    }
}

現在直接使用各支付方式支付,使用時,只需要在枚舉類中添加,代碼更健壯,更易修改、維護

代理角色、被代理的角色(目標對象)

策略模式:只有選擇權(注重可擴展)

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