代理模式(Proxy Pattern)

2024/3/28 Java进阶

为其他对象提供一种代理以控制对这个对象的访问。本质就是通过A来访问B的方法。

# 1、代理模式案例

华为手机打电话,但我想在前后记录时间,但不改变原来的模式,就可以通过代理方式,用代理对象来调用call方法,这样在调用前后就可以做操作了。

public interface Phone {
    void call();
}

public class HuaweiPhone implements Phone{
    @Override
    public void call() {
        System.out.println("华为打电话");
    }
}
1
2
3
4
5
6
7
8
9
10
public class HuaweiProxy implements Phone{

    private HuaweiPhone huaweiPhone;

    public HuaweiProxy(HuaweiPhone huaweiPhone) {
        this.huaweiPhone = huaweiPhone;
    }

    @Override
    public void call() {
        System.out.println("开始打电话:"+ LocalTime.now());
        huaweiPhone.call();
        System.out.println("结束打电话:"+ LocalTime.now());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ClientTest {

    public static void main(String[] args) {
        HuaweiProxy proxy = new HuaweiProxy(new HuaweiPhone());
        proxy.call();
    }

}
/**
 * 输出结果
 * 开始打电话:13:34:45.351
 * 华为打电话
 * 结束打电话:13:34:45.352
 * /
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2、JDK动态代理案例

上面代理的方式太多麻烦,每次都得新建一个类,JDK提供了一种动态代理的方式,不过只能为接口创建动态代理。

public interface Phone {
    void call();
}

public class HuaweiPhone implements Phone{
    @Override
    public void call() {
        System.out.println("华为打电话");
    }
}
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws NoSuchMethodException,
            InvocationTargetException, InstantiationException, IllegalAccessException {
    // 创建原对象
    Phone phone = new HuaweiPhone();

    // 创建动态代理对象,底层是反射机制,newProxyInstance(接口实现类,代理的接口,InvocationHandler实例)
    Phone phoneProxy=(Phone) Proxy.newProxyInstance(HuaweiPhone.class.getClassLoader(), 
            HuaweiPhone.class.getInterfaces(), 
            new InvocationHandler() {
            /**
             * proxy:被代理对象的引用,系统会自动创建被代理对象的一个映射
             * method:被代理对象的方法
             * args: 被代理对象方法的参数
             * 返回值是被代理对象执行后的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //被代理对象方法的执行,并获得返回值
                Object result=null;
                System.out.println("开始打电话:"+ LocalTime.now());
                result = method.invoke(phone,args);
                System.out.println("结束打电话:"+ LocalTime.now());
                return result;
            }
    });

    // 只打电话
    phone.call();
    // 通过代理对象来调用打电话,能够执行代理内部逻辑
    phoneProxy.call();
}
/**
 * 输出结果:
 * 华为打电话
 * 开始打电话:14:00:13.008
 * 华为打电话
 * 结束打电话:14:00:13.008
 * /
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 3、CGLIB动态代理案例

不仅仅可以为接口,也可以为类创建动态代理

public interface Phone {
    void call();
}

public class HuaweiPhone implements Phone{
    @Override
    public void call() {
        System.out.println("华为打电话");
    }
}
1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
    //1.创建Enhancer对象
    Enhancer enhancer = new Enhancer();
    //2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
    enhancer.setSuperclass(HuaweiPhone.class);
    //3.设置回调,需要Callback接口,此处我们实现的是MethodInterceptor,是Callback的子接口
    //当调用代理对象的任何方法时,都会被MethodInterceptor的invoke方法处理
    enhancer.setCallback(new MethodInterceptor() {
        /**
         * Object o:代理对象
         * Method method:被代理的类的方法,即HuaweiPhone中的方法
         * Object[] objects:调用方法传递的参数
         * MethodProxy methodProxy:方法代理对象
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, 
                                    MethodProxy methodProxy) throws Throwable {
            System.out.println("开始打电话:"+ LocalTime.now());
            Object result = methodProxy.invokeSuper(o,objects);
            System.out.println("结束打电话:"+ LocalTime.now());
            return result;
        }
    });
    //4.获取代理对象:通过enhancer.create()获取代理对象,返回值是Object类型,需强转
    HuaweiPhone proxy = (HuaweiPhone) enhancer.create();
    //5.调用代理对象的方法
    proxy.call();
}
/**
 * 输出结果:
 * 开始打电话:14:12:13.214
 * 华为打电话
 * 结束打电话:14:12:13.243
 * /
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

动态代理最佳体现,Spring的AOP实现

参考:动态代理详解 (opens new window)