万能的动态代理

x33g5p2x  于2022-05-11 转载在 其他  
字(3.3k)|赞(0)|评价(0)|浏览(270)

一 点睛

在静态代理中,真实对象和代理角色是“一对一”的关系。而在动态代理中,真实对象和代理角色是“多对一”的关系。动态代理可以用一个“万能”的代理者来代理任何类型的真实对象,而不用像静态代理那样给每个真实角色都设置一个代理,这也是动态代理的最大优势。

静态代理需要实现和真实对象相同的接口。例如,房屋中介和真正房屋的主人,都需要“出租房”,因此“出租房”就是两者需要共同实现的接口。但是,动态代理既然是一个“万能”的代理者,那么动态代理应该实现什么接口呢?例如,房屋主人和汽车主人,都需要动态代理,那么这个动态代理应该实现“出租房”还是“出租车”的接口呢?为了解决这个问题,JDK 提供了一个“万能”的动态代理接口——InvocationHandler,及使用动态代理时可用  InvocationHandler 接口中的 invoke() 方法代理出租房、出租车等各种业务方法。

二 代码

1 租房接口

package dynamicproxy;

// 租房接口
public interface Subject {
    boolean rent(int money);
}

2 租车接口

package dynamicproxy;

// 出租车接口
public interface Subject2 {
    boolean rentCar(String type);
}

3 租房真实角色

package dynamicproxy;

// 租房真实角色
public class RealSubject implements Subject {
    @Override
    public boolean rent(int money) {
        System.out.println("租房" + money + "元");
        return true;
    }
}

4 租车真实角色

package dynamicproxy;

// 租车真实接口
public class RealSubject2 implements Subject2 {
    @Override
    public boolean rentCar(String type) {
        System.out.println("租用的车类型:" + type);
        return true;
    }
}

5 动态代理角色

package dynamicproxy;

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

/**
* @className: DynamicProxy
* @description: 动态代理角色
* @date: 2022/5/10
* @author: cakin
*/
public class DynamicProxy implements InvocationHandler {
    private Object obj; // 可以代理任意 类型的角色

    public DynamicProxy(Object obj) {
        this.obj = obj;
    }

    public void before() {
        System.out.println("before rent()...");
    }

    /**
     * 功能描述:真正被代理的方法的返回值
     *
     * @param proxy  代理的角色
     * @param method 被代理的方法
     * @param args   方法的参数
     * @return Object
     * @author cakin
     * @date 2022/5/10
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.before();
        // obj.method(args)
        boolean result = (boolean) (method.invoke(obj, args)); // 通过反射调用rent()方法
        this.after();
        return result;
    }

    public void after() {
        System.out.println("after...");
    }
}

6 租房测试

package dynamicproxy;

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

public class Test {
    public static void main(String[] args) {
        // 真实对象
        Subject realSubject = new RealSubject();
        // 初步的代理对象:handler 就是真实角色 realSubject 的初步代理对象
        InvocationHandler handler = new DynamicProxy(realSubject);

        // 最终的代理对象
        /*
         * newProxyInstance(a,b,c) ;
         * a:初步代理对象的类加载器
         * b:接口类型的数组。要代理的方法是在哪些接口中定义的,即 realSubject 的接口;因为语法上允许一个类实现多个接口,因此接口是一个数组
         * c:要将哪一个 初步代理对象 转成最终代理对象
         */
        Subject subProxy = (Subject) Proxy.newProxyInstance(
                handler.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler);
        boolean result = subProxy.rent(3000); // 会调用动态代理对象的 invoke()
        System.out.println(subProxy.getClass().getName());
        System.out.println(result ? "ok" : "error");
    }
}

7 租车测试

package dynamicproxy;

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

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

        Subject2 realSubject2 = new RealSubject2();
        InvocationHandler handler2 = new DynamicProxy(realSubject2);
        Subject2 subProxy2 = (Subject2) Proxy.newProxyInstance(
                handler2.getClass().getClassLoader(),
                realSubject2.getClass().getInterfaces(),
                handler2);
        subProxy2.rentCar("宝马");
    }
}

三 测试

1 租房测试

before rent()...

租房3000元

after...

com.sun.proxy.$Proxy0

ok

2 租车测试

before rent()...

租用的车类型:宝马

after...

四 动态代理的编写步骤

1 编写自定义接口。接口本身包含了真实角色中的方法(称为方法A)。

2 编写真实角色类。真实角色类需要实现自定义接口,并重写接口中的方法A。

3 编写动态代理角色类。动态代理角色类实现 InvocationHandler 接口,并重写接口中的 invoke() 方法,再通过 method.invoke(obj,args)调用真实角色中的方法A。

4 使用时,先根据真实角色,产生一个初步代理角色 handler,即 handler = new DynamicProxy(真实角色);再通过 subProxy = (自定义接口) Proxy.newProxyInstance(类加载器,真正角色接口数组,初步代理角色)产生一个最终的代理对象 subProxy;最后通过 subProxy 调用真实角色方法 A,但实际上,在调用代理角色方法的 invoke() 方法。

相关文章