博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式 Proxy
阅读量:2747 次
发布时间:2019-05-13

本文共 23126 字,大约阅读时间需要 77 分钟。

什么是代理

# 增强一个对象的功能# 买火车票,App 就是一个代理,他代理了火车站,小区当中的代售窗口# Java 当中如何实现代理

Java 实现的代理的两种办法

代理的名词

代理对象 ===> 增强后的对象目标对象 ===> 被增强的对象他们不是绝对的,会根据情况发生变化

静态代理

1、继承代理对象继承目标对象,重写需要增强的方法;缺点:会代理类过多,非常复杂​2、聚合目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。缺点:也会产生类爆炸,只不过比继承少一点点​# 总结:如果在不确定的情况下,尽量不要去使用静态代理。因为一旦你写代码,就会产生类,一旦产生类就爆炸。

动态代理

自己模拟 JDK 动态代理

# 不需要订阅专栏手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。​缺点:首先要生成文件缺点:动态编译class文件缺点:需要一个 URLclassloader软件性能的最终体现在 IO 操作

接口:FutureDao

/** * @description: 接口 * @author: Mr.Li * @date: Created in 2020/7/1 15:42 * @version: 1.0 * @modified By: */public interface FutureDao {​    public void query();        public String returnString();}

实现类:FutureDaoImpl

/** * @description: 实现类 * @author: Mr.Li * @date: Created in 2020/7/1 15:43 * @version: 1.0 * @modified By: */public class FutureDaoImpl implements FutureDao {​    @Override    public void query(){        System.out.println("FutureLL");​    }        @Override    public String returnString() {        return "Return String";    }}

自己实现动态代理:ProxyUtil

import javax.tools.JavaCompiler;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;​/** * @description: 自己实现动态代理 * @author: Mr.Li * @date: Created in 2020/7/1 13:19 * @version: 1.0 * @modified By: */public class ProxyUtil {​    /**     * content --->string     * .java  io     * .class     * 

    * .new   反射----》class     *     * @return     */    public static Object newInstance(Object target) {​        // 定义代理对象        Object proxy = null;        // getClass(): Returns the runtime class of this {@code Object}.        // getInterfaces(): Determines the interfaces implemented by the class or interface represented by this object.        Class targetInf = target.getClass().getInterfaces()[0];        Method methods[] = targetInf.getDeclaredMethods();        String line = "\n";        String tab = "\t";        // getSimpleName(): Returns the simple name of the underlying class as given in the source code.        // infName = "FutureDao";        String infName = targetInf.getSimpleName();        String content = "";        String packageContent = "package com.google;" + line;        // Returns the name of the entity represented by this {@code Class} object, as a {@code String}.        // targetInf.getName() = "com.futurell.dao.FutureDao"        String importContent = "import " + targetInf.getName() + ";" + line;        String clazzFirstLineContent = "public class $Proxy implements " + infName + "{" + line;        String filedContent = tab + "private " + infName + " target;" + line;        String constructorContent = tab + "public $Proxy (" + infName + " target){" + line                + tab + tab + "this.target = target;"                + line + tab + "}" + line;        String methodContent = "";        for (Method method : methods) {            // getReturnType(): Returns a Class object that represents the formal return type of the method represented by this {@code Method} object.            String returnTypeName = method.getReturnType().getSimpleName();            // getName(): Returns the name of the method represented by this {@code Method} object, as a {@code String}.            String methodName = method.getName();            // Sting.class String.class            Class args[] = method.getParameterTypes();            String argsContent = "";            String paramsContent = "";            int flag = 0;            for (Class arg : args) {                //getSimpleName(): Returns the simple name of the underlying class as given in the source code.                String temp = arg.getSimpleName();                // String                // String p0,Sting p1,                argsContent += temp + " p" + flag + ",";                paramsContent += "p" + flag + ",";                flag ++;           }            if (argsContent.length() > 0) {                // lastIndexOf(): Returns the index within this string of the last occurrence of the specified substring.                argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1);                paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1);           }​            methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line                    + tab + tab + "System.out.println(\" Log \");" + line;​            if (returnTypeName.equals("void")) {                methodContent += tab + tab + "target." + methodName + "(" + paramsContent + ");" + line                        + tab + "}" + line;           } else {                methodContent += tab + tab + "return target." + methodName + "(" + paramsContent + ");" + line                        + tab + "}" + line;           }       }​        content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";​        File file = new File("e:\\com\\google\\$Proxy.java");        try {            if (!file.exists()) {                file.createNewFile();           }​            FileWriter fw = new FileWriter(file);            fw.write(content);            fw.flush();            fw.close();​            /**             * 编译过程如下:             */            // getSystemJavaCompiler(): Gets the Java; programming language compiler provided with this platform.            // 得到编译类,可以动态的编译一些文件            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();​            // getStandardFileManager(): Gets a new instance of the standard file manager implementation for this tool.            // 编译文件需要文件管理器            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);            // getJavaFileObjects(): Gets file objects representing the given files.            // 把文件放到文件管理器中            Iterable units = fileMgr.getJavaFileObjects(file);​            // getTask(): Creates a future for a compilation task with the given components and arguments.            // JavaCompiler.CompilationTask: Interface representing a future for a compilation task.            // 把文件管理器当成一个任务来执行            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);            // call(): Performs this compilation task.            t.call();            fileMgr.close();​            // URL[]: Class {@code URL} represents a Uniform Resource Locator, a pointer to a "resource" on the World Wide Web.            URL[] urls = new URL[]{new URL("file:E:\\\\")};            // URLClassLoader: This class loader(类加载器) is used to load classes and resources from a search path of URLs            //                 referring(引用) to both JAR files(Java Archive, Java归档) and directories(目录).            // JAR: 通常用于聚合大量的Java类文件、相关的元数据和资源(文本、图片等)文件到一个文件,以便开发Java平台应用软件或库。            URLClassLoader urlClassLoader = new URLClassLoader(urls);            // loadClass(): Loads the class with the specified binary name.            Class clazz = urlClassLoader.loadClass("com.google.$Proxy");            // getConstructor(): Returns a {@code Constructor} object that reflects(反应) the specified            //                   public constructor of the class represented by this {@code Class} object.            // clazz这个类它有构造方法,只能以构造方法来new这个类的实例,所以需要先得到该类的构造方法            Constructor constructor = clazz.getConstructor(targetInf);​            // newInstance(): Uses the constructor represented by this {@code Constructor} object to            //               create and initialize a new instance of the constructor's            //               declaring class, with the specified initialization parameters.            // 通过构造方法来创建实例            proxy = constructor.newInstance(target);            // clazz.newInstance();            // Class.forName()       } catch (Exception e) {            e.printStackTrace();       }​        /**         * public UserDaoLog(UserDao target){         *     this.target = target;         * }         */        return proxy;   }}

测试类:Test

import com.futurell.dao.FutureDao;import com.futurell.dao.FutureDaoImpl;import com.futurell.proxy.ProxyUtil;​/** * @description: 测试 * @author: Mr.Li * @date: Created in 2020/7/1 10:56 * @version: 1.0 * @modified By: */public class Test {​    public static void main(String[] args) {​        FutureDao fProxy = (FutureDao) ProxyUtil.newInstance(new FutureDaoImpl());        fProxy.query();​        System.out.println("------------------");​        FutureDao fProxyString = (FutureDao) ProxyUtil.newInstance(new FutureDaoImpl());        System.out.println(fProxyString.returnString());​    }}/** *  输出:  * ------------------ *  Log  * FutureLL * ------------------ *  Log  * Return String */

JDK 动态代理

接口和实现类与上述自定义相同

代理类:FutureInvocationHandler

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;​/** * @description: * @author: Mr.Li * @date: Created in 2020/7/2 12:24 * @version: 1.0 * @modified By: */public class FutureInvocationHandler implements InvocationHandler {​    Object target;​    public FutureInvocationHandler(Object target) {        this.target = target;    }​    /**     *     * @param proxy 代理对象     * @param method 代理对象包含目标对象,目标对象中的方法,就是method     * @param args 目标方法的参数     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("proxy");        return method.invoke(target, args);    }}

测试类:Test

import com.futurell.JDK.dao.FutureDao;import com.futurell.JDK.dao.FutureDaoImpl;import com.futurell.JDK.proxy.FutureInvocationHandler;​import java.lang.reflect.Proxy;​/** * @description: 测试 * @author: Mr.Li * @date: Created in 2020/7/1 10:56 * @version: 1.0 * @modified By: */public class Test {​    public static void main(String[] args) {​        FutureDao jdkProxy = (FutureDao) Proxy.newProxyInstance(                // getClassLoader(): Returns the class loader for the class.                // 判断一个类是否相同,需要根据类加载器是否相同来判断,为了保证加载器可用,那么传入当前所在类的ClassLoader                // 在自定义动态代理的代码中,使用了URLClassLoader,因为自定义的类不在工程当中                Test.class.getClassLoader(),                // Class targetInf = target.getClass().getInterfaces()[0];                // 自定义使用了上边的代码,我们需要得到接口,用来得到接口中的方法,然后对方法进行代理                new Class[]{FutureDao.class},                new FutureInvocationHandler(new FutureDaoImpl()));​        jdkProxy.query();        System.out.println("--------------");        System.out.println(jdkProxy.returnString());    }}

CGLIB

如果是要实现 CGLIB,需要先添加 cglib 依赖,然后实现一个接口 MethodInterceptor,重写 intercept() 方法

依赖

 
cglib
 
cglib
 
3.3.0
 
compile

代理类:MyMethodInterceptor

import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 自定义MethodInterceptor */public class MyMethodInterceptor implements MethodInterceptor{     /**     * sub:cglib生成的代理对象     * method:被代理对象方法     * objects:方法入参     * methodProxy: 代理方法     */    @Override    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        System.out.println("======插入前置通知======");        Object object = methodProxy.invokeSuper(sub, objects);        System.out.println("======插入后者通知======");        return object;    }}

JDK 底层原理

测试类:Test

FutureDao jdkProxy = (FutureDao) Proxy.newProxyInstance(    // getClassLoader(): Returns the class loader for the class.    // 判断一个类是否相同,需要根据类加载器是否相同来判断,为了保证加载器可用,那么传入当前所在类的ClassLoader    // 在自定义动态代理的代码中,使用了URLClassLoader,因为自定义的类不在工程当中    Test.class.getClassLoader(),    // Class targetInf = target.getClass().getInterfaces()[0];    // 自定义使用了上边的代码,我们需要得到接口,用来得到接口中的方法,然后对方法进行代理    new Class[]{FutureDao.class},    new FutureInvocationHandler(new FutureDaoImpl()));

测试类调用 Proxy 类的 newProxyInstance() 方法

/** * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. */@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,                                      Class
[] interfaces,                                      InvocationHandler h) throws IllegalArgumentException {        Objects.requireNonNull(h);​    // 拿到实现类的接口,使用接口可以对接口中的方法进行处理    final Class
[] intfs = interfaces.clone();    // 系统做的安全验证,这个不需要我们去管    // getSecurityManager(): Gets the system security interface.    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);​    /*     * Invoke its constructor with the designated invocation handler.     */    try {        if (sm != null) {            checkNewProxyPermission(Reflection.getCallerClass(), cl);       }​        // 通过 cl.getConstructor() 得到一个构造方法        final Constructor
cons = cl.getConstructor(constructorParams);        final InvocationHandler ih = h;        if (!Modifier.isPublic(cl.getModifiers())) {            AccessController.doPrivileged(new PrivilegedAction
() {                public Void run() {                    cons.setAccessible(true);                    return null;               }           });       }        // 这里将得到的构造方法 new 成一个对象        // 所以我们需要看这里 new 出来的对象是不是我们需要的代理对象        // 首先复制 cons.newInstance(new Object[]{h}),鼠标右键点击 Evaluate Expression,将复制的代码粘贴回车,看到 result 的值就是我们的代理对象        // 得到 cl 代理类,代理对象就轻易被 new 出来了,在我们的传入的是 new FutureInvocationHandler(new FutureDaoImpl())        // 所以最重要的是我们怎么得到的 cl 代理类,进入 getProxyClass0() 这个方法        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);   }}

进入 getProxyClass0() 我们要知道如何得到的 cl 代理类

/** * Generate a proxy class.  Must call the checkProxyAccess method * to perform permission checks before calling this. */private static Class
getProxyClass0(ClassLoader loader,                                       Class
... interfaces) {    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 创建代理类    // 这个方法只有一行代码,我们进入 get() 方法    return proxyClassCache.get(loader, interfaces);}

进入 get() 方法

/** * Look-up the value through the cache. This always evaluates the * {@code subKeyFactory} function and optionally evaluates * {@code valueFactory} function if there is no entry in the cache for given * pair of (key, subKey) or the entry has already been cleared. */public V get(K key, P parameter) {    Objects.requireNonNull(parameter);​    expungeStaleEntries();​    Object cacheKey = CacheKey.valueOf(key, refQueue);​    // lazily install the 2nd level valuesMap for the particular cacheKey    ConcurrentMap
> valuesMap = map.get(cacheKey);    if (valuesMap == null) {        ConcurrentMap
> oldValuesMap            = map.putIfAbsent(cacheKey,                              valuesMap = new ConcurrentHashMap<>());        if (oldValuesMap != null) {            valuesMap = oldValuesMap;       }   }​    // create subKey and retrieve the possible Supplier
stored by that    // subKey from valuesMap    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));    Supplier
supplier = valuesMap.get(subKey);    Factory factory = null;​    while (true) {        if (supplier != null) {            // supplier might be a Factory or a CacheValue
instance            // 进入这个 get() 方法            V value = supplier.get();            if (value != null) {                // 最终返回的是这个 value,我们不知道从哪里看的时候,就找到结果从下往上反推即可                // 向上看有一个 supplier.get() 它将返回值赋给了 value                return value;           }       }        // else no supplier in cache        // or a supplier that returned null (could be a cleared CacheValue        // or a Factory that wasn't successful in installing the CacheValue)​        // lazily construct a Factory        if (factory == null) {            factory = new Factory(key, parameter, subKey, valuesMap);       }​        if (supplier == null) {            supplier = valuesMap.putIfAbsent(subKey, factory);            if (supplier == null) {                // successfully installed Factory                supplier = factory;           }            // else retry with winning supplier       } else {            if (valuesMap.replace(subKey, supplier, factory)) {                // successfully replaced                // cleared CacheEntry / unsuccessful Factory                // with our Factory                supplier = factory;           } else {                // retry with current supplier                supplier = valuesMap.get(subKey);           }       }   }}

进入另一个 get() 方法

@Overridepublic synchronized V get() { // serialize access    // re-check    Supplier
supplier = valuesMap.get(subKey);    if (supplier != this) {        // something changed while we were waiting:        // might be that we were replaced by a CacheValue        // or were removed because of failure ->        // return null to signal WeakCache.get() to retry        // the loop        return null;   }    // else still us (supplier == this)​    // create new value    // 这个方法我们可以看出,结果需要返回 value,而 value 最终的取值是在下边的 try-finally 中取值    V value = null;    try {        // 首先我们进入 apply() 方法        value = Objects.requireNonNull(valueFactory.apply(key, parameter));   } finally {        if (value == null) { // remove us on failure            valuesMap.remove(subKey, this);       }   }    // the only path to reach here is with non-null value    assert value != null;​    // wrap value with CacheValue (WeakReference)    CacheValue
cacheValue = new CacheValue<>(value);​    // put into reverseMap    reverseMap.put(cacheValue, Boolean.TRUE);​    // try replacing us with CacheValue (this should always succeed)    if (!valuesMap.replace(subKey, this, cacheValue)) {        throw new AssertionError("Should not reach here");   }​    // successfully replaced us with new CacheValue -> return the value    // wrapped by it    // 返回 value    return value;}

进入 apply() 方法

@Overridepublic Class
apply(ClassLoader loader, Class
[] interfaces) {​    Map
, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);    // 这里的 interfaces 的所有接口只有一个 FutureDao,循环所有接口,将接口赋给 intf    for (Class
intf : interfaces) {        /*         * Verify that the class loader resolves the name of this         * interface to the same Class object.         */        Class
interfaceClass = null;        try {            // 这个有个点需要注意, intf 本身就是一个 Class,为什么这里还要再次得到一个 Class            // 判断对象是否相同的一个前提,是否是同一个类加载器            // 原因: 这里要判断两个接口是不是同一个接口,            interfaceClass = Class.forName(intf.getName(), false, loader);       } catch (ClassNotFoundException e) {                   }        // 如果两个不相同那么抛出异常: IllegalArgumentException,相等继续执行后面的代码        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;​    /*     * 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();        // 如果这个接口不是 public,那么引用的时候会有问题,这里需要做一个转换        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) {        // 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();    // 这里给 proxyPkg 加一个标识,防止并发的情况下类名相同    String proxyName = proxyPkg + proxyClassNamePrefix + num;​    /*     * Generate the specified proxy class.     * 生成指定的代理类的二进制文件,     */    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);        try {        // 我们知道一个对象的产生顺序:        // .java ---> .class ---ClassLoader---> JVM ---byte[]---> object        // 而这里需要返回一个 Class,那么将二进制文件转换成对象就在 defineClass0() 方法中进行        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());   }}

进入 defineClass0() 方法

// 这个方法是一个被 native 修饰的方法是一个本地方法,也就是一个 Java 调用非 Java 代码的接口,再开发 JDK 的时候底层使用了 C 或 C++ 开发(可以去看一下 OpenJDK),也就是说 C 和 C++ 负责把 byte[] 变成 Class 对象private static native Class
defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);

总结:

# 通过接口反射得到字节码,然后把字节码转换成 Class,就得到了我们想要的对象

有兴趣的同学可以关注我的个人公众号,期待我们共同进步!!!

转载地址:http://qxzad.baihongyu.com/

你可能感兴趣的文章
oracle中exp,imp的使用详解
查看>>
oracle drop表空间
查看>>
好心态
查看>>
Web MVC
查看>>
java强行删除文件(针对进程正在使用的文件的删除)
查看>>
【OSGi】OSGi框架的三个层次
查看>>
ActionContext,ServletContext和ServletActionContext有什么区别?
查看>>
Struts2标签 %{ } %{# }详解
查看>>
Java集合----HashSet的实现原理
查看>>
HashMap实现原理分析
查看>>
${pageContext.request.contextPath} JSP取得绝对路径
查看>>
ztree显示
查看>>
JQUERY树型插件ZTREE和插件tmpl()
查看>>
zTree--jQuery快速学习笔记
查看>>
jQuery基础知识
查看>>
[原]红帽 Red Hat Linux相关产品iso镜像下载
查看>>
win10和linux双系统的安装教程
查看>>
VMWare虚拟机无法打开内核设备"\\.\Global\vmx86"的解决方法
查看>>
linux中mv命令使用详解
查看>>
Linux下配置JDK以及报cannot restore segment prot after reloc: Permission denied错解决方案
查看>>