1、Proxy代理模式

1、Proxy 代理模式

  • 代理(Proxy)提供了對(duì)目標(biāo)對(duì)象另外的訪問(wèn)方式;即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
  • 代理模式可分為靜態(tài)代理和動(dòng)態(tài)代理(JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理)

1.1靜態(tài)代理

  • 靜態(tài)代理在使用時(shí),需要定義接口或者父類(lèi),被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類(lèi)。
1)定義接口
public interface Image {
   void display();
}
2)接口實(shí)現(xiàn)類(lèi),擴(kuò)展了新功能
public class RealImage implements Image {
 
   private String fileName;
 
   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
 
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
 
   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}
3)接口代理類(lèi),可以調(diào)用實(shí)現(xiàn)類(lèi)的功能。
public class ProxyImage implements Image{
 
   private RealImage realImage;
   private String fileName;
 
   public ProxyImage(String fileName){
      this.fileName = fileName;
   }
 
   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}
4)Demo,通過(guò)代理類(lèi)來(lái)調(diào)用實(shí)體類(lèi)功能。
public class ProxyPatternDemo {   
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg"); 
      // 圖像將從磁盤(pán)加載
      image.display(); 
      System.out.println("");
      // 圖像不需要從磁盤(pán)加載
      image.display();  
   }
}
5)執(zhí)行程序,輸出結(jié)果:
Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

1.2動(dòng)態(tài)代理

  • 代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理
  • JDK代理:只對(duì)有實(shí)現(xiàn)接口的目標(biāo)對(duì)象生成代理,通過(guò)反射機(jī)制實(shí)現(xiàn)aop的動(dòng)態(tài)代理,使用的是委派機(jī)制,在動(dòng)態(tài)生成的實(shí)現(xiàn)類(lèi)里委托處理器去調(diào)用原始實(shí)現(xiàn)類(lèi)的方法。
    -Cglib代理: 目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,也能生成代理,使用字節(jié)碼處理框架asm,修改字節(jié)碼生成子類(lèi),使用的是繼承機(jī)制,代理類(lèi)繼承被代理類(lèi)。

1.2.1 JDK 自帶的動(dòng)態(tài)代理

java.lang.reflect.Proxy:生成動(dòng)態(tài)代理類(lèi)和對(duì)象;
java.lang.reflect.InvocationHandler(處理器接口):可以通過(guò)invoke方法實(shí)現(xiàn)對(duì)真實(shí)角色的代理訪問(wèn)。
每次通過(guò) Proxy 生成的代理類(lèi)對(duì)象都要指定對(duì)應(yīng)的處理器對(duì)象。

JDK實(shí)現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個(gè)參數(shù),完整的寫(xiě)法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
  • ClassLoader loader,:指定當(dāng)前目標(biāo)對(duì)象使用類(lèi)加載器,獲取加載器的方法是固定的
  • Class<?>[] interfaces,:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類(lèi)型,使用泛型方式確認(rèn)類(lèi)型
  • InvocationHandler h:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入
1) 接口:Subject.java
public interface Subject {
    public int sellBooks();

    public String speak();
}
2) 真實(shí)對(duì)象:RealSubject.java
public class RealSubject implements Subject{
    @Override
    public int sellBooks() {
        System.out.println("賣(mài)書(shū)");
        return 1 ;
    }

    @Override
    public String speak() {
        System.out.println("說(shuō)話");
        return "張三";
    }
}
3)處理器對(duì)象:MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 因?yàn)樾枰幚碚鎸?shí)角色,所以要把真實(shí)角色傳進(jìn)來(lái)
     */
    Subject realSubject ;

    public MyInvocationHandler(Subject realSubject) {
        this.realSubject = realSubject;
    }

    /**
     *
     * @param proxy    代理類(lèi)
     * @param method    正在調(diào)用的方法
     * @param args      方法的參數(shù)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("調(diào)用代理類(lèi)");
        if(method.getName().equals("sellBooks")){
            int invoke = (int)method.invoke(realSubject, args);
            System.out.println("調(diào)用的是賣(mài)書(shū)的方法");
            return invoke ;
        }else {
            String string = (String) method.invoke(realSubject,args) ;
            System.out.println("調(diào)用的是說(shuō)話的方法");
            return  string ;
        }
    }
}
4)測(cè)試用例 :Client.java
public class Client {
    public static void main(String[] args) {
        //真實(shí)對(duì)象
        Subject realSubject =  new RealSubject();

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
        //代理對(duì)象
        Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);

        proxyClass.sellBooks();

        proxyClass.speak();
    }
}

1.2.2 CGLib動(dòng)態(tài)代理

CGLib采用了非常底層的字節(jié)碼技術(shù),其原理是通過(guò)字節(jié)碼技術(shù)為一個(gè)類(lèi)創(chuàng)建子類(lèi),并在子類(lèi)中采用方法攔截的技術(shù)攔截所有父類(lèi)方法的調(diào)用,順勢(shì)織入橫切邏輯。但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類(lèi)進(jìn)行代理。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)。

1) 目標(biāo)對(duì)象類(lèi):UserDao.java
/**
 * 目標(biāo)對(duì)象,沒(méi)有實(shí)現(xiàn)任何接口
 */
public class UserDao {
    public void save() {
        System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
    }
}
2) Cglib代理工廠:ProxyFactory.java
/**
 * Cglib子類(lèi)代理工廠
 * 對(duì)UserDao在內(nèi)存中動(dòng)態(tài)構(gòu)建一個(gè)子類(lèi)對(duì)象
 */
public class ProxyFactory implements MethodInterceptor{
    //維護(hù)目標(biāo)對(duì)象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象
    public Object getProxyInstance(){
        //1.工具類(lèi)
        Enhancer en = new Enhancer();
        //2.設(shè)置父類(lèi)
        en.setSuperclass(target.getClass());
        //3.設(shè)置回調(diào)函數(shù)
        en.setCallback(this);
        //4.創(chuàng)建子類(lèi)(代理對(duì)象)
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("開(kāi)始事務(wù)...");

        //執(zhí)行目標(biāo)對(duì)象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事務(wù)...");

        return returnValue;
    }
}
3) 測(cè)試類(lèi)
/**
 * 測(cè)試類(lèi)
 */
public class App {
    @Test
    public void test(){
        //目標(biāo)對(duì)象
        UserDao target = new UserDao();
        //代理對(duì)象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
        //執(zhí)行代理對(duì)象的方法
        proxy.save();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容