Java中的三種代理模式

aliyeye發表於2021-02-03

第一種:靜態代理

程式碼實現

1.設計介面

public interface IUserDao {
    void save();
}

2.實現介面

public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("----已經儲存資料!----");
    }
}

3.代理類 必須實現介面

public class UserDaoProxy implements IUserDao {
    //接收儲存目標物件
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }
    @Override
    public void save() {
        System.out.println("開始事務...");
        target.save();//執行目標物件的方法
        System.out.println("提交事務...");
    }
}

4.測試類

public class App {
    public static void main(String[] args) {
        //目標物件
        UserDao target = new UserDao();
        //代理物件,把目標物件傳給代理物件,建立代理關係
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.save();//執行的是代理的方法
    }
}

第二種:JDK動態代理

程式碼實現

1.設計介面

public interface IUserDao {
    void save();
}

2.實現介面

public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("----已經儲存資料!----");
    }
}

3.代理類 不需要實現介面

public class ProxyFactory {
    //維護一個目標物件
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }

    //這是Java8語法
    public Object getProxyInstanceLambda(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy,method,args) -> {
                    System.out.println("開始事務2");
                    //執行目標物件方法
                    Object returnValue = method.invoke(target, args);
                    System.out.println("提交事務2");
                    return returnValue;
                });
    }

    //傳統語法
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("開始事務2");
                        //執行目標物件方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事務2");
                        return returnValue;
                    }
                });
    }
}

4.測試類

public class App {
    public static void main(String[] args) {
        // 目標物件
        IUserDao target = new UserDao();
        // 【原始的型別 class UserDao】
        System.out.println(target.getClass());

        // 給目標物件,建立代理物件
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstanceLambda();
        // class $Proxy0   記憶體中動態生成的代理物件
        System.out.println(proxy.getClass());

        // 執行方法   【代理物件】
        proxy.save();
    }
}

第三種:Cglib代理

程式碼實現

1.目標物件類

public class UserDao {
    public void save() {
        System.out.println("----已經儲存資料!----");
    }
}

2.Cglib代理工廠

public class ProxyFactory implements MethodInterceptor {
    //維護目標物件
    private Object target;

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

    //給目標物件建立一個代理物件
    public Object getProxyInstance(){
        //1.工具類
        Enhancer en = new Enhancer();
        //2.設定父類
        en.setSuperclass(target.getClass());
        //3.設定回撥函式
        en.setCallback(this);
        //4.建立子類(代理物件)
        return en.create();

    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("開始事務...");
        //執行目標物件的方法
        Object returnValue = method.invoke(target, objects);
        System.out.println("提交事務...");
        return returnValue;
    }
}

3.測試類

public class App {

    public static void main(String[] args) {
        //目標物件
        UserDao target = new UserDao();
        //代理物件
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
        //執行代理物件的方法
        proxy.save();
    }
}

三者的區別

1.靜態代理

目標物件要實現介面,而且代理物件需要實現介面。
這樣的代理類只能代理特定介面的物件,靈活度不夠。

2.JDK動態代理

代理物件不需要實現介面,但是目標物件一定要實現介面,否則不能用動態代理

3.CGLib動態代理

代理物件不需要實現介面,目標物件也可以是單純的一個物件。

在Spring的AOP程式設計中:
如果加入容器的目標物件有實現介面,用JDK代理.
如果目標物件沒有實現介面,用Cglib代理.

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章