一篇文章带你熟悉代理模式

代理模式

一、 代理模式简介

  • **意图:**为其他对象提供一种代理以控制对这个对象的访问。

  • 主要解决:

在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
  • **何时使用:**想在访问一个类时做一些控制。

  • **如何解决:**增加中间层。

  • **关键代码:**实现与被代理类组合。

  • UML图示:

一篇文章带你熟悉代理模式_第1张图片

我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。

ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。(来自菜鸟教程)


二、 代理模式使用和源码解析

无论是哪种代理,核心都是介绍中提到的,代理对象和被代理对象实现共同的接口,并且代理对象只是进行增强方法(前置处理和后置处理),核心方法还是调用被代理对象的方法。
1. 静态代理

静态代理非常简单,就是创建一个代理类 持有被被代理类对象的引用,并实现共同接口,重写方法,在方法中进行前置处理后置处理

  • 共同接口

    public interface IUserService {
    
        void add();
    
        void update();
    }
    
  • 被代理对象

    public class UserServiceImpl implements IUserService{
        @Override
        public void add() {
            System.out.println("---------add方法执行----------");
        }
    
        @Override
        public void update() {
            System.out.println("---------update方法执行----------");
        }
    }
    
  • 代理对象

public class StaticProxy implements IUserService {

    //需要持有被代理对象的引用
    IUserService userService;

    public StaticProxy(IUserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        //前置处理
        System.out.println("事务开启了......");
        //代理对象调用方法
        userService.add();
        //后置处理
        System.out.println("事务提交了......");
    }

    @Override
    public void update() {
        System.out.println("事务开启了......");
        userService.update();
        System.out.println("事务提交了......");
    }
}

在静态代理中,代理对象是程序员通过代码实实在在编写的一个类,所以称之为静态

这种硬编码的方式并不可取,我们理想的方式应该是 只需要输入被代理类的接口方法

以及前置处理、后置处理(我们需要添加的逻辑)等信息,就能动态地生成一个代理对象供我们使用,这才是程序员正确的思维方式,所以动态代理应用而生。

2. JDK动态代理

JDK动态代理是JDK提供的生成代理对象的方法。

① 使用
public class DynamicProxy {
    public static IUserService getProxyInstance() {
        //动态代理 (固定流程)
        /**
         * 第一个参数:被代理类的类加载器 这个是有固定的写法
         *    被代理的类.class.getClassLoader
         *  第二个参数:就是我们被代理的类实现的接口
         *    如果被代理的是类:被代理的类.class.getInterfaces();
         *    如果被代理的是接口:new Class[]{被代理的接口.class}
         *  第三个参数:就是对这个代理的类或者接口中方法执行的监听
         *    new InvacationHandler(){}
         */
        ClassLoader classLoader=UserServiceImpl.class.getClassLoader();
        Class<?>[] interfaces = UserServiceImpl.class.getInterfaces();
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("事务开启了......");
                method.invoke(new UserServiceImpl(),args);
                System.out.println("事务提交了......");
                return null;
            }
        };
        IUserService proxyInstance = (IUserService) java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, handler);
        return proxyInstance;
    }
}
② 源码解析
2.2.1 分析代理对象

JDK动态代理生成的代理对象是这样的(省略部分代码)

public class UserServiceProxy implements IUserService {
    
    private InvocationHandler invocationHandler;
    
    public UserServiceProxy(InvocationHandler invocationHandler) {
        this.invocationHandler = invocationHandler;
    }

    public void add() throws Throwable {
        //获取父类中的add 方法的method对象
        Method method = IUserService.class.getMethod("add");
        invocationHandler.invoke(this, method, null);
    }

    public void update() throws Throwable {
        //获取父类中的add 方法的method对象
        Method method = IUserService.class.getMethod("update");
        invocationHandler.invoke(this, method, null);
    }
}

当我们使用代理对象,调用add()方法时

  • 首先会通过反射获取该方法的Method对象;

  • Method对象作为参数,调用invocationHandler.invoke方法

  • invoke方法是我们重写的逻辑(前置处理,后置处理)

    UserServiceProxy userServiceProxy = new UserServiceProxy(new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                 //就能监控到这个方法在什么时候执行
                 String name = method.getName();
                 System.out.println("当前执行的方法是:"+name);
                 System.out.println("打开事务.....");
                    
                 Object invoke = method.invoke(new UserService(), args);
                    
                 System.out.println("提交事务.....");
                 return invoke;
                }
            });
    
  • 由于Method对象是该方法的参数,所以能够在该方法中通过反射调用被代理对象的方法

    Object invoke = method.invoke(new UserService(), args);
    

分析完了代理对象,接下来看看这个代理对象是如何生成的

2.2.2 生成代理对象

​ 通过字符串拼接 和 Java编译 生成代理对象…(具体过程和代码请移步另一篇文章)

JDK动态代理的前提是被代理对象必须实现接口

核心在于利用多态,将生成的代理对象 ,即接口实现类 赋值给 接口

通过调用接口方法间接地调用接口实现类中重写的方法

但不妨思考思考,多态只有这一种用法吗?


3. CGLib动态代理
多态还有另外一种使用,就是子类赋值给父类,通过调用父类方法间接地调用子类的方法,这就是CGLib的由来。
① 使用
public class CGLibDynamicProxy implements MethodInterceptor {

    private UserServiceImpl userService;

    public CGLibDynamicProxy(UserServiceImpl userService){
        this.userService = userService;
    }

    public UserServiceImpl getCGLibProxyInstance(){

        Enhancer enhancer=new Enhancer();
        //给生成的这个类 搞个爹
        enhancer.setSuperclass(UserServiceImpl.class);
        //指定拦截的接口实例
        enhancer.setCallback(this);
        return (UserServiceImpl) enhancer.create();
    }

    /**
     * 在这里来进行拦截
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("打开事务.....");
        Object invoke = method.invoke(this.userService, objects);
        System.out.println("提交事务.....");

        return invoke;
    }
}
② 源码解析
3.2.1 分析代理对象

CGLib生成的代理对象和JDK的非常类似, 核心不同在于

实现 ------------- 继承

InvocationHandler ------------- methodInterceptor

invoke ------------- intercept

public class UserServiceProxy extends UserService {

    private MethodInterceptor methodInterceptor;

    //父类的class对象
    private Class superClass;

    public UserServiceProxy(MethodInterceptor methodInterceptor, Class superClass) {
        this.methodInterceptor = methodInterceptor;
        this.superClass = superClass;
    }

    @Override
    public void add() throws Throwable {
        //这里需要找到父类中的方法
        Method method = this.superClass.getMethod("add");
        this.methodInterceptor.intercept(this, method, null, null);
    }

    @Override
    public void update() throws Throwable {
        //这里需要找到父类中的方法
        Method method = this.superClass.getMethod("update");
        this.methodInterceptor.intercept(this, method, null, null);
    }
}
3.2.2 生成代理对象

​ 代理对象的生成的步骤和JDK动态代理如出一辙,不在进行冗余讲述。

你可能感兴趣的:(设计模式,代理模式,设计模式)