设计模式之代理模式和Java中的代理以及JDK动态代理的实现原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/starexplode/article/details/83042760

设计模式之代理模式和Java中的代理

代理模式(Proxy Pottern)是一个使用频率很高的模式,其定义如下:

Provide a surrogate or placegolder for another object to contol access to it.(为其他对象提供一种代理以控制这个对象的访问。)

代理模式在很多的开源项目中出现,基本上一些只需要提供接口,就能访问到具体内容的都是通过代理实现的。

代理模式中涉及的角色如下:

  • Subject 抽象主体角色
    这个可以是一个抽象的接口,也可以是一个普通的类。
  • RealSubject 具体主体角色
    也叫作委托角色,被代理的类,是业务逻辑的具体执行者。
  • Proxy代理主体角色
    也叫作委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实的主体角色实现。并在真实主体角色处理完毕后做预处理和善后工作。
    代理模式原型图

代理模式的实现

代理模式有两种实现,一种是静态代理,另一种是动态代理。下面讲一下这两种代理模式。

静态代理

静态代理的过程就是上面UML图的实现。
Subject接口如下:

public interface Subject {
    void request();
}

RealSubject实现了Subject,如下:

public class RealSubject implements Subject{

    public void request() {
        // 这里写一些业务逻辑
    }
}

RealSubject是真正业务实现类,代理模型的核心就在代理类上,代理类如下:

public class Proxy implements Subject {

    /**
     * 要代理的类
     */
    private Subject subject;

    public Proxy() {
        this.subject = new Proxy();
    }

    /**
     * 通过构造函数传递代理类
     */
    public Proxy(Subject... subjects) {

    }

    /**
     * 实现接口中定义的方法
     */
    public void request() {
        before();
        this.subject.request();
        after();
    }

    /**
     * 预处理
     */
    private void before() {

    }

    /**
     * 后处理
     */
    private void after() {

    }
}

代理模型的优点

  • 职责清晰
    真实的角色实现的就是业务逻辑,而不关心其他非本职责的事务,通过后期的处理完成事务。
  • 高可扩展性
    无论被代理的对象如何改变,只要代理类和被代理的类都实现了同一个接口,所以即使修改了角色类,代理类也不用修改。

动态代理

Java中的动态代理的实现有两种:

  1. jdk动态代理,jdk中的动态代理是通过反射机制实现的。
  2. cglib的动态代理,cglib动态代理底层是通过asm来实现的,cglib动态代理应用的更加广泛,因为效率比较高。

jdk的动态代理

下面通过一个用户登录的简单案例,来讲一下动态代理的使用。

用户类

public class User {

    private int id;
    private String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

用户服务类

public interface UserService {
    boolean login(int id);
}

用户服务的实现类

public class UserServiceImpl implements UserService{

    private static Map<Integer, User> users = new HashMap<>();

    static {
        users.put(1, new User(1, "张三"));
        users.put(2, new User(2, "李四"));
    }

    @Override
    public boolean login(int id) {
        if (users.containsKey(id)) {
            return true;
        } else {
            return false;
        }
    }
}

使用JDK作为代理:

public class UserServiceInvocationHandler implements InvocationHandler {

    private UserService userService;

    public UserService bind(UserService userService) {
        this.userService = userService;
        return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader()
                , userService.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("login")) {
            before(args);
            boolean result = (boolean) method.invoke(userService, args);
            after(result, args);
            return result;
        }
        return null;
    }

    private void before(Object[] args) {
        System.out.println("id为" + args[0] + "的用户尝试登录");
    }

    private void after(boolean result, Object[] args) {
        if (result) {
            System.out.println("id为" + args[0] + "的用户登录成功");
        } else {
            System.out.println("id为" + args[0] + "的用户登录失败");
        }
    }
}

主类:

public class Run {
    public static void main(String[] args) {
        UserServiceInvocationHandler proxy = new UserServiceInvocationHandler();
        UserService userService = proxy.bind(new UserServiceImpl());
        userService.login(1);
        userService.login(5);
    }
}

cglib的动态代理

JDK动态代理机制只能代理接口,而不能代理类(class),而cglib可以,它的原理是对指定的目标生成一个子类,并覆盖其中的方法实现增强。cglib采用的是继承,所以不能对final修改的类进行代理。

cglib的代理类:

public class CglibProxy implements MethodInterceptor {

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if (method.getName().equals("login")) {
            before(objects);
            boolean result = (boolean) methodProxy.invokeSuper(o, objects);
            after(result, objects);
            return result;
        }
        return null;
    }

    private void before(Object[] args) {
        System.out.println("id为" + args[0] + "的用户尝试登录");
    }

    private void after(boolean result, Object[] args) {
        if (result) {
            System.out.println("id为" + args[0] + "的用户登录成功");
        } else {
            System.out.println("id为" + args[0] + "的用户登录失败");
        }
    }
}

cglib的调用类:

public class RunCglib {

    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        UserService userService = (UserService) cglibProxy.getInstance(new UserServiceImpl());
        userService.login(1);
        userService.login(3);
    }
}

关于JDK的动态代理

将JDK中的sun.misc.ProxyGenerator.saveGeneratedFiles属性设置为true,会将代理类生成的字节码内容保存在项目的目录中,文件名为$Proxy0.class
$Proxy0.class

PS:我使用的是Maven创建的项目,然后这个文件没有出现,然后使用javac的多文件编译可以。这里如果IDE编译后没有出现这个类,尝试使用javac编译。

动态代理的过程中涉及到以下的类:

  1. java.lang.reflect.Proxy
  2. java.lang.reflect.InvocationHandler
  3. java.lang.reflect.WeakCache
  4. sun.misc.ProxyGenerator

代理过程的第一步是使用newProxyInstace()创建一个Proxy对象。下面来看下它的源码:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    // 这里是判断InvocationHandler是否为空
    Objects.requireNonNull(h);
    // clone一份接口
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    /*
     * 生成接口的代理类的字节码
     * Look up or generate the designated proxy class.
     */
    Class<?> cl = getProxyClass0(loader, intfs);
    /*
     * 使用自定义的InvocationHandler作为参数,调用构造方法获取代理类对象
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

newProxyInstace()方法中调用了getProxyClass0()方法生成了代理类的字节码文件。

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    // 限定代理的接口不能超过65535个
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    // 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类
    return proxyClassCache.get(loader, interfaces);
}

这里我们不关心缓存,只关心代理类是如何生成的。
这里使用的缓存是WeakCache,如果Cache中没有,则会调用ProxyClassFactory的apply方法创建一个代理类。

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names
    // 所有jdk代理类的前缀名
    private static final String proxyClassNamePrefix = "$Proxy";
    // next number to use for generation of unique proxy class names
    // 一个无锁的long
    private static final AtomicLong nextUniqueNumber = new AtomicLong();
    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        // 校验类加载器是否能通过接口名称加载该类
        for (Class<?> intf : interfaces) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * 校验该类是否接口类型
             * Verify that the Class object actually represents an
             * interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * 校验接口是否重复
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }
        // 代理类的包名
        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
        /*
         * 对于非public类,代理类的包名和接口的包名相同
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }
        if (proxyPkg == null) {
            // public的代理接口,使用com.sun.proxy包名
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }
        /*
         * 给代理类起一个名字
         * Choose a name for the proxy class to generate.
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
        /*
         * 这个方法是真正生成字节码的地方
         * Generate the specified proxy class.
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // 使用类加载器将代理类的字节码加载到JVM中
            return defineClass0(loader, proxyName,
                                proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
            throw new IllegalArgumentException(e.toString());
        }
    }
}

从上面的方法可以看出生成代理类实在ProxyGenerator中,但是该类没有开源(因为这个代理是sun公司的,从包名中就可以看出),可以通过反编译工具来看。如下:

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    final byte[] var4 = var3.generateClassFile();
    // 是否存储在磁盘上
    if (saveGeneratedFiles) {
        // ,,,
    }
    return var4;
}

这里设置sum.misc.ProxyGenerator.saveGeneratedFiles的值为true。

所以运行后就有$Proxy0.class这个文件,通过反编译后的内容如下:

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import top.mcwebsite.proxy_pattern.UserService;

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

    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 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);
        }
    }

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m3 = Class.forName("top.mcwebsite.proxy_pattern.UserService").getMethod("login", Integer.TYPE);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

代理类继承了Proxy,实现了指定的接口,所以jdk的代理类不能代理没有实现接口的类。代理类中重写了hashCode(),equals()和toString()方法。

代理类通过反射指定了代理类的所有方法。通过invoke执行了目标方法doSoemthing()。

猜你喜欢

转载自blog.csdn.net/starexplode/article/details/83042760