spring-4代理设计模式-源码解析

手动模拟的动态代理

  不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。

  自己模拟的动态代理:

  缺点:首先要生成文件

  缺点:动态编译文件 class

  缺点:需要一个URLclassloader

  模拟过程:

  Fille --(读取file,生成类的类对象)--> class(byte[]) --(通过类的类对象)-->得到对象 (可以是 clazz.newInstance();直接new 或者通过constructor.newInstance("invoke ");构造方法new 出

 

1、手动创建handle接口

public interface InterfaceHandle {
    public Object invoke(Method method, Object[] args);
}

 

2、实现handle接口

public class User_InvoctionHandle implements InterfaceHandle {
    private Object target;

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

    public Object invoke(Method method, Object[] args) {
        System.out.println("我是自定义invoke方法");
        try {
            return method.invoke(this.target, null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

3、手写代理类(附带逻辑重写)

public class ProxyUtil {
    public static Object getInstance(Class targetInfo, User_InvoctionHandle handle) {
        Object proxy = null;
//        Class targetInfo = target.getClass().getInterfaces()[0];

        String tab = "	";
        String line = "
";

        String implName = targetInfo.getSimpleName();
        //创建java内容
        String javaContent = "";
        //package
        String packageContent = "package com.proxy;" + line;

        //importClass
        String impPackageContent = "import "+handle.getClass().getName()+";"+line+
                                   "import java.lang.reflect.Method;"+line+
                                   "import "+targetInfo.getName()+";"+line;

        //创建类体
        String classContent = "public class $Proxy implements " + implName + " {" + line;

        //创建私有对象handle
        String privateObject = tab + "private " + handle.getClass().getSimpleName() + " h;" + line;

        //创建构造,将handle当做对象
        String constructorContent = tab + "public $Proxy (" + handle.getClass().getSimpleName() + " h ){" + line;
        constructorContent +=   tab + tab + "this.h = h;";
        constructorContent +=   line + tab + "}" + line;

        //创建方法
        String methedContent = "";
        Method[] methods = targetInfo.getDeclaredMethods();
        for (Method method : methods) {
            //获取方法的返回类型
            String methodTypeName = method.getReturnType().getSimpleName();
            //获取方法的名字
            String methodName = method.getName();
            methedContent += tab + "public " + methodTypeName + " " + methodName + " (";
            //创建参数
            Object[] args = method.getParameterTypes();
            String argContent = "";
            for (int i = 0; i < args.length - 1; i++) {
                //获取参数的类型
                String argsTypeName = args[i].getClass().getSimpleName();
                //获取参数名称 i1 i2
                argContent = argsTypeName + " i" + i;
                if (i != args.length - 1) {
                    //多个参数的情况下需要使用','但是最后一个不需要
                    argContent += ",";
                }
            }
            //组装方法内容,会自动将方法与目标方法一致
            methedContent += argContent + "){"
                    + line + tab + tab +"try{"
                    + line + tab + tab + tab + "Method method = Class.forName(""+targetInfo.getName()+"").getDeclaredMethod(""+method.getName()+"");"+line + tab + tab + tab ;
            if(!"void".equals(methodTypeName)){
                methedContent+= "return ("+methodTypeName+")";
            }
                methedContent+=  "h.invoke(method,null);" + line
                    + tab + tab +"}catch(Exception e){"
                    + line + tab + tab + tab + "e.printStackTrace();"
                    + line + tab + tab +"}";
            if(!"void".equals(methodTypeName)){
                methedContent+= line + tab + tab +"return null;";
            }
            methedContent+= line + tab +"}"+line+line;

        }
        javaContent = packageContent + impPackageContent + classContent + privateObject +
                      constructorContent +
                      methedContent + line + "}";
        //1、使用IO字符流将创建好String 放到D盘中,用于查看是否存在问题。
        String filePath = "D:\com\proxy\";
        String classFileName = "com.proxy.$Proxy";
        File fileDir = new File("D:\com\proxy\");
        try {
            if (!fileDir.isDirectory()) {
                fileDir.mkdirs();
            }
            File file = new File("D:\com\proxy\$Proxy.java");
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(javaContent);
            fileWriter.flush();
            fileWriter.close();

            //创建java编译器
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            //第三方管理器
            StandardJavaFileManager fileMgr = javaCompiler.getStandardFileManager(null, null, null);
            //将java文件放到管理器中
            Iterable units = fileMgr.getJavaFileObjects(file);
            //创建编译任务
            JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileMgr, null, null, null, units);
            //开始启动任务
            task.call();
            fileMgr.close();

            //使用反射获取编译后的$Proxy对象
            URL [] urls = new URL[]{new URL("file:D:\\")};
            URLClassLoader ucl = new URLClassLoader(urls);
            Class clazz = ucl.loadClass(classFileName);
            Constructor constructor = clazz.getConstructor(User_InvoctionHandle.class);
            proxy = constructor.newInstance(handle);
            System.out.println("成功!");
        } catch (Exception e) {
            System.out.println("失败!");
            e.printStackTrace();
        }
        return proxy;
    }
}

  

4、测试

 public static void main(String args[]) {
        //调用手动写的代理
        ObjectDao objectDao = (ObjectDao) ProxyUtil.getInstance(ObjectDao.class, new User_InvoctionHandle(new User_Defined_Dao()));
        System.out.println(objectDao.queryStr());

      
    }

  

5、测试重生成的$Proxy代理类及class

自动生成代理类的编码

package com.dao;
import com.handle.User_InvoctionHandle;
import java.lang.reflect.Method;
import com.dao.ObjectDao;
public class $Proxy implements ObjectDao {
	private User_InvoctionHandle h;
	public $Proxy (User_InvoctionHandle h ){
		this.h = h;
	}
	public void query (){
		try{
			Method method = Class.forName("com.dao.ObjectDao").getDeclaredMethod("query");
			h.invoke(method,null);
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	public String queryStr (){
		try{
			Method method = Class.forName("com.dao.ObjectDao").getDeclaredMethod("queryStr");
			return (String)h.invoke(method,null);
		}catch(Exception e){
			e.printStackTrace();
		}
		return null;
	}


}

  

结果

 手写带参数动态代理

package com.proxy;

/*
 * 对象是如何生成的?
 * java
 * class
 * new
 * **/

import com.handle.User_InvoctionHandle;

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.lang.reflect.Type;
import java.net.URL;
import java.net.URLClassLoader;

public class ProxyUtil {
    public static Object getInstance(Class targetInfo, User_InvoctionHandle handle) {
        Object proxy = null;
//        Class targetInfo = target.getClass().getInterfaces()[0];

        String tab = "	";
        String line = "
";

        String implName = targetInfo.getSimpleName();
        //创建java内容
        String javaContent = "";
        //package
        String packageContent = "package com.proxy;" + line;

        //importClass
        String impPackageContent = "import "+handle.getClass().getName()+";"+line+
                                   "import java.lang.reflect.Method;"+line+
                                   "import "+targetInfo.getName()+";"+line;

        //创建类体
        String classContent = "public class $Proxy implements " + implName + " {" + line;

        //创建私有对象handle
        String privateHandle = tab + "private " + handle.getClass().getSimpleName() + " h;" + line;

        //创建构造,将handle当做对象
        String constructorContent = tab + "public $Proxy (" + handle.getClass().getSimpleName() + " h ){" + line;
        constructorContent +=   tab + tab + "this.h = h;";
        constructorContent +=   line + tab + "}" + line;

        //创建方法
        String methedContent = "";
        Method[] methods = targetInfo.getDeclaredMethods();
        for (Method method : methods) {
            //获取方法的返回类型
            String methodTypeName = method.getReturnType().getSimpleName();
            //获取方法的名字
            String methodName = method.getName();
            methedContent += tab + "public " + methodTypeName + " " + methodName + " (";
            //创建参数
            Object[] args = method.getParameterTypes();
            String argContent = "";
            String argClassContent = "";
            String argValue = "Object [] objects = new Object[]{";

            for (int i = 0; i < args.length ; i++) {
                //获取参数的类型
                String allTypeName = ((Class)args[i]).getName();
                String argsTypeName = allTypeName.substring(allTypeName.lastIndexOf('.')+1,allTypeName.length());

                //获取参数名称 i1 i2
                argContent += argsTypeName + " i" + i;
                argClassContent+= ","+argsTypeName+".class";

                argValue += "i"+i;
                if (i != args.length - 1) {
                    //多个参数的情况下需要使用','但是最后一个不需要
                    argContent += "";
                    argValue +=",";
//                    argClassContent+=",";

                }else{
                    argValue+="};"+line;
                }
            }
            //组装方法内容
            methedContent += argContent + "){"
                    + line + tab + tab +"try{";
            //如果没有参数则不创建Objects
            if(argContent!=null && !"".equals(argContent)){
                methedContent+=line+tab+tab+tab+argValue;
            }
            methedContent+= line + tab + tab + tab + "Method method = Class.forName(""+targetInfo.getName()+"").getDeclaredMethod(""+method.getName()+""";
            methedContent+=argClassContent;
            methedContent+=");"+line ;


            if(!"void".equals(methodTypeName)){
                methedContent+= tab+tab+tab+"return ("+methodTypeName+")";
            }
                methedContent+=  "h.invoke(method";
            if(argContent!=null && !"".equals(argContent)){
                methedContent+=",objects";
            }
                methedContent+=  ");" + line
                    + tab + tab +"}catch(Exception e){"
                    + line + tab + tab + tab + "e.printStackTrace();"
                    + line + tab + tab +"}";
            if(!"void".equals(methodTypeName)){
                methedContent+= line + tab + tab +"return null;";
            }
            methedContent+= line + tab +"}"+line+line;

        }
        javaContent = packageContent + impPackageContent + classContent + privateHandle +
                      constructorContent +
                      methedContent + line + "}";
        //1、使用IO字符流将创建好String 放到D盘中,用于查看是否存在问题。
        String filePath = "D:\com\proxy\";
        String classFileName = "com.proxy.$Proxy";
        File fileDir = new File("D:\com\proxy\");
        try {
            if (!fileDir.isDirectory()) {
                fileDir.mkdirs();
            }
            File file = new File("D:\com\proxy\$Proxy.java");
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file);
            fileWriter.write(javaContent);
            fileWriter.flush();
            fileWriter.close();

            //创建java编译器
            JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
            //第三方管理器
            StandardJavaFileManager fileMgr = javaCompiler.getStandardFileManager(null, null, null);
            //将java文件放到管理器中
            Iterable units = fileMgr.getJavaFileObjects(file);
            //创建编译任务
            JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileMgr, null, null, null, units);
            //开始启动任务
            task.call();
            fileMgr.close();

            //使用反射获取编译后的$Proxy对象
            URL [] urls = new URL[]{new URL("file:D:\\")};
            URLClassLoader ucl = new URLClassLoader(urls);
            Class clazz = ucl.loadClass(classFileName);
            Constructor constructor = clazz.getConstructor(User_InvoctionHandle.class);
            proxy = constructor.newInstance(handle);
            System.out.println("成功!");
        } catch (Exception e) {
            System.out.println("失败!");
            e.printStackTrace();
        }
        return proxy;
    }
}  

自定义写的动态代理缺点:

  需要生成文件

  动态编译文件

  IO操作

  需要一个URLClassloader

  性能问题

JDK动态代理源码分析

  file(java)-class-baty[]-object-User

  使用jdk代理方式查看生成的代理类在此之前我们要先读一下源码,以下代码是生成一个代理对象的实体,也就是动态代理实现接口+聚合方式生成的代理类(如不理解可以查看上章节级上上章节的知识)

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         *查找或生成指定的代理类 这里返回的是查看代理缓存里面是否存在同一个类加载器加载的接口 return proxyClassCache.get(loader,interfaces)
         *扩展,判断两个对象是否相同,首先先要判断是否是同一个类加载器加载进来的对象。(类似两个不同的公司【代表类加载器】,就算部门【对象】一致也不能说这两个部门是同一部门)
         *从这段代码中,我们可以看出jkd代理,底层是存在缓存的,也就是当jdk每次代理一个接口时,已经会在完成前先存入缓存,方便以后使用。(可想而知,高级开发[规范有必要]中一定应该想到性能的问题)
     **/
       Class<?> cl = getProxyClass0(loader, intfs); /* * 使用指定的调用处理程序调用其构造函数。 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); }         //调用静态构造器 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h;
        //判断这个方法的修饰是否为public(很关键,因为这个设计到相关包的问题,如果不是public则不是同包的情况下不能引包) if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); }
        //这里的cons就是代理器的实体对象,里面包含了目标对象,非常重要 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); } }

  从上述方法中可以看出Constructor获取的对象cons很重要,因为从此代码往下就是直接使用并获取InvocationHandle对象了,下面深入了解一下当中的构造类的构造对象如何获取

final Constructor<?> cons = cl.getConstructor(constructorParams); 

 此方法就是从缓存中获取代理对象,如果没有缓存则使用其他方式获取代理对象,请看以下代码

如何看源码?在这里叫大家一个比较使用的方式,直接找到返回值查看这个返回值是否是想要的,如果是则查看生成返回值得地方就可以
public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // 懒加载cacheKey的第2级valuesMap
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    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);
                }
            }
        }
    }

  以上代码中我们得到的就是value 而这个value就是我们想要的$Proxy对象


根据上述截图将断点设置在value=supplier.get();
并F5跟进去
根据以上方法我们最终进入到Proxy类中的内部类
@Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 *验证类加载器是否解析了这个类的名称
                 * 查看这个接口数组中的对象是否与自身相等,这就是上述扩展中提到的查看两个对象是否一致,首先要判断是否是用一个加载器加载进来的对象
                 */interfaceClass!=intf?   这个地方就是判断,很奇怪的判断方式有没有
                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");
                }
                /*
                 * 判断interfaceClass是否是一个接口(这里说的就是前2章节说的,jdk动态代理是由接口方式实现的,也必须是接口否则就会抛出异常"IllegalArgumentExcaption这不是一个接口")
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * 验证代理对象实现的接口是否在内存中单例
                 */
                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;

            /*
             * 记录一个非公共代理接口包,以便代理类将在同一个包中定义。验证所有非公共代理接口都在同一个包中
             */
            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) {
                // 如果没有非公共代理接口,则默认使用com.cun 代理包
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * 给生成的代理类取名字
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成代理类,重中之重,生成的代理类是一个byte数组使用的是ProxyGenerator中的generateProxyClass方法,
        *可以使用此方法通过IO方式生成代理类的.class文件然后通过反编译查看jdk生成的代理类是什么样子的
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                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());
            }
        }
    }

  

使用byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);生成jdk代理类.class文件
//获取JDK生成的代理对象.Class文件
        byte bytes[] =ProxyGenerator.generateProxyClass("Proxy_wjw",new Class []{(ObjectDao.class)});
        //为了方便直接在方法中throws Exception
        FileOutputStream fileOutputStream = new FileOutputStream("D:\$Proxy_JDK.class");
        fileOutputStream.write(bytes);
        fileOutputStream.flush();
        fileOutputStream.close();

  

 $Proxy_JDK.class文件反编译后

pa
package com.handle;

import com.dao.ObjectDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy_JDK extends Proxy implements ObjectDao {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m2;
private static Method m0;

public $Proxy_JDK(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 queryStr(String var1) throws {
try {
return (String)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}

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

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

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.dao.ObjectDao").getMethod("queryStr", Class.forName("java.lang.String"));
m3 = Class.forName("com.dao.ObjectDao").getMethod("query");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}

}

  

区别就是多了一个继承和几个方法(equals、toString、hashCOde)

defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);此方法是JDK将bety数组转换成对象的底层方法
总结:通过接口反射成字节码,然后把字节码转换成class,再通过native方法转成对象

遗留疑问:JDK代理和cglib代理的区别?spring底层就是用的cglib,预知后续请持续关注





  

  

原文地址:https://www.cnblogs.com/gnwzj/p/11117781.html