作者:青石路

cnblogs.com/youzhibing/p/10464274.html

什么是代理模式

所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的左右。

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问,通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

说简单点,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。一般而言会分三种:静态代理动态代理CGLIB代理

代理模式结构如下:

静态代理

静态代理需要代理对象和被代理对象实现一样的接口,我们来看个例子就清楚了

示例代理

https://gitee.com/youzhibing/proxy/tree/master/static-proxy

代理类:UserDaoProxy.java

/*** 代理逻辑在代理类中,而不是由用户自定义*/
public class UserDaoProxy implements IUserDao {private IUserDao target;            // 被代理对象public UserDaoProxy(IUserDao target) {this.target = target;}/***  前置/后置 处理一旦写完,就固定死了,后续想修改的话需要改此代理类* @param id* @return*/public int delete(int id) {// 前置处理,例如开启事务System.out.println("前置处理...");// 调用目标对象方法int count = target.delete(id);// 后置处理,例如提交事务或事务回滚System.out.println("前置处理...");return count;}
}

UserDaoProxy代理IUserDao类型,此时也只能代理IUserDao类型的被代理对象。测试结果就不展示了,相信大家看了代码也知道了

优点:可以在不修改目标对象的前提下扩展目标对象的功能

缺点:如果需要代理多个类,每个类都会有一个代理类,会导致代理类无限制扩展;如果类中有多个方法,同样的代理逻辑需要反复实现、应用到每个方法上,一旦接口增加方法,目标对象与代理对象都要进行修改

一个静态代理只能代理一个类,那么有没有什么方式可以实现同一个代理类来代理任意对象呢?肯定有的,也就是下面讲到的:动态代理。更多设计模式,可以参考:设计模式内容聚合

动态代理

代理类在程序运行时创建的代理方式被成为动态代理。也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。下面我们一步一步手动来实现动态代理。

下面的示例都是直接针对接口的,就不是针对接口的具体实现类了,静态代理示例中,UserDaoProxy代理的是IUserDao的实现类:UserDaoImpl,那么动态代理示例就直接针对接口了,下面示例针对的都是UserMapper接口,模拟的mybatis,但不局限于UserMapper接口

代理类源代码持久化

1、先利用反射动态生成代理类,并持久化代理类到磁盘(也就是生成代理类的java源文件),generateJavaFile方法如下

/*** 生成接口实现类的源代码, 并持久化到java文件* @param interface_* @param proxyJavaFileDir* @throws Exception*/private static void generateJavaFile(Class<?> interface_, String proxyJavaFileDir) throws Exception {StringBuilder proxyJava = new StringBuilder();proxyJava.append("package ").append(interface_.getPackage().getName()).append(";").append(ENTER).append(ENTER).append("public class ").append(PROXY_CLASS_NAME).append(" implements ").append(interface_.getName()).append(" {");Method[] methods = interface_.getMethods();for(Method method : methods) {Type returnType = method.getGenericReturnType();Type[] paramTypes = method.getGenericParameterTypes();proxyJava.append(ENTER).append(ENTER).append(TAB_STR).append("@Override").append(ENTER).append(TAB_STR).append("public ").append(returnType.getTypeName()).append(" ").append(method.getName()).append("(");for(int i=0; i<paramTypes.length; i++) {if (i != 0) {proxyJava.append(", ");}proxyJava.append(paramTypes[i].getTypeName()).append(" param").append(i);}proxyJava.append(") {").append(ENTER).append(TAB_STR).append(TAB_STR).append("System.out.println(\"数据库操作, 并获取执行结果...\");").append(ENTER); // 真正数据库操作,会有返回值,下面的return返回应该是此返回值if (!"void".equals(returnType.getTypeName())) {proxyJava.append(TAB_STR).append(TAB_STR).append("return null;").append(ENTER);      // 这里的"null"应该是上述中操作数据库后的返回值,为了演示写成了null}proxyJava.append(TAB_STR).append("}").append(ENTER);}proxyJava .append("}");// 写入文件File f = new File(proxyJavaFileDir + PROXY_CLASS_NAME + ".java");FileWriter fw = new FileWriter(f);fw.write(proxyJava.toString());fw.flush();fw.close();}

生成的代理类:$Proxy0.java 如下

package com.lee.mapper;public class $Proxy0 implements com.lee.mapper.UserMapper {@Overridepublic java.lang.Integer save(com.lee.model.User param0) {System.out.println("数据库操作, 并获取执行结果...");return null;}@Overridepublic com.lee.model.User getUserById(java.lang.Integer param0) {System.out.println("数据库操作, 并获取执行结果...");return null;}
}

这个代理类的生成过程是我们自己实现的,实现不难,但排版太繁琐,我们可以用javapoet来生成代理类源代码,generateJavaFileByJavaPoet方法如下

/*** 用JavaPoet生成接口实现类的源代码,并持久化到java文件* @param interface_ 目标接口类* @return*/
public static void generateJavaFileByJavaPoet(Class<?> interface_) throws Exception {// 类名、实现的接口,以及类访问限定符TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("JavaPoet$Proxy0").addSuperinterface(interface_).addModifiers(Modifier.PUBLIC);Method[] methods = interface_.getDeclaredMethods();for (Method method : methods) {// 方法参数列表List<ParameterSpec> paramList = new ArrayList<>();Type[] paramTypes = method.getGenericParameterTypes();int count = 1 ;for (Type param : paramTypes) {ParameterSpec paramSpec = ParameterSpec.builder(Class.forName(param.getTypeName()), "param" + count).build();count ++;paramList.add(paramSpec);}// 方法名、方法访问限定符、参数列表、返回值、方法体等Class<?> returnType = method.getReturnType();MethodSpec.Builder builder = MethodSpec.methodBuilder(method.getName()).addModifiers(Modifier.PUBLIC).addParameters(paramList).addAnnotation(Override.class).returns(returnType).addCode("\n").addStatement("$T.out.println(\"数据库操作, 并获取执行结果...\")", System.class)    // 真正数据库操作,会有返回值,下面的return返回应该是此返回值.addCode("\n");if (!"void".equals(returnType.getName())) {builder.addStatement("return null");       // 这里的"null"应该是上述中操作数据库后的返回值,为了演示写成了null}MethodSpec methodSpec = builder.build();typeSpecBuilder.addMethod(methodSpec);}JavaFile javaFile = JavaFile.builder(interface_.getPackage().getName(), typeSpecBuilder.build()).build();javaFile.writeTo(new File(SRC_JAVA_PATH));
}

生成的代理类:JavaPoet$Proxy0.java 如下

package com.lee.mapper;import com.lee.model.User;
import java.lang.Integer;
import java.lang.Override;
import java.lang.System;public class JavaPoet$Proxy0 implements UserMapper {@Overridepublic Integer save(User param1) {System.out.println("数据库操作, 并获取执行结果...");return null;}@Overridepublic User getUserById(Integer param1) {System.out.println("数据库操作, 并获取执行结果...");return null;}
}

利用javapoet生成的代理类更接近我们平时手动实现的类,排版更符合我们的编码习惯,看上去更自然一些;两者的实现过程是一样的,只是javapoet排版更好

2、既然代理类的源代码已经有了,那么需要对其编译了,compileJavaFile方法如下

/*** 编译代理类源代码生成代理类class* @param proxyJavaFileDir*/
private static void compileJavaFile(String proxyJavaFileDir) throws Exception {JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//获得文件管理者StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);Iterable<? extends JavaFileObject> fileObjects = manager.getJavaFileObjects(proxyJavaFileDir + PROXY_CLASS_NAME + ".java");//编译任务JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, fileObjects);//开始编译,执行完可在指定目录下看到.class文件task.call();//关闭文件管理者manager.close();
}

会在指定目录下看到:$Proxy0.class

3、加载$Proxy0.class,并创建其实例对象(代理实例对象)

public static <T> T newInstance(Class<T> interface_) throws Exception{String proxyJavaFileDir = SRC_JAVA_PATH + interface_.getPackage().getName().replace(".", File.separator) + File.separator;// 1、生成interface_接口的实现类,并持久化到磁盘:$Proxy0.javagenerateJavaFile(interface_, proxyJavaFileDir);// 2、编译$Proxy0.java,生成$Proxy0.class到磁盘compileJavaFile(proxyJavaFileDir);// 3、加载$Proxy0.class,并创建其实例对象(代理实例对象)MyClassLoader loader = new MyClassLoader(proxyJavaFileDir, interface_);Class<?> $Proxy0 = loader.findClass(PROXY_CLASS_NAME);return (T)$Proxy0.newInstance();
}private static class MyClassLoader<T> extends ClassLoader {private String proxyJavaFileDir;private Class<T> interface_;public MyClassLoader(String proxyJavaFileDir, Class<T> interface_) {this.proxyJavaFileDir = proxyJavaFileDir;this.interface_ = interface_;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {File clazzFile = new File(proxyJavaFileDir, name + ".class");//如果字节码文件存在if (clazzFile.exists()) {//把字节码文件加载到VMtry {//文件流对接class文件FileInputStream inputStream = new FileInputStream(clazzFile);ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while ((len = inputStream.read(buffer)) != -1) {baos.write(buffer, 0, len);                     // 将buffer中的内容读取到baos中的buffer}//将buffer中的字节读到内存加载为classreturn defineClass(interface_.getPackage().getName() + "." + name, baos.toByteArray(), 0, baos.size());} catch (Exception e) {e.printStackTrace();}}return super.findClass(name);}
}

有了代理实例对象,我们就可以利用它进行操作了,演示结果如下

完整工程地址:

https://gitee.com/youzhibing/proxy/tree/master/proxy-java-file

完整流程图如下

此时的Proxy类能创建任何接口的实例,解决了静态代理存在的代理类泛滥、多个方法中代理逻辑反复实现的问题;但有个问题不知道大家注意到:$Proxy0.java有必要持久化到磁盘吗,我们能不能直接编译内存中的代理类的字符串源代码,得到$Proxy0.class呢?

代理类源代码不持久化

$Proxy0.java$Proxy0.class是没必要生成到磁盘的,我们直接编译内存中的代理类的字符串源代码,同时直接在内存中加载$Proxy0.class,不用写、读磁盘,可以提升不少性能

完整工程地址:

https://gitee.com/youzhibing/proxy/tree/master/proxy-none-java-file

此时的流程图如下

Proxy.java源代码如下

package com.lee.proxy;import com.itranswarp.compiler.JavaStringCompiler;import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;public class Proxy {private static final String ENTER = "\r\n";private static final String TAB_STR = "    ";private static final String PROXY_FILE_NAME = "$Proxy0";/*** 生成接口实现类的源代码, 并持久化到java文件* @param interface_* @throws Exception*/private static String generateJavaFile(Class<?> interface_) throws Exception {StringBuilder proxyJava = new StringBuilder();proxyJava.append("package ").append(interface_.getPackage().getName()).append(";").append(ENTER).append(ENTER).append("public class ").append(PROXY_FILE_NAME).append(" implements ").append(interface_.getName()).append(" {");Method[] methods = interface_.getMethods();for(Method method : methods) {Type returnType = method.getGenericReturnType();Type[] paramTypes = method.getGenericParameterTypes();proxyJava.append(ENTER).append(ENTER).append(TAB_STR).append("@Override").append(ENTER).append(TAB_STR).append("public ").append(returnType.getTypeName()).append(" ").append(method.getName()).append("(");for(int i=0; i<paramTypes.length; i++) {if (i != 0) {proxyJava.append(", ");}proxyJava.append(paramTypes[i].getTypeName()).append(" param").append(i);}proxyJava.append(") {").append(ENTER).append(TAB_STR).append(TAB_STR).append("System.out.println(\"数据库操作, 并获取执行结果...\");").append(ENTER); // 真正数据库操作,会有返回值,下面的return返回应该是此返回值if (!"void".equals(returnType.getTypeName())) {proxyJava.append(TAB_STR).append(TAB_STR).append("return null;").append(ENTER);      // 这里的"null"应该是上述中操作数据库后的返回值,为了演示写成了null}proxyJava.append(TAB_STR).append("}").append(ENTER);}proxyJava .append("}");return proxyJava.toString();}private final static Class<?> compile(String className, String content) throws Exception {JavaStringCompiler compiler = new JavaStringCompiler();Map<String, byte[]> byteMap = compiler.compile(PROXY_FILE_NAME + ".java", content);Class<?> clazz = compiler.loadClass(className, byteMap);return clazz;}public static <T> T newInstance(Class<T> interface_) throws Exception{// 1、生成源代码字符串String proxyCodeStr = generateJavaFile(interface_);// 2、字符串编译成Class对象Class<?> clz = compile(interface_.getPackage().getName() + "." + PROXY_FILE_NAME, proxyCodeStr);return (T)clz.newInstance();}
}

相比有代理类源代码持久化,核心的动态代理生成过程不变,只是减少了.java和.class文件的持久化;其中用到了第三方工具:com.itranswarp.compile(我们也可以拓展jdk,实现内存中操作),完成了字符串在内存中的编译、class在内存中的加载,直接用jdk的编译工具,会在磁盘生成$Proxy0.class

测试结果如下

可以看到,没有.java和.class的持久化

此时就完美了吗?如果现在有另外一个接口ISendMessage,代理逻辑不是

System.out.println("数据库操作, 并获取执行结果...")

我们该怎么办?针对ISendMessage又重新写一个Proxy?显然还不够灵活,说的简单点:此种代理可以代理任何接口,但是代理逻辑确是固定死的,不能自定义,这样会造成一种代理逻辑会有一个代理工厂(Proxy),会造成代理工厂的泛滥。  更多的设计模式内容,可以在咱们Java知音公众号回复“设计模式聚合”,完整讲解23种设计模式

代理逻辑接口化,供用户自定义

既然无代理类源代码持久化中的代理逻辑不能自定义,那么我们就将它抽出来,提供代理逻辑接口

完整工程地址:

https://gitee.com/youzhibing/proxy/tree/master/proxy-none-java-file-plus

流程图与无代理类源代码持久化中一样,此时代理类的生成过程复杂了不少,涉及到代理逻辑接口:InvacationHandler的处理

generateJavaFile(…)方法

/*** 生成接口实现类的源代码* @param interface_* @throws Exception*/
private static String generateJavaFile(Class<?> interface_, InvocationHandler handler) throws Exception {StringBuilder proxyJava = new StringBuilder();proxyJava.append("package ").append(PROXY_PACKAGE_NAME).append(";").append(ENTER).append(ENTER).append("import java.lang.reflect.Method;").append(ENTER).append(ENTER).append("public class ").append(PROXY_FILE_NAME).append(" implements ").append(interface_.getName()).append(" {").append(ENTER).append(ENTER).append(TAB_STR).append("private InvocationHandler  handler;").append(ENTER).append(ENTER);// 代理对象构造方法proxyJava.append(TAB_STR).append("public ").append(PROXY_FILE_NAME).append("(InvocationHandler handler) {").append(ENTER).append(TAB_STR).append(TAB_STR).append("this.handler = handler;").append(ENTER).append(TAB_STR).append("}").append(ENTER);// 接口方法Method[] methods = interface_.getMethods();for(Method method : methods) {String returnTypeName = method.getGenericReturnType().getTypeName();Type[] paramTypes = method.getGenericParameterTypes();proxyJava.append(ENTER).append(TAB_STR).append("@Override").append(ENTER).append(TAB_STR).append("public ").append(returnTypeName).append(" ").append(method.getName()).append("(");List<String> paramList = new ArrayList<>();     // 方法参数值List<String> paramTypeList = new ArrayList<>(); // 方法参数类型for(int i=0; i<paramTypes.length; i++) {if (i != 0) {proxyJava.append(", ");}String typeName = paramTypes[i].getTypeName();proxyJava.append(typeName).append(" param").append(i);paramList.add("param" + i);paramTypeList.add(typeName+".class");}proxyJava.append(") {").append(ENTER).append(TAB_STR).append(TAB_STR).append("try {").append(ENTER).append(TAB_STR).append(TAB_STR).append(TAB_STR).append("Method method = ").append(interface_.getName()).append(".class.getDeclaredMethod(\"").append(method.getName()).append("\",").append(String.join(",", paramTypeList)).append(");").append(ENTER).append(TAB_STR).append(TAB_STR).append(TAB_STR);if (!"void".equals(returnTypeName)) {proxyJava.append("return (").append(returnTypeName).append(")");}proxyJava.append("handler.invoke(this, method, new Object[]{").append(String.join(",", paramList)).append("});").append(ENTER).append(TAB_STR).append(TAB_STR).append("} catch(Exception e) {").append(ENTER).append(TAB_STR).append(TAB_STR).append(TAB_STR).append("e.printStackTrace();").append(ENTER).append(TAB_STR).append(TAB_STR).append("}").append(ENTER);if (!"void".equals(returnTypeName)) {proxyJava.append(TAB_STR).append(TAB_STR).append("return null;").append(ENTER);}proxyJava.append(TAB_STR).append("}").append(ENTER);}proxyJava .append("}");// 这里可以将字符串生成java文件,看看源代码对不对/*String proxyJavaFileDir = System.getProperty("user.dir") + File.separator + "proxy-none-java-file-plus"+ String.join(File.separator, new String[]{"","src","main","java",""})+ PROXY_PACKAGE_NAME.replace(".", File.separator) + File.separator;File f = new File(proxyJavaFileDir + PROXY_FILE_NAME + ".java");FileWriter fw = new FileWriter(f);fw.write(proxyJava.toString());fw.flush();fw.close();*/return proxyJava.toString();
}

测试结果如下

此时各组件之间关系、调用情况如下

此时Proxy就可以完全通用了,可以生成任何接口的代理对象了,也可以实现任意的代理逻辑;至此,我们完成了一个简易的仿JDK实现的动态代理

JDK的动态代理

我们来看看JDK下动态代理的实现,示例工程:

https://gitee.com/youzhibing/proxy/tree/master/proxy-jdk

测试结果就不展示了,我们来看看JDK下Proxy.newInstance方法,有三个参数
1.Classloader:类加载器,我们可以使用自定义的类加载器;上述手动实现示例中,直接在Proxy写死了;

2.Class<?>[]:接口类数组,这个其实很容易理解,我们应该允许我们自己实现的代理类同时实现多个接口。我们上述手动实现中只传入一个接口,是为了简化实现;

3.InvocationHandler:这个没什么好说的,与我们的实现一致,用于自定义代理逻辑

我们来追下源码,看看JDK的动态代理是否与我们的手动实现是否一致

与我们的自定义实现差不多,利用反射,逐个接口、逐个方法进行处理;

ProxyClassFactory负责生成代理类的Class对象,主要由apply方法负责,调用了

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

来生成代理类的Class;ProxyGenerator中有个是有静态常量:saveGeneratedFiles,标识是否持久化代理类的class文件,默认值是false,也就是不持久化,我们可以通过设置jdk系统参数,实现JDK的动态代理持久化代理类的class文件

CGLIB代理

对cglib不做深入研究了,只举个使用案例:proxy-cglib,使用方式与JDK的动态代理类似,实现的效果也基本一致,但是实现原理上还是有差别的

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,而CGLIB没有这个限制,具体区别不是本文范畴了,大家自行去查阅资料

应用场景

长篇大论讲了那么多,我们却一直没有讲动态代理的作用,使用动态代理我们可以在不改变源码的情况下,对目标对象的目标方法进行前置或后置增强处理。这有点不太符合我们的一条线走到底的编程逻辑,这种编程模型有一个专业名称叫AOP,面向切面编程,具体案例有如下:

1、spring的事务,事务的开启可以作为前置增强,事务的提交或回滚作为后置增强,数据库的操作处在两者之间(目标对象需要完成的事);

2、日志记录,我们可以在不改变原有实现的基础上,对目标对象进行日志的输出,可以前置处理,记录参数情况,也可以后置处理,记录返回的结果;

3、web编程,传入参数的校验;

4、web编程,权限的控制也可以用aop来实现;

只要明白了AOP,那么哪些场景能使用动态代理也就比较明了了

总结

1、示例代码中的Proxy是代理工厂,负责生产代理对象的,不是代理对象类

2、手动实现动态代理,我们分了三版

第一版:代理类源代码持久化,为了便于理解,我们将代理类的java文件和class文件持久化到了磁盘,此时解决了静态代理中代理类泛滥的问题,我们的代理类工厂(Proxy)能代理任何接口;

第二版:代理类源代码不持久化,代理类的java文件和和class文件本来就只是临时文件,将其去掉,不用读写磁盘,可以提高效率;但此时有个问题,我们的代理逻辑却写死了,也就说一个代理类工厂只能生产一种代理逻辑的代理类对象,如果我们有多种代理逻辑,那么就需要有多个代理类工厂,显然灵活性不够高,还有优化空间;

第三版:代理逻辑接口化,供用户自定义,此时代理类工厂就可以代理任何接口、任何代理逻辑了,反正代理逻辑是用户自定义传入,用户想怎么定义就怎么定义;

3、示例参考的是mybatis中mapper的生成过程,虽然只是简单的模拟,但流程却是一致的

参考

《java与模式》
https://juejin.im/post/5a99048a6fb9a028d5668e62

END

Java面试题专栏

【51期】一道阿里面试题:说说你知道的关于BeanFactory和FactoryBean的区别

【52期】记一道简单的Java面试题,但答错率很高!

【53期】面试官:谈一下数据库分库分表之后,你是如何解决事务问题?

【54期】Java序列化三连问,是什么?为什么需要?如何实现?

【55期】面试中经常被问到Java引用类型原理,带你深入剖析

【56期】你说你熟悉并发编程,那么你说说Java锁有哪些种类,以及区别

【57期】面试官问,MySQL建索引需要遵循哪些原则呢?

【58期】盘点那些面试中最常问的MySQL问题,第一弹!

【59期】MySQL索引是如何提高查询效率的呢?(MySQL面试第二弹)

【60期】事务隔离级别中的可重复读能防幻读吗?(MySQL面试第三弹)

我知道你 “在看”

设计模式之代理:手动实现动态代理,揭秘原理实现相关推荐

  1. 【设计模式】--- 装饰器模式、静态代理模式和动态代理模式

    文章目录 1 引子 2 业务场景介绍 3 静态代理模式 4 装饰器模式 5 动态代理模式 5.1 Proxy --- 具体的代理对象生成组件 5.2 InvocationHandler --- 封装被 ...

  2. 动态代理,动态代理设计模式 ,JDK动态代理,cglib动态代理

    为什么80%的码农都做不了架构师?>>>    一:在看此篇代码示例前,先看静态代理, 链接地址:http://my.oschina.net/dyyweb/blog/656760   ...

  3. 23种设计模式之代理模式(动态代理)

    代理模式 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式的组成 抽 ...

  4. 设计模式—代理模式以及动态代理的实现

    代理模式(Proxy Design Pattern)是为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象.被代理的对象可以是远程对象.创建开销大的对象或需要安全控制的对象. 一 ...

  5. 代理详解 静态代理+JDK/CGLIB 动态代理实战

    1. 代理模式 代理模式是一种比较好理解的设计模式.简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对 ...

  6. cglib动态代理jar包_代理模式详解:静态代理+JDK/CGLIB 动态代理实战

    1. 代理模式 代理模式是一种比较好的理解的设计模式.简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标 ...

  7. 静态代理,JDK动态代理,Cglib动态代理详解

    目录 一.代理模式 二.静态代理 三.动态代理 3.1 JDK动态代理 3.2 Cglib动态代理 四.两种动态代理区别 一.代理模式 代理模式(Proxy Pattern)是程序设计中的一种设计模式 ...

  8. Java动态代理、CGLIB动态代理

    文章目录 代理模式 静态代理 动态代理 CGLIB动态代理 JDK动态代理源码分析 代理模式 代理模式是常见的设计模式之一,Java我们通常通过new一个对象然后调用其对应的方法来访问我们需要的服务. ...

  9. 代理模式和动态代理模式_代理模式介绍

    代理模式和动态代理模式 代表:被选中或当选为他人投票或代理的人– Merriam-Webster . 委托模式:在软件工程中,委托模式是面向对象编程中的一种设计模式,其中,一个对象而不是执行其陈述的任 ...

最新文章

  1. Python判断两个文件夹中互相不同的文件有哪些、判断一个文件夹相对于另外一个文件夹缺少了哪些文件
  2. 【Linux 经典面试题】
  3. pytorch优化器,学习率衰减学习笔记
  4. Nginx初学第一步
  5. java C++ 实现 leetcode 第三题 3. 无重复字符的最长子串 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
  6. java array 元素的位置_数据结构与算法:动态图解十大经典排序算法(含JAVA代码实现)...
  7. 图自编码器的起源和应用
  8. 【Qt】2D绘图之绘制文字
  9. Extjs 登录界面源码
  10. cocos2d-lua3.7组件篇(三)-http通信demo
  11. 《SpringCloud超级入门》使用Eureka编写服务消费者《十一》
  12. 不重复数字(洛谷 P4305)
  13. DotNetBar.Bar作为容器使用的方法及Text更新原理
  14. 如何复制对冲基金的成功?
  15. replace()替换文字扑获组做法
  16. Java静态代理类的特点和示例
  17. linux运维搭建官网,Linux运维学习之LAMP搭建个人博客网站
  18. mysql添加表字段脚本_mysql数据库修改字段及新增字段脚本
  19. 证件照的背景颜色转换
  20. 5.Django路由path和re_path详解

热门文章

  1. Apple Watch要用上microLED显示屏了 最早明年...
  2. 荣耀20 PRO差评太多?原来是这个原因,回应:差评不冤
  3. 小米组织架构调整:雷军兼任中国区总裁 组建大家电部门
  4. 救救孩子?强制实名游戏不足四成 青少年视力保护状况堪忧
  5. request_irq() | 注册中断服务【ZT】
  6. 方便快捷的php长文章分页函数,PHP长文章分页的一种实现方法
  7. python字符串出栈方法_Python 实现字符串反转的9种方法
  8. maven build后Downloading maven-metadata.xml
  9. Oracle的数据字典常用操作(持续更新)
  10. 【clickhouse】clickhouse 副本与分片 分片详解