java的动态代理机制详解
代理模式原理及实例讲解
代理模式详解包含原理详解
Java动态代理的实现
JAVA学习篇--静态代理VS动态代理
深入理解Java Proxy机制
一、什么是动态代理

在静态代理(Static Proxy)模式中,代理类都是真实存在的,由程序员提前创建好的java类,是静态的,每一个代理类在编译之后都会生成一个.class字节码文件,静态代理类所实现的接口和所代理的方法早在编译期都已被固定了。
动态代理(Dynamic Proxy)则不同:动态代理使用字节码动态生成和加载技术,在系统运行时动态地生成和加载代理类。
与静态代理相比,动态代理有以下优点:首先,无需再为每一个真实主题写一个形式上完全一样的代理类,假如抽象主题接口中的方法很多的话,为每一个接口方法写一个代理方法也很麻烦,同样地,如果后期抽象主题接口发生变动,则真实主题和代理类都要修改,不利于系统维护;其次,动态代理可以让系统根据实际需要来动态创建代理类,同一个代理类能够代理多个不同的真实主题类,并且可以代理多个不同的方法。

二、Java对动态代理的支持

从JDK 1.3版本开始,Java语言提供了对动态代理的支持,在Java中实现动态代理机制,需要用到 java.lang.reflect 包中的 InvocationHandler 接口和 Proxy 类,我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:

InvocationHandler

[html] view plaincopy
  1. InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
  2. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the
  3. method invocation is encoded and dispatched to the invoke method of its invocation handler.
[html] view plaincopy
  1. InvocationHandler 是代理实例的调用处理程序实现的接口。
  2. 每个代理实例都具有一个与之关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程
  3. 序的 invoke() 方法。

invoke() 方法形式如下:

[java] view plaincopy
  1. Object invoke(Object proxy,
  2. Method method,
  3. Object[] args)
  4. throws Throwable

InvocationHandler 接口只包含invoke()这唯一一个方法,该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组。

Proxy 

[html] view plaincopy
  1. Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass
  2. of all dynamic proxy classes created by those methods.
[html] view plaincopy
  1. Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。

Proxy提供给我们的静态方法有以下四个:

[html] view plaincopy
  1. //返回指定代理实例的调用处理程序。
  2. static InvocationHandler getInvocationHandler(Object proxy)
  3. //返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
  4. static Class<?><span style="white-space:pre">   </span>getProxyClass(ClassLoader loader, Class<?>[] interfaces)
  5. //当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
  6. static boolean<span style="white-space:pre">  </span>isProxyClass(Class<?> cl)
  7. //返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
  8. //方法中的 ClassLoader loader 参数用来指定动态代理类的类加载器,Class<?>[] interfaces用来指定动态代理类要实现的接口。
  9. //InvocationHandler h 用来指定与即将生成的动态代理对象相关联的调用处理程序
  10. static Object<span style="white-space:pre">   </span>newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

下面我们以为数据库增加日志记录功能(为简单起见,我们仅记录下所有操作的执行时间及操作执行的结果)为例来看一看如何使用这两个类实现动态代理:
为了使演示更清晰,在此先定义两个简单类,一个User类和一个Document类分别表示数据库中的用户记录和文档记录,其代码如下。

[java] view plaincopy
  1. public class User {
  2. // 用户在数据库中的ID
  3. private Long id;
  4. // 用户的姓名
  5. private String name;
  6. public User(Long id, String name) {
  7. super();
  8. this.id = id;
  9. this.name = name;
  10. }
  11. public Long getId() {
  12. return id;
  13. }
  14. public void setId(Long id) {
  15. this.id = id;
  16. }
  17. public String getName() {
  18. return name;
  19. }
  20. public void setName(String name) {
  21. this.name = name;
  22. }
  23. }
[java] view plaincopy
  1. public class Document {
  2. // 文档在数据库中的ID
  3. private Long id;
  4. // 文档的标题
  5. private String title;
  6. public Document(Long id, String title) {
  7. super();
  8. this.id = id;
  9. this.title = title;
  10. }
  11. public Long getId() {
  12. return id;
  13. }
  14. public void setId(Long id) {
  15. this.id = id;
  16. }
  17. public String getTitle() {
  18. return title;
  19. }
  20. public void setTitle(String title) {
  21. this.title = title;
  22. }
  23. }

另外还需定义一个DataBase类用来扮演数据库的功能,在此为简单起见,将数据库中的用户记录和文档记录分别存储在一个Map中,代码如下。

[java] view plaincopy
  1. import java.util.HashMap;
  2. import java.util.Map;
  3. public class DataBase {
  4. private static Map<Long, User> userMap = null;
  5. private static Map<Long, Document> documentMap = null;
  6. // 用来记录当前登陆用户信息
  7. private static User currentUser = null;
  8. private DataBase() {
  9. // 数据初始化,为数据库中增加几条用户记录。。。
  10. userMap = new HashMap<Long, User>();
  11. userMap.put(20160708L, new User(20160708L, "燕凌娇"));
  12. userMap.put(20160709L, new User(20160709L, "姬如雪"));
  13. userMap.put(20160710L, new User(20160710L, "百里登风"));
  14. // 数据初始化,为数据库中增加几条文档记录。。。
  15. documentMap = new HashMap<Long, Document>();
  16. documentMap.put(30160708L, new Document(30160708L, "C++常用算法手册"));
  17. documentMap
  18. .put(30160709L, new Document(30160709L, "深入理解Android内核设计思想"));
  19. documentMap.put(30160710L, new Document(30160710L, "Java从入门到放弃"));
  20. }
  21. public User getCurrentUser() {
  22. return currentUser;
  23. }
  24. public void setCurrentUser(User currentUser) {
  25. DataBase.currentUser = currentUser;
  26. }
  27. public Map<Long, User> getUserMap() {
  28. return userMap;
  29. }
  30. public Map<Long, Document> getDocumentMap() {
  31. return documentMap;
  32. }
  33. public static DataBase getDataBaseInstance() {
  34. return DataBaseHolder.dataBase;
  35. }
  36. public static class DataBaseHolder {
  37. private static DataBase dataBase = new DataBase();
  38. }
  39. }

数据库布置完成了,接下来开始写动态代理相关代码了,为了与静态代理进行比较,在此我们来创建两个接口(抽象主题角色)。

[java] view plaincopy
  1. public interface UserDao {
  2. // 登陆数据库,为了演示方便将字符串作为执行结果返回
  3. String login(Long id);
  4. // 退出数据库,为了演示方便将字符串作为执行结果返回
  5. String logout();
  6. }
[java] view plaincopy
  1. public interface DocumentDao {
  2. // 新增文档,为了演示方便将字符串作为执行结果返回
  3. String add(Document document);
  4. // 删除文档,为了演示方便将字符串作为执行结果返回
  5. String delete(Document document);
  6. }

接下来是两个真实主题角色,ImpUserDao 类和ImpDocumentDao类,为了使示例结果清晰,此处将接口的执行结果直接以字符串形式返回。

[java] view plaincopy
  1. public class ImpUserDao implements UserDao {
  2. @Override
  3. public String login(Long id) {
  4. User user = DataBase.getDataBaseInstance().getUserMap().get(id);
  5. if (null != user) {
  6. // 数据库有此用户的信息,则允许登陆...
  7. DataBase.getDataBaseInstance().setCurrentUser(user);
  8. return "用户[" + user.getName() + "]登陆成功...";
  9. } else {
  10. // 数据库没有此用户信息,则不让登陆...
  11. return "登陆失败,ID为\"" + id + "\"的用户不存在!";
  12. }
  13. }
  14. @Override
  15. public String logout() {
  16. User user = DataBase.getDataBaseInstance().getCurrentUser();
  17. if (null != user) {
  18. // 若当前有用户登陆,则退出成功...
  19. DataBase.getDataBaseInstance().setCurrentUser(null);
  20. return "用户[" + user.getName() + "]退出登陆成功...";
  21. } else {
  22. // 若当前无用户登陆,则退出失败...
  23. return "退出登陆失败,当前无登陆用户!";
  24. }
  25. }
  26. }
[java] view plaincopy
  1. public class ImpDocumentDao implements DocumentDao {
  2. @Override
  3. public String add(Document document) {
  4. User user = DataBase.getDataBaseInstance().getCurrentUser();
  5. if (null == user) {
  6. // 若当前用户未登陆,则新增文档失败...
  7. return "保存失败,未获取到登陆信息!";
  8. } else {
  9. Document dbDocument = DataBase.getDataBaseInstance()
  10. .getDocumentMap().get(document.getId());
  11. if (null != dbDocument) {
  12. // 若数据库中已经存在该ID的文档,则新增文档失败...
  13. return "添加文档《" + document.getTitle() + "》失败,文档已存在!";
  14. } else {
  15. // 若该ID的文档在数据库不存在,则新增文档成功...
  16. DataBase.getDataBaseInstance().getDocumentMap()
  17. .put(document.getId(), document);
  18. return "添加文档《" + document.getTitle() + "》成功...";
  19. }
  20. }
  21. }
  22. @Override
  23. public String delete(Document document) {
  24. User user = DataBase.getDataBaseInstance().getCurrentUser();
  25. if (null == user) {
  26. // 若当前用户未登陆,则新增文档失败...
  27. return "保存失败,未获取到登陆信息!";
  28. } else {
  29. Document dbDocument = DataBase.getDataBaseInstance()
  30. .getDocumentMap().get(document.getId());
  31. if (null == dbDocument) {
  32. // 若数据库中该文档不存在,则删除文档失败...
  33. return "删除文档《" + document.getTitle() + "》失败,文档不存在!";
  34. } else {
  35. // 若数据库中该文档存在,则删除文档成功...
  36. DataBase.getDataBaseInstance().getDocumentMap()
  37. .remove(document.getId());
  38. return "删除文档《" + document.getTitle() + "》成功...";
  39. }
  40. }
  41. }
  42. }

最后,我们就要定义动态代理类了,前面说过,每一个动态代理类都必须要实现 InvocationHandler 这个接口,因此我们这个动态代理类也不例外。

[java] view plaincopy
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.util.Calendar;
  4. import java.util.GregorianCalendar;
  5. public class DataBaseLogHandler implements InvocationHandler {
  6. private Object object;
  7. private Calendar calendar;
  8. public DataBaseLogHandler(Object object) {
  9. super();
  10. this.object = object;
  11. }
  12. //invoke()方法用于处理对代理类实例的方法调用并返回相应的结果
  13. @Override
  14. public Object invoke(Object proxy, Method method, Object[] args)
  15. throws Throwable {
  16. before(method);
  17. //继续转发请求给内部真实主题角色
  18. Object result = method.invoke(object, args);
  19. after(result);
  20. if (method.getName().equalsIgnoreCase("logout")) {
  21. System.out.println("**********************************");
  22. System.out.println("");
  23. }
  24. return result;
  25. }
  26. public void before(Method method) {
  27. calendar = new GregorianCalendar();
  28. int year = calendar.get(Calendar.YEAR);
  29. int month = calendar.get(Calendar.MONTH);
  30. int date = calendar.get(Calendar.DATE);
  31. int hour = calendar.get(Calendar.HOUR_OF_DAY);
  32. int minute = calendar.get(Calendar.MINUTE);
  33. int second = calendar.get(Calendar.SECOND);
  34. String time = hour + "时" + minute + "分" + second + "秒";
  35. System.out.println("北京时间:" + year + "年" + month + "月" + date + "日"
  36. + time + ",执行方法\"" + method.getName() + "\"");
  37. }
  38. public void after(Object object) {
  39. System.out.println("执行结果:" + object);
  40. }
  41. }

至此,动态代理所需要的类就算创建完成了,接下来创建一个Client充当客户端来测试一下。

[java] view plaincopy
  1. import java.lang.reflect.Proxy;
  2. public class Client{
  3. public static void main(String[] args) {
  4. // 此处来创建了两个动态代理类对象...
  5. UserDao userDao = new ImpUserDao();
  6. DataBaseLogHandler userHandler = new DataBaseLogHandler(userDao);
  7. DocumentDao doucumentDao = new ImpDocumentDao();
  8. DataBaseLogHandler documentHandler = new DataBaseLogHandler(
  9. doucumentDao);
  10. UserDao userProxy = (UserDao) Proxy.newProxyInstance(
  11. UserDao.class.getClassLoader(), new Class[] { UserDao.class },
  12. userHandler);
  13. DocumentDao documentProxy = (DocumentDao) Proxy.newProxyInstance(
  14. DocumentDao.class.getClassLoader(),
  15. new Class[] { DocumentDao.class }, documentHandler);
  16. // 先输入一个不存在的用户Id登陆试试...
  17. userProxy.login(20160718L);
  18. documentProxy.add(new Document(30160711L, "转角遇见幸福"));
  19. userProxy.logout();
  20. // 再用一个真实用户Id登陆试试...
  21. userProxy.login(20160708L);
  22. documentProxy.add(new Document(30160711L, "转角遇见幸福"));
  23. documentProxy.add(new Document(30160711L, "转角遇见幸福"));
  24. userProxy.logout();
  25. }
  26. }

运行程序打印结果如下:

[html] view plaincopy
  1. 北京时间:2016年6月11日19时33分46秒,执行方法"login"
  2. 执行结果:登陆失败,ID为"20160718"的用户不存在!
  3. 北京时间:2016年6月11日19时33分46秒,执行方法"add"
  4. 执行结果:保存失败,未获取到登陆信息!
  5. 北京时间:2016年6月11日19时33分46秒,执行方法"logout"
  6. 执行结果:退出登陆失败,当前无登陆用户!
  7. **********************************
  8. 北京时间:2016年6月11日19时33分46秒,执行方法"login"
  9. 执行结果:用户[燕凌娇]登陆成功...
  10. 北京时间:2016年6月11日19时33分46秒,执行方法"add"
  11. 执行结果:添加文档《转角遇见幸福》成功...
  12. 北京时间:2016年6月11日19时33分46秒,执行方法"add"
  13. 执行结果:添加文档《转角遇见幸福》失败,文档已存在!
  14. 北京时间:2016年6月11日19时33分46秒,执行方法"logout"
  15. 执行结果:用户[燕凌娇]退出登陆成功...
  16. **********************************

从以上程序的最终运行结果可以看出:通过使用JDK自带的动态代理,我们同时实现了对ImpUserDao和ImpDocumentDao两个真实主题类的统一代理和集中控制。至于该动态代理类是如何被创建的?将在下一篇文章详细讨论,接下来我们先看看如何使用CGLIB实现动态代理。

三、使用CGLIB实现动态代理

生成动态代理类的方法很多,如上例中JDK自带的动态代理、CGLIB、Javassist 或者 ASM 库。JDK 的动态代理使用简单,它内置在 JDK 中,因此不需要引入第三方 Jar 包,但相对功能比较弱。CGLIB 和 Javassist 都是高级的字节码生成库,总体性能比 JDK 自带的动态代理好,而且功能十分强大。ASM 是低级的字节码生成工具,使用 ASM 已经近乎于在使用 Java bytecode 编程,对开发人员要求最高,当然,也是性能最好的一种动态代理生成工具。但 ASM 的使用很繁琐,而且性能也没有数量级的提升,与 CGLIB 等高级字节码生成工具相比,ASM 程序的维护性较差,如果不是在对性能有苛刻要求的场合,还是推荐 CGLIB 或者 Javassist。
接下来我们继续用上面的例子来体验一下如何使用CGLIB实现动态代理。
首先,使用CGLIB来实现动态代理需引入“asm.jar”(CGLIB的底层是使用ASM实现的)和“cglib.jar”两个第三方jar包,引入两个jar包时需注意其版本,若版本有冲突会出现以下异常:Exception in thread "main" java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter.<init>(I)V
接下来开始写代码,延用上例中的:User类、Document类、DataBase类、两个抽象主题接口UserDao和DocumentDao、两个真实主题角色类ImpUserDao和ImpDocumentDao,这几个类无需做任何修改,此处不再重复贴出。
去掉上例中的 DataBaseLogHandler 类,新增一个 CglibProxy 类,该类需实现CGLIB的 MethodInterceptor(方法拦截) 接口。

[java] view plaincopy
  1. import java.lang.reflect.Method;
  2. import java.util.Calendar;
  3. import java.util.GregorianCalendar;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import net.sf.cglib.proxy.MethodInterceptor;
  6. import net.sf.cglib.proxy.MethodProxy;
  7. public class CglibProxy implements MethodInterceptor {
  8. private Calendar calendar;
  9. /**
  10. * 创建动态代理类对象
  11. *
  12. * @param clazz  需要创建子类代理的父类的类型
  13. *
  14. * @return
  15. */
  16. public Object getProxyInstance(Class<?> clazz) {
  17. Enhancer enhancer = new Enhancer();
  18. // 设置要创建的动态代理类的父类
  19. enhancer.setSuperclass(clazz);
  20. // 设置回调的对象,此句会导致调用动态代理类对象的方法会被指派到CglibProxy对象的intercept()方法
  21. enhancer.setCallback(this);
  22. // 通过字节码技术动态创建动态代理类实例
  23. return enhancer.create();
  24. }
  25. @Override
  26. // 回调方法
  27. public Object intercept(Object obj, Method method, Object[] args,
  28. MethodProxy proxy) throws Throwable {
  29. before(method);
  30. //通过动态子类代理实例调用父类的方法
  31. Object result = proxy.invokeSuper(obj, args);
  32. after(result);
  33. if (method.getName().equalsIgnoreCase("logout")) {
  34. System.out.println("**********************************");
  35. System.out.println("");
  36. }
  37. return result;
  38. }
  39. public void before(Method method) {
  40. calendar = new GregorianCalendar();
  41. int year = calendar.get(Calendar.YEAR);
  42. int month = calendar.get(Calendar.MONTH);
  43. int date = calendar.get(Calendar.DATE);
  44. int hour = calendar.get(Calendar.HOUR_OF_DAY);
  45. int minute = calendar.get(Calendar.MINUTE);
  46. int second = calendar.get(Calendar.SECOND);
  47. String time = hour + "时" + minute + "分" + second + "秒";
  48. System.out.println("北京时间:" + year + "年" + month + "月" + date + "日"
  49. + time + ",执行方法\"" + method.getName() + "\"");
  50. }
  51. public void after(Object object) {
  52. System.out.println("执行结果:" + object);
  53. }
  54. }

对客户端进行相应修改,如下。

[java] view plaincopy
  1. public class Client{
  2. public static void main(String[] args) {
  3. // 创建一个CglibProxy代理类对象,用来创建子类代理实例
  4. CglibProxy cglib = new CglibProxy();
  5. // 为ImpUserDao类添加一个动态代理类对象,即子类代理对象
  6. UserDao userProxy = (UserDao) cglib.getProxyInstance(ImpUserDao.class);
  7. // 为ImpDocumentDao类添加一个动态代理类对象,即子类代理对象
  8. DocumentDao documentProxy = (DocumentDao) cglib
  9. .getProxyInstance(ImpDocumentDao.class);
  10. // 先输入一个不存在的用户Id登陆试试...
  11. userProxy.login(20160718L);
  12. documentProxy.add(new Document(30160711L, "转角遇见幸福"));
  13. userProxy.logout();
  14. // 再用一个真实用户Id登陆试试...
  15. userProxy.login(20160708L);
  16. documentProxy.add(new Document(30160711L, "转角遇见幸福"));
  17. documentProxy.add(new Document(30160711L, "转角遇见幸福"));
  18. userProxy.logout();
  19. }
  20. }

运行程序结果打印如下,与之前使用JDK自带动态代理程序运行结果完全相同:

[html] view plaincopy
  1. 北京时间:2016年6月12日20时22分35秒,执行方法"login"
  2. 执行结果:登陆失败,ID为"20160718"的用户不存在!
  3. 北京时间:2016年6月12日20时22分35秒,执行方法"add"
  4. 执行结果:保存失败,未获取到登陆信息!
  5. 北京时间:2016年6月12日20时22分35秒,执行方法"logout"
  6. 执行结果:退出登陆失败,当前无登陆用户!
  7. **********************************
  8. 北京时间:2016年6月12日20时22分35秒,执行方法"login"
  9. 执行结果:用户[燕凌娇]登陆成功...
  10. 北京时间:2016年6月12日20时22分35秒,执行方法"add"
  11. 执行结果:添加文档《转角遇见幸福》成功...
  12. 北京时间:2016年6月12日20时22分35秒,执行方法"add"
  13. 执行结果:添加文档《转角遇见幸福》失败,文档已存在!
  14. 北京时间:2016年6月12日20时22分35秒,执行方法"logout"
  15. 执行结果:用户[燕凌娇]退出登陆成功...
  16. **********************************

PS:CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib采用动态创建子类的方法来对被代理的父类的功能进行增强和代理,所以,无法对被声明为final的类或方法进行代理。

四、动态代理模式的特点

动态代理类使用字节码动态生成加载技术,在运行时生成并加载代理类。与静态代理相比,动态代理具有以下优点:

-无需单独为每一个接口添加一个代理类,使用动态代理可以一次性为多个接口实现代理。
-无需逐个为接口中的所有方法添加实现,使用动态代理可以一次性为多个接口中的所有方法实现代理,在接口方法数量比较多的时候,可以避免出现大量的重复代码。

动态代理的缺点:
目前,JDK中提供的动态代理只能对接口实现代理,无法代理未实现接口的类。如果需要动态代理未实现接口的类,必须借助第三方工具,如:CGLib(Code Generation Library)等。

JAVA设计模式-11-代理模式(动态)(一)相关推荐

  1. Java设计模式-之代理模式(动态代理)

    一.简介 1.什么叫代理模式:        简而言之就是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...

  2. 【设计模式】代理模式 ( 动态代理 | 模拟 Java 虚拟机生成对应的 代理对象 类 )

    文章目录 前言 一.模拟 JVM 生成对应的 代理对象 二.模拟 JVM 生成对应的 代理对象 完整流程展示 1.目标对象接口 2.被代理对象 3.调用处理程序 4.模拟 JVM 生成的代理对象类 5 ...

  3. Java 设计模式_代理模式(2016-08-19)

    概念: 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 就是一个人或者机构代表另一个人或者机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一 ...

  4. Java设计模式(三) -- 代理模式

    代理是一种设计模式,即通过代理对象访问目标对象,这样可以在目标对象实现的基础上,增强额外的功能,即扩展目标类的功能. 通常编程中存在一种思想,即不要随意修改别人的代码或者方法,所以代理就可以在不修改目 ...

  5. Java设计模式之代理(动态代理,静态代理)

    代理设计模式,是一种常用的设计模式.顾名思义,在面向对象的世界中,就是一个对象对另一个对象的代理. 如现实生活中,处处都是代理,中介是对业主的代理,王婆是对金莲的代理,经纪人至于艺人的代理,销售代理等 ...

  6. java设计模式之代理模式详解

    代理模式在Java中十分常见,有如Spring实现AOP而使用动态代理,更有RPC实现中使用的调用端调用的代理服务.代理模型除了是一种设计模式之外,它更是一种思维,所以探讨并深入理解这种模型是非常有必 ...

  7. java设计模式之代理模式多种写法(三)

    背景 代理模式属于结构型模式,代理模式也是我们开发中常用的设计模式之一,本次讲解以下几种代理模式的写法,希望可以帮助大家更快的理解代理模式,包括自己写一个动态代理的方法. 静态代理 package c ...

  8. 【设计模式】代理模式 ( 动态代理使用流程 | 创建目标对象 | 创建被代理对象 | 创建调用处理程序 | 动态创建代理对象 | 动态代理调用 )

    文章目录 前言 一.静态代理的弊端 二.动态代理的优势 三.动态代理使用流程 1.目标对象接口 2.被代理对象 3.调用处理程序 4.客户端 四.动态生成 代理对象 类 的 字节码 文件数据 前言 代 ...

  9. Java设计模式之代理模式(Proxy)

    参考http://blog.csdn.net/jianghuxiaoxiami/article/details/3403924 1.代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的 ...

  10. Java 设计模式之代理模式

    1.静态代理 被代理对象和代理对象都要实现相同的接口或者继承相同的父类. 不需要修改被代理对象就可以实现对功能的扩展,缺点是如果接口发生变化,比如增加新方法,被代理对象和代理对象都需要维护. 1)首先 ...

最新文章

  1. 对抗 Google优势 微软考虑收购雅虎
  2. springboot实战 获取spring上下文的4种方式
  3. 体验是情感的(译稿)
  4. CSS-布局样式之筛选条件右边线的处理方法(no CSS3)
  5. python selenium 进入新标签页_Python 爬虫 | 用selenium实现批改网的自动翻译
  6. [导入]LINQ体验(11)——LINQ to SQL语句之Null语义和String/DateTime方法
  7. [html] 如何优化页面的渲染过程?
  8. 前端学习(2380):调整目录结构
  9. mysql5.7 glibcxx_3.4.15_CentOS6.5 缺少 libstdc++.so.6(GLIBCXX_3.4.15)
  10. HTTP、HTTPS、SSL、TLS之间的关系
  11. Java高级语法笔记-反射机制(Reflection) (1)
  12. 11 所允许的仓储单位类型没有针对仓储类型xxx定义
  13. Denoise 方法汇总
  14. Eclipse更改字体大小
  15. python下载付费文档教程-python爬文档
  16. java解决windows下文件没有Everyone以及完全控制的权限问题
  17. 分享 那些经典电影的经典台词
  18. Android_GitHub_xUtils之DbUtils、ViewUtils、HttpUtils、BitmapUtils
  19. 建立“顾客购买图书”的活动图(使用泳道)
  20. 影视后期制作学习(AE)(父子级链接-表达式)

热门文章

  1. Arnold置乱变换的代码实现与置乱度分析
  2. linux 运行.deb,Ubuntu下如何用命令行运行deb安装包
  3. AT32F407/437 PTP Daemon使用demo
  4. 回文数,回文字符串的判断
  5. js校验统一社会信用代码的合法性GB 32100-2015
  6. python tests in xxx问题
  7. imx6 android gadget,imx6q安卓启动失败显示Suspending console(s)(已解决)
  8. 用 DiskGenius 和 HDD Regenerator 修复硬盘逻辑坏道和隐藏物理坏道
  9. 商标注册成功后的中肯建议
  10. A Game of Thrones(58)