Java设计模式——代理模式

Java设计模式之代理模式

代理模式:为另一种对象提供一个替身或者占位符来控制对这个对象的访问。

使用代理对象创建代表对象,让代表对象控制对对象的访问,被代理对象可以使远程对象、创建开销大的对象或者是需要安全控制的对象。

代理访问控制:

  • 远程代理控制访问远程对象

    可以作为另一个JVM上的对象的本地代表,利用网络Socket进行连接,通过网络将结果返回给代理。

  • 虚拟代理控制访问创建开销大的对象

  • 保护代理基于权限控制对资源的访问

  • 智能引用代理,相当于对原对象的一种功能扩展,在访问原对象时,加入了新功能,例如统计访问次数等。

  • Subject:提供统一接口,让Proxy可以替代RealSubject
  • RealSubject:真正做事的对象,被Proxy代理控制访问
  • Proxy:代理对象,持有RealSubject引用,负责客户的调用

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.

关键:在程序未运行之前,代理对象就已经被创建出来了,.class文件存在。

优点:

  • 在不修改元对象的情况下,对目标进行扩展。

缺点:

  • 每个方法都需要扩展,当有很多方法时,需要对每个方法都添加相应的操作,不方便维护。

使用

  • 点餐
public interface MenuOrder {
    public void order();
}
  • 被代理类——厨师
public class ChefAcceptOrder implements MenuOrder{
    public void order() {
        System.out.println( new Meal().toString());
    }
}
  • 任务返回值——Meal
public class Meal {
    private String[] names;
    public Meal(){
        names = new String[3];
        names[0] = "鱼香肉丝";
        names[1] = "宫保鸡丁";
        names[2] = "紫菜汤";
    }
    @Override
    public String toString() {
        return "Meal{" +
                "names=" + Arrays.toString(names) +
                '}';
    }
}
  • 代理类——女服务员
public class WaitressProxy implements MenuOrder{
    private ChefAcceptOrder acceptOrder;
    public WaitressProxy(ChefAcceptOrder acceptOrder){
        this.acceptOrder = acceptOrder;
    }
    public void order() {
        System.out.println("请您稍等,正在为你备餐");
        acceptOrder.order();
        System.out.println("请您享用");
    }
}
  • 测试
public class Test {
    public static void main(String[] args) {
        ChefAcceptOrder acceptOrder = new ChefAcceptOrder();
        WaitressProxy proxy = new WaitressProxy(acceptOrder);
        proxy.order();
    }
}

远程代理

使用

  • 远程方法调用接口
public interface MyRemote extends Remote {
    public String doSomething() throws RemoteException;
}
  • 远程方法调用实现——还需继承UnicastRemoteObject
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
    protected MyRemoteImpl() throws RemoteException {
    }

    public String doSomething() throws RemoteException {
        return "我完成了一件事。";
    }
}
  • 客户端测试远程调用
    public static void main(String[] args) {
        try {
            MyRemote remote = new MyRemoteImpl();
            Context context = new InitialContext();
            LocateRegistry.createRegistry(8090);
            context.bind("rmi://localhost:8090/myRemoteService",remote);
            MyRemote remote1 = (MyRemote) context.lookup("rmi://localhost:8090/myRemoteService");
            System.out.println(remote1.doSomething());
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

动态代理

在运行时,为被代理对象利用反射创建代理对象

其中Proxy实际没有,是由JDK为我们动态创建的

动态代理有两种类型:

  • 接口被实现的情况下,属于JDK中的Proxy代理
  • 没有接口被实现,属于Cglib代理

优点:

  • 对代理类的函数进行统一的处理,而不用修改每个代理类的函数

JDK动态代理——Proxy

通过Proxy类中静态方法newProxyInstance创建代理对象,用代理对象class文件加载造出被代理对象,相当于复制一份

newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)

接受三个参数:

  • 被代理对象的类加载器,加载被代理对象
  • 被代理对象的方法数组
  • 方法执行处理器,你需要实现一个调用处理器,实现InvocationHandler接口

所有方法都被提交到InvocationHandler.invoke中执行

java.lang.reflect.InvocationHandler类就一个invoke方法

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。
Object invoke(Object proxy, Method method, Object[] args)
使用
  • 调用方法接口
public interface SayHello {
    public void sayHello();
}
  • 调用方法接口实现
public class SayHelloImpl implements SayHello {
    public void sayHello() {
        System.out.println("hello");
    }
}
  • InvocationHandler-处理方法调用
public class SayHelloInvocationHandler implements InvocationHandler {
    private SayHello sayHello;
    public SayHelloInvocationHandler(SayHello sayHello){
        this.sayHello = sayHello;
    }
    public Object invoke(Object proxy, Method method, Object[] args) {
        try {
            System.out.println("before");
            Object o = method.invoke(sayHello, args);
            System.out.println("after");
            return o;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  • 获取代理对象
public class ProxyFactory {
    //维护一个目标对象
    private SayHello target;
    public ProxyFactory(SayHello target){
        this.target=target;
    }
    public Object getNewProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new SayHelloInvocationHandler(target));
    }
}
  • 测试
public static void main(String[] args) {
        SayHelloImpl sayHello = new SayHelloImpl();
        ProxyFactory proxyFactory = new ProxyFactory(sayHello);
        SayHello proxy = (SayHello) proxyFactory.getNewProxy();
        proxy.sayHello();
    }

动态代理——Cglib

上面的静态代理和动态代理模式都是要求目标对象实现一个接口或者多个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用构建目标对象子类的方式实现代理,这种方法就叫做:Cglib代理

  • 实现MethodInterceptor接口,提供intercept方法拦截
使用
  • 服务类——被代理对象
public class TaskService {
    public void doTask(){
        System.out.println("正在进行紧张的计算");
    }
}
  • 获取代理对象
public class ProxyFactory implements MethodInterceptor {
    private Object target;
    public ProxyFactory(Object target){
        this.target = target;
    }
    public Object getNewProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始执行");
        Object res = method.invoke(target,objects);
        System.out.println("结束执行");
        return res;
    }
}
  • 测试
public static void main(String[] args) {
        TaskService taskService = new TaskService();
        ProxyFactory proxyFactory = new ProxyFactory(taskService);
        TaskService cglib = (TaskService) proxyFactory.getNewProxy();
        cglib.doTask();
    }

代理模式和装饰者模式

代理模式和装饰者模式UML基本一样,但是两者的核心不一样:

  • 装饰者模式强调添加行为

  • 代理模式强调控制访问


代码地址:Github代理模式


Java设计模式|策略模式

Java设计模式|观察者模式

Java设计模式|装饰者模式

Java设计模式|工厂模式

Java设计模式|命令模式

Java设计模式|适配器模式和外观模式

Java设计模式|模板方法模式

Java设计模式|迭代器模式和组合模式

Java设计模式|状态模式

Java设计模式|代理模式

Java设计模式|单例模式

Java设计模式|备忘录模式

Java设计模式|访问者模式

Java设计模式|复合模式

Java设计模式|桥接模式

Java设计模式|生成器模式

Java设计模式|享元模式/蝇量模式

Java设计模式|原型模式

Java设计模式|责任链模式

Java设计模式|中介者模式

  • 本文作者: dzou | 微信:17856530567
  • 本文链接: http://www.dzou.top/post/653661c0.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!