1. 程式人生 > >Spring 靜態代理和動態代理

Spring 靜態代理和動態代理

靜態代理:

優點:

  1、  實現鬆散耦合。

  2、做到在不修改目標物件的功能前提下,對目標功能擴充套件。

 

缺點:

 如果專案中有多個類,則需要編寫多個代理類,工作量大,不好修改,不好維護,不能應對變化。

模擬某位學生去考試作為例子:

 

下面建立 考試介面

public interface Examable {
    void exam();
}

建立Student學生實現這個考試介面(被代理)

public class Student implements Examable {
  
    @Override
    
public void exam() { System.out.println("奮筆疾書,完成考題。。。"); } }

創鍵Cheater(代理)物件,同時也去實現Examable介面 

 

public class Cheater implements Examable {

  //被代理物件
private final Examable student; public Cheater(Examable student){ this.student=student; } @Override
public void exam() { System.out.println("在現場唱了一首歌,差點被勸退"); student.exam();//呼叫Student類的方法 } }

測試:

public class Main {
//組合優於繼承
public static void main(String[] args) {
//cheater 就是一個代理物件
//因為它是受student 委託,完成某個功能
//它要完成的主要功能,來自student
//除了委託做的事情,可能還會擴充套件一些行為



Examable xiaoming = new Student();//原來的行為
xiaoming.exam();

System.out.println("------下面是代理行為------");

Examable cheater = new Cheater(xiaoming);
cheater.exam();

}
}

結果:

奮筆疾書,完成考題。。。
------下面是代理行為------
在現場唱了一首歌,差點被勸退
奮筆疾書,完成考題。。。

 

動態代理:

使用JDK內建的Proxy實現

接著拿上面作為例子

 

下面建立 考試介面

public interface Examable {
    void exam();
}

建立Student學生實現這個考試介面(被代理)

public class Student implements Examable {
  
    @Override
    public void exam() {
        System.out.println("奮筆疾書,完成考題。。。");
    }

}

建立一個 JdkProxy 類 實現 InvocationHandler 介面

public class JdkProxy implements InvocationHandler {


    private Object object;//被代理

    public JdkProxy() {
    }

    public JdkProxy(Object object) {
        this.object = object;    ////初始化的時候就賦值
    }

    /**
     * 當用戶呼叫物件中的每個方法時都通過下面的方法執行,方法必須在介面
     * proxy 被代理後的物件
     * method 將要被執行的方法資訊(反射)
     * args 執行方法時需要的引數
     */


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        Object invoke = null;
        try {
            invoke = method.invoke(object, args);
        } catch (Exception e) {
            System.out.println("異常的資訊" + e.getMessage());
        }
        return invoke;//呼叫被代理物件原來的方法(行為)
    }
}

測試:

 public class Main {

public static void main(String[] args) {
/*
         * 通過Proxy的newProxyInstance方法來建立我們的代理物件,我們來看看其三個引數
         * 第一個引數 handler.getClass().getClassLoader() ,我們這裡使用handler這個類的ClassLoader物件來載入我們的代理物件
         * 第二個引數person1.getClass().getInterfaces(),我們這裡為代理物件提供的介面是真實物件所實行的介面,表示我要代理的是該真實物件,這樣我就能呼叫這組介面中的方法了
         * 第三個引數handler, 我們這裡將這個代理物件關聯到了上方的 InvocationHandler 這個物件上
         */


        ClassLoader cl= Thread.currentThread().getContextClassLoader();


        Examable o = (Examable) Proxy.newProxyInstance(
                cl,//類載入器
                new Class[]{Examable.class},//獲取被代理物件的所有介面
                new JdkProxy(new Student())//InvocationHandler物件
        );
        o.exam();//代理後的行為

    }

}

結果:

 

 

使用內建的Proxy實現動態代理有一個問題:被代理的類必須實現介面,未實現介面則沒辦法完成動態代理。

2、動態代理,使用cglib實現

還是拿上面的例子來說吧。

這次我們不寫介面了。不過 我們要實現MethodInterceptor介面,並實現方法

去maven 中心倉庫 找到 CGlib 的依賴

 

 

Student類

複製程式碼
public class Student {

    public void exam(){
        System.out.println("奮筆疾書,完成考試啦");
    }

}
複製程式碼

建立一個 CglibProxy 的類 並 實現  MethodInterceptor 介面 ,並實現方法

 

package com.nf147.sim.proxy.p5;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxy implements MethodInterceptor {


    /*
     * 引數
     * Object 為由CGLib動態生成的代理類例項
     * method 為上文中實體類所呼叫的被代理的方法引用
     * objects 為引數值列表
     * methodProxy 為生成的代理類對方法的代理引用
     * return 從代理例項的方法呼叫返回的值
     * */


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(o, objects);
    }
}

 

測試:

package com.nf147.sim.proxy.p5;


import com.nf147.sim.proxy.p1.Student;
import net.sf.cglib.proxy.Enhancer;

public class Main {
    public static void main(String[] args) {

        //第一種方式


        //增強器,動態程式碼生成器
        Enhancer enhancer = new Enhancer();
        //設定 生成類 的 父類
        enhancer.setSuperclass(Student.class);
        //回撥函式
        enhancer.setCallback(new CglibProxy());
        //動態生成位元組碼並返回代理物件
        Student o = (Student) enhancer.create();
        o.exam();

        System.out.println("-----------");

        //第二種方式
        //這裡是簡化寫法
        //第一個引數 設定 生成類 的父類 ,第二引數 被代理類的所有介面 ,回撥函式
        Student student = (Student) Enhancer.create(Student.class, null, new CglibProxy());
        student.exam();

    }
}

 結果: