Java 设计模式 -- 代理模式

代理模式概念

  • 实现一个代理类以及它所对应的受代理对象,其他所有类对受代理对象的访问,都由代理对象代为实现。
  • 优点:在目标对象基础之上,可以增加一些额外的功能。
  • 个人认为,这种模式适用于:
    • 你需要使用别人写好的一个类(别人已经打包成 jar 包,你只有 .class 文件),但是有额外功能需要添加,这时候可以通过代理的方式来扩展该类。
    • 当一个类对于不同的调用者提供不同的访问权限时,可以实现不同的代理类来实现。

静态代理

  • 需要实现三个类,受代理对象的抽象类或接口(称为主题类),受代理对象的类(真实主题类),代理对象类,其中受代理对象与代理对象都应继承前面那个抽象类或者实现那个接口。
  • 这个实现比较简单,仅需要实现主题类、真实主题类以及代理对象类。下面展示一下对应的 UML。

  • 缺点:每次新增一个真实主题类时,需要手动增加一个代理类。

动态代理(接口代理)

  • 依赖于 java.lang.reflect 包下的 Proxy 类与 InvocationHandler 接口实现。
  • 先来看新建一个 Proxy 实例的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
                                     InvocationHandler h) throws IllegalArgumentException
// get a new Proxy Instance
// loader 通过反射机制获取到的受代理对象的类加载器
// interfaces 受代理对象实现的业务接口组(这个动态代理所能代理的方法便是这些业务接口里面实现的方法)
// h Proxy 实例被调用时便会将调用的 Method 对象与参数传给 h 中实现的 invoke 方法
  • 再来看接口 InvocationHandler 的定义

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler -- Java SE8 API

每一个 Proxy 实例都需要一个 InvocationHandler 接口的实现与其配合工作,当一个 Proxy 的方法被调用,这个方法调用会被编码并将信息发送给 Invocation Handler 里定义的 invoke 方法。下面来看看 invoke 方法。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

// proxy 被调用的 Proxy 实例
// method 调用的方法对象(这个方法应该是 Proxy 实例所实现了的接口里面定义的方法,即上一步参数 interfaces 中定义的方法)
// args 调用时传入的参数
  • 使用上述两个方法,我们便可以实现动态代理,即不需要为新增的受代理对象定义一个代理类,而是可以采用定义一个静态代理工厂类,让其实例化各种各样的代理类。参考代码如下
// ProxyFactory.java
package Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 静态工厂类,需要实现 InvocationHandler 接口,这样才能够调用 newProxyInstance()
public class ProxyFactory implements InvocationHandler{
    
    private Object target;
    
    public ProxyFactory(Object target) {
        this.target = target;
    }
    
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), // 运用反射机制获取接口组
                this); // 该方法三个参数里的 h 恰好就是实现了 InvocationHandler 接口的 this
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.printf("start Proxy the method is %s\n", method);
        Object aobj = method.invoke(target, args);
        return aobj;
    }
}

// Subject.java
package Proxy;
// 主题接口的定义
public interface Subject {
    public void doSomething();
}

// RealSubject.java
package Proxy;
// 一个真实主题类
public class RealSubject implements Subject{

    public RealSubject() {
    }
    
    @Override
    public void doSomething() {
        System.out.println("the RealSubject is invoked");
    }
}

// ClientTest.java
package Proxy;
// 客户测试类
public class ClientTest {
    
    public static void main(String[] args) {
        RealSubject target = new RealSubject();
        ProxyFactory afactory = new ProxyFactory(target); // 生成工厂实例
        Subject aproxy = (Subject) afactory.getProxyInstance(); // 生成代理实例
        aproxy.doSomething(); // 通过代理实现业务功能
    }
}

  • 缺点:真实主题类是依靠接口实现的代理,即需要将业务逻辑写在一个接口里面。

Cglib 代理

  • Cglib 代理是子类代理,即它是在内存中构建一个受代理类的子类,从而实现代理以及功能扩展。

  • 这个实现需要 Cglib jar 包(Spring 核心包内已有),并调用里面设定好的方法即可。这个留到学习 Spring 的时候再填坑。

posted @ 2019-04-20 10:13  Melles  阅读(184)  评论(0编辑  收藏  举报