【一文读懂】Java代理模式

1 定义

定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理对象和目标对象通常会继承相同的接口,来保持行为的一致性。

2 目的

  • 职责清晰:真实的角色只关心核心的业务逻辑。
  • 保护对象:代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
  • 高扩展性:是实现AOP等的前提。

3 分类

区分动态代理和静态代理主要是从代理类的创建时序和类的确定性

3.1 静态代理

代理类在编译之前就已创建并明确,目标对象和代理对象会继承相同的接口以保持行为的一致性,而且代理对象对目标的增强是确定的。
下面一个简单例子来实现静态代理:
首先,定义一个用户操作的接口

public interface UserDao {
    public void create();
    public void delete();
    public String get();
}

然后,实现一个目标对象类来继承改接口

public class UserDaoImpl implements UserDao {
    @Override
    public void create() {
        System.out.println("UserDaoImpl: create");
    }

    @Override
    public void delete() {
        System.out.println("UserDaoImpl: delete");
    }

    @Override
    public String get() {
        System.out.println("UserDaoImpl: get");
        return null;
    }
}

然后,写一个静态代理类,同样的继承用户接口,此处添加属性UserDaoImpl ,并利用构造函数完成实例的传入。

public class UserDaoImplProxy implements UserDao{
    private UserDaoImpl userDaoImpl;

    public UserDaoImplProxy(UserDaoImpl userDaoImpl){
        this.userDaoImpl = userDaoImpl;
    }

    @Override
    public void create() {
        System.out.println("UserDaoImplProxy: create");
        userDaoImpl.create();
    }

    @Override
    public void delete() {
        System.out.println("UserDaoImplProxy: delete");
        userDaoImpl.delete();
    }

    @Override
    public String get() {
        System.out.println("UserDaoImplProxy: get");
        userDaoImpl.get();
        return null;
    }
}

最后在main函数中调用代理类

public class ScheduleApplication {

    public static void main(String[] args) {
        UserDaoImplProxy userDaoImplProxy = new UserDaoImplProxy(new UserDaoImpl());
        userDaoImplProxy.create();
    }
}

可以看到打印如下所示

UserDaoImplProxy: create
UserDaoImpl: create

这样实现的好处是整个代理类的功能是确定的,且一目了然,对于帮助读懂代理类功能是有帮助的。
但是,如果有太多的目标类需要被代理,那么这项工作就显得过于繁琐,可能维护起来的困难不次于直接改动目标类的实现。这时,就需要动态代理的功能了。

3.2 动态代理

根据应用场景和实现方式的不同,动态代理又可以分为JDK动态代理和cglib动态代理,动态代理实现
【一文读懂】Java代理模式_第1张图片

3.2.1 JDK动态代理

JAVA反射机制 :是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

JDK动态代理 :是基于Java反射机制,在程序运行过程中,通过被代理的接口来动态生成代理类的字节码二进制流,并加载运行(java反射-对象方法的执行)的过程。因此,JDK的动态代理只适用于实现了接口的对象

首先介绍下JDK动态代理的使用方法:
先创建目标接口和实现类,为了说明动态代理的特性,特意创建两个接口

//目标接口1
public interface TeamDao {
    public void create();
}
//目标接口2
public interface UserDao {
    public void create();
}
//目标实现类1
public class TeamDaoImpl implements TeamDao {
    @Override
    public void create() {
        System.out.println("TeamDaoImpl: create");
    }
}
//目标实现类2
public class UserDaoImpl implements UserDao {
    @Override
    public void create() {
        System.out.println("UserDaoImpl: create");
    }
}

定义动态代理增强类

public class DynamicProxy implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println("Dynamics run Begin");
        Object result = method.invoke(target, objects);
        System.out.println("Dynamics run End");
        return result;
    }
}

最后,编写客户端代码进行类的调用

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class ScheduleApplication {

    public static void main(String[] args) {
        //目标类的InvocationHandler设置
        InvocationHandler userHandler = new DynamicProxy(new UserDaoImpl());
        //创建实现了UserDao接口的代理类$Proxy0的实例
        UserDao userDao = (UserDao) Proxy.newProxyInstance(
                Client.class.getClassLoader(),new Class[]{UserDao.class},userHandler);
        //代理类(即继承Proxy类)方法的调用,实际上执行的是其InvocationHandler的invoke方法
        userDao.create();
        //
        InvocationHandler teamHandler = new DynamicProxy(new TeamDaoImpl());
        //创建实现了TeamDao接口的代理类$Proxy1的实例
        TeamDao teamDao = (TeamDao) Proxy.newProxyInstance(
                Client.class.getClassLoader(),new Class[]{TeamDao.class}, teamHandler);
        teamDao.create();
    }
}

执行结果为:

Dynamics run Begin
UserDaoImpl: create
Dynamics run End
Dynamics run Begin
TeamDaoImpl: create
Dynamics run End

可以发现,代理类方法的调用执行的是InvocationHandler的invoke方法,那么第一个问题是:
为什么代理类的方法调用执行的是InvocationHandler的invoke方法?
我们首先看一下生成的代理类,此处需要将生成的class文件保存到本地,然后反编译得到其源码,其中,将JDK动态生成的class文件保存到本地的方法是在代码中添加

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

利用Idea的反编译插件Pug,得到代理类的源码是

package com.sun.proxy;

import com.example.zyq.schedule.ProxyDemo.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements UserDao {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void create() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.example.zyq.schedule.ProxyDemo.UserDao").getMethod("create");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

我们暂且不去考虑$Proxy0.class是如何生成的,可以看到对该类create方法的调用,实际执行的就是调用的InvocationHandler(也就是此处DynamicProxy)实现类的invoke方法。
现在我们可以得到JDK动态代理的实现主要通过是java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口,其中:

  • Proxy类:Proxy提供静态方法用来创建代理对象类和实例。(Proxy provides static methods for creating objects that act like instances of interfaces but allow for customized method invocation.)
  • InvocationHandler接口:用来约束调用者实现

3.2.2 cglib动态代理

CGLIB动态代理的原理:就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数

  • net.sf.cglib.proxy.Enhancer 主要的增强类。
  • net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
  • net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

定义CGLib的拦截类,继承MethodInterceptor接口并实现拦截(intercept)方法。

import java.lang.reflect.Method;

public class CGLibDynamicProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Before Cglib Dynamic");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("After Cglib Dynamic");
        return result;
    }
}

客户端调用函数:

public class CglibApplication {

    public static void main(String[] args){
        Enhancer enhancer = new Enhancer();
        CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy();
        enhancer.setSuperclass(UserDaoImpl.class);
        enhancer.setCallback(cgLibDynamicProxy);
        UserDaoImpl userDao = (UserDaoImpl)enhancer.create();
        userDao.create();
    }
}

执行结果为:

Before Cglib Dynamic
UserDaoImpl: create
After Cglib Dynamic

完成代理的调用。

你可能感兴趣的:(Java)