23种设计模式之代理(Proxy)模式

913 阅读4分钟

1、定义

给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。

2、静态代理

2.1 模式结构

静态代理模式由三部分组成:

  • Subject(抽象角色):声明真实对象和代理对象的共同接口。
  • Proxy(代理角色):代理对象角色内部含有对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
  • RealSubject(真实角色):代理角色所代表的真实对象,使我们最终要引用的对象。
2.2 实例

2.2.1 Subject

public interface Subject {

    public void buy();
}

2.2.2 RealSubject

public class RealSubject implements Subject {
    
    @Override
    public void buy() {
        System.out.println("购买一台港版的MacBook Pro");
    }
}

2.2.3 ProxyFactory

public class ProxyFactory implements Subject {

    private Subject target;

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

    @Override
    public void buy(){
        System.out.println("代理开始。。。");
        target.buy();
        System.out.println("代理结束。。。");
    }
}

2.2.4 客户端调用

public class Client {

    public static void main(String[] args) {
        ProxyFactory proxy = new ProxyFactory(new RealSubject());
        proxy.buy();
    }
}
2.3 优缺点

2.3.1 优点

  • 在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。

2.3.2 缺点

  • 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。
  • 一旦接口增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。

3、动态代理(JDK代理、接口代理)

3.1 模式结构

动态代理模式的角色和静态代理模式类似,不同之处就是代理对象不需要实现接口。代理对象的生成,是利用JDK的API动态的在内存中构建的。

3.2 实例

3.2.1 Subject

public interface Subject {
    public void buy();
}

3.2.2 RealSubject

public class RealSubject implements Subject {
    
    @Override
    public void buy() {
        System.out.println("购买一台港版的MacBook Pro");
    }
}

3.2.3 ProxyFactory

public class ProxyFactory {

    private Object target;

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

    // 给目标对象生成一个一个代理对象
    public Object getProxyInstance() {
        /*
        newProxyInstance三个参数说明:
        ClassLoader loader:指定当前目标对象使用类加载器,用null表示默认类加载器。
        Class[] interfaces:需要实现的接口数组。
        InvocationHandler handler:调用处理器,执行目标对象的方法时,会触发调用处理器的方法,从而把当前执行目标对象的方法作为参数传入。
        */
        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("代理开始。。。");
                        Object result = method.invoke(target, args);
                        System.out.println("代理结束。。。");
                        return result;
                    }
                });
    }
}

3.2.4 客户端调用

public class Client {
    
    public static void main(String[] args) {
        
        Subject proxy = (Subject) new ProxyFactory(new RealSubject()).getProxyInstance();
        proxy.buy();
    }
}
3.3 优缺点

3.3.1 优点

  • 解决了静态代理中冗余的代理实现类问题。

3.3.2 缺点

  • JDK动态代理是基于接口设计实现的,如果没有接口,会抛异常。

4、Cglib代理

4.1 模式结构

4.2 实例

4.2.1 导包

java项目需要导入以下四个jar包:

需要的jar包

mavan项目增加以下依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

4.2.2 RealSubject

public class RealSubject {
    
    public void buy() {
        System.out.println("购买一台港版的MacBook Pro");
    }
}

4.2.3 ProxyFactory

public class ProxyFactory implements MethodInterceptor {

    private Object target;

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

    // 给目标对象创建一个代理对象
    public Object getProxyInstance() {
        // 1、工具类
        Enhancer enhancer = new Enhancer();
        // 2、设置父类
        enhancer.setSuperclass(target.getClass());
        // 3、设置回调函数
        enhancer.setCallback(this);
        // 4、创建子类(代理对象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("代理开始。。。");
        Object result = method.invoke(target, args);
        System.out.println("代理结束。。。");
        return result;
    }

}

4.2.4 客户端调用

public class Client {
    
    public static void main(String[] args) {
        
        RealSubject proxy = (RealSubject) new Proxy(new RealSubject()).getProxyInstance();
        proxy.buy();
    }
}
4.3 优缺点

4.3.1 优点

  • 没有接口也能实现动态代理,而且采用字节码增强技术,性能不错。

4.3.2 缺点

  • Cglib采用动态创建子类的方法,对于final类,无法处理。

5、代理模式VS装饰者模式

  • 代理模式是为控制对被代理对象的访问,而装饰者模式是为了增加被装饰对象的功能。
  • 代理类所能代理的类完全由代理类确定,装饰类装饰的对象需要根据实际使用的客户端组合来确定。
  • 被代理对象有的代理对象创建,客户端甚至不需要知道被代理类的存在,被装饰对象对象有客户端创建并传给装饰对象。

源代码:github.com/freedom9/de…