一篇博客读懂设计模式之---动态代理与反射

一篇博客读懂设计模式之---动态代理与反射

 

先来讲一下反射:

1 关于反射
反射最大的作用之一就在于我们可以不用在编译时就知道某个对象的类型,而在运行时通过提供完整的”包名+类名.class”得到。注意:不是在编译时,而是在运行时

功能:
•在运行时能判断任意一个对象所属的类
•在运行时能构造任意一个类的对象。
•在运行时判断任意一个类所具有的成员变量和方法
•在运行时调用任意一个对象的方法。
说大白话就是,利用Java反射机制我们可以加载一个运行时才得知名称的class,获悉其构造方法,并生成其对象实体,能对其fields设值并唤起其methods。

应用场景:
反射技术常用在各类通用框架开发中。因为为了保证框架的通用性,需要根据配置文件加载不同的对象或类,并调用不同的方法,这个时候就会用到反射——运行时动态加载需要加载的对象。

缺点:
由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

2 动态代理
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在两者之间起到中介的作用(可类比房屋中介,房东委托中介销售房屋、签订合同等)。
所谓动态代理,就是实现阶段不用关心代理谁,而是在运行阶段才指定代理哪个一个对象(不确定性)。如果是自己写代理类的方式就是静态代理(确定性)。

组成要素:
(动态)代理模式主要涉及三个要素:
其一:抽象类接口
其二:被代理类(具体实现抽象接口的类)
其三:动态代理类:实际调用被代理类的方法和属性的类

 

动态代理有三个必要条件:

1. 要有两个角色(代理对象和被代理对象)

2. 代理对象要持有被代理对象的引用(要有被代理对象的信息)

3. 注重过程,必须要做的事情,被代理对象没时间或者不想做

 


实现方式:
实现动态代理的方式很多,比如 JDK 自身提供的动态代理,就是主要利用了反射机制。还有其他的实现方式,比如利用字节码操作机制,类似 ASM、CGLIB(基于 ASM)、Javassist 等。

 

举例,常可采用的JDK提供的动态代理接口InvocationHandler来实现动态代理类。其中invoke方法是该接口定义必须实现的,它完成对真实方法的调用。通过InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。此外,我们常可以在invoke方法实现中增加自定义的逻辑实现,实现对被代理类的业务逻辑无侵入。

 

代理模式在我们生活中其实都有很多形象的例子:比如媒婆,租房中介等等,都是典型的动态代理模式,下面我们就以大家比较熟悉的媒婆来举例,更加生动形象的来介绍动态代理:

首先先用JDK来实现动态代理,必要条件:被代理对象要实现一个接口:

 

public interface Person {void findGF();
}
public class James implements Person{@Overridepublic void findGF() {System.out.println("高富帅");System.out.println("有房有车的");System.out.println("身高要求180cm以上,体重70kg");}
}
public class Meipo implements InvocationHandler {private Object target; //被代理对象的引用作为一个成员变量保存下来了//获取被代理人的个人资料public Object getInstance(Object target) throws Exception{this.target = target;Class clazz = target.getClass();System.out.println("被代理对象的class是:"+clazz);return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是媒婆:" + "得给你找个异性才行");System.out.println("开始进行海选...");System.out.println("------------");//调用被代理类的方法,这里就会调用James的方法method.invoke(this.target, args);System.out.println("------------");System.out.println("如果合适的话,就准备办事");return null;}
}

 

测试类:

public class TestJDKProxy {public static void main(String[] args) {try{Person person = (Person) new JDKMeiPo().getInstance(new XiaoJie());//下面调用findGF()就会调用invoke()方法person.findGF();}catch (Exception e){e.printStackTrace();}}
}

 

 

下面用CGLIB方式来实现动态代理:CGLIB相比JDK的方式优势是:被代理类不一定要去实现接口,它是通过CGLIB来生成被代理类的子类,继承父类的方法和属性,从而达到代理的效果:

public class James {public void findLove(){System.out.println("肤白貌美大长腿");}
}

代理类需要实现MethodInterceptor接口


public class Meipo implements MethodInterceptor{//好像并没有持有被代理对象的引用?public Object getInstance(Class clazz) throws Exception{Enhancer enhancer = new Enhancer();//把父类设置为谁?//这一步就是告诉cglib,生成的子类需要继承哪个类enhancer.setSuperclass(clazz);//设置回调enhancer.setCallback(this);//第一步、生成源代码//第二步、编译成class文件//第三步、加载到JVM中,并返回被代理对象return enhancer.create();}//同样是做了字节码重组这样一件事情//对于使用API的用户来说,是无感知@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("我是媒婆:" + "得给你找个异性才行");System.out.println("开始进行海选...");System.out.println("------------");//这个obj的引用是由CGLib给我们new出来的//cglib new出来以后的对象,是被代理对象的子类(继承了我们自己写的那个类)//OOP, 在new子类之前,实际上默认先调用了我们super()方法的,//new了子类的同时,必须先new出来父类,这就相当于是间接的持有了我们父类的引用//子类重写了父类的所有的方法//我们改变子类对象的某些属性,是可以间接的操作父类的属性的proxy.invokeSuper(obj, args);System.out.println("------------");System.out.println("如果合适的话,就准备办事");return null;}
}

 

public class TestGglibProxy {public static void main(String[] args) {//JDK的动态代理是通过接口来进行强制转换的//生成以后的代理对象,可以强制转换为接口//CGLib的动态代理是通过生成一个被代理对象的子类,然后重写父类的方法//生成以后的对象,可以强制转换为被代理对象(也就是用自己写的类)//子类引用赋值给父类try {James obj = (James)new Meipo().getInstance(James.class);obj.findLove();} catch (Exception e) {e.printStackTrace();}}
}

 

额外:

  静态代理:事先写好代理类,可以手工编写,也可以用工具生成。缺点是每个业务类都要对应一个代理类,非常不灵活。
   动态代理:运行时自动生成代理对象。缺点是生成代理代理对象和调用代理方法都要额外花费时间
  JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才能用这种办法生成代理对象。新版本也开始结合ASM机制。
   cglib动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类
Java 发射机制的常见应用:动态代理(AOP、RPC)、提供第三方开发者扩展能力(Servlet容器,JDBC连接)、第三方组件创建对象(DI)……

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558972.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git初学札记(五)————Branch分支管理

引言 正如之前的博客中提到的,Git区别于Svn的一个最明显的功能就是分支管理功能。 那么什么是分支?分支又能为我们的开发带来什么翻天覆地的变化呢?(为了使博客的内容更具权威性和专业性,以下部分内容摘自官方文档《Gi…

多线程中 start()和run()方法的区别

多线程中 start()和run()方法的区别:Java线程一直是一个比较容易困扰的地方,首先,我们来认识下怎样生存线程。认识 Thread 和Runnablejava中实现多线程有两种途径:继承Thread类或者实现Runnabl…

Git初学札记(六)————在远程新建本地Branch与在本地新建远程Branch

引言 本篇博客介绍将现有的本地分支以新的分支形式推送到远程库中,和以新的分支的形式从远程库中拉取一个分支。这两个功能都是比较简单的操作,但是在实际开发中,可能会在开发初期有所触及。比如我们希望将远程的dev分支拉取到本地来进行开发…

Git初学札记(七)————合并分支(merge)

目录 引言 开始Merge 1、History视图 2、Team菜单 3、Git Repositories视图 巧用Git Staging视图 放弃Merging 可能的Merge结果 引言 Git鼓励开发者使用分支来进行程序的开发。但是最终只会有一个版本发行出去,因此,我们需要将开发好的分支merg…

公钥,私钥和数字签名这样理解轻松入门!

公钥,私钥和数字签名这样理解轻松入门!参考博文:https://blog.csdn.net/21aspnet/article/details/7249401 (公钥和私钥是成对出现的,可以把他们看成锁头和钥匙的关系,公钥为锁头,私钥是钥匙&am…

Spring Boot + JSP

目录 引言 Maven依赖 JSP页面 application.properties配置 controller 结果展示 引言 在尝试使用shiro的时候需要页面登录的加持,但是长期的前后端分离工作,导致页面的知识几乎忘光。突然想通过jsp这种简单的形式来学习其他的技术,作为…

https和http的区别

https和http的区别一、基本概念(http服务器-->本地浏览器,正确快速传输;https安全套接字层,http的安全版本, httpssl层,建立一个信息安全的通道,保证数据传输的安全,确认网站的…

Markdown简明使用

#Markdown使用技巧 ##代码背景 可以使用反引号,来将特殊文字括起来,这样Markdown会自动为引号中的内容加入背景。 形如:内容 例如:Java project > team > share project ##图片上传 图片无法像正常的文本编辑器那样直接粘贴…

Spring Boot + Mybatis 快速整合

引言 最近在工作结束后抽时间学习了一下mybatis的知识,因为之前有学习过,但是经久不用,也未曾踏实地整理,因此有所淡忘。 super meeting会议管理系统是我厂最近开发的一套会议预约平台。持久层框架经讨论,选为灵活优…

SQL关联查询————LEFT JOIN关键字的使用

引言 关联查询一直是非常重要的SQL使用技巧。 在一次查询操作中&#xff0c;使用mybatis进行条件查询&#xff0c;在没有使用 LEFT JOIN 关键字的情况下是这样写的&#xff1a; <!-- 查找成员 --><select id"selectUsers" resultMap"selectUsers_Res…

Java知识点全面汇总

&#xfeff;&#xfeff; 相关概念 面向对象的三个特征 封装&#xff0c;继承&#xff0c;多态&#xff0c;这个应该是人人皆知&#xff0c;有时候也会加上抽象。 多态的好处 允许不同类对象对同一消息做出响应&#xff0c;即同一消息可以根据发送对象的不同而采用多种不…

Git初学札记(八)————版本回退

引言 不论是使用svn还是git&#xff0c;版本回退都是一个非常重要的功能。 EGit版本回退 在History视图中&#xff0c;我们可以看到我们的commit历史&#xff0c;选中任意一个commit版本&#xff0c;右键reset —>Hard &#xff0c;即可回退到指定版本。 注意&#xff0c…

Java实现用户头像上传(修改默认文件大小限制)

概述 每次说起文件上传&#xff0c;就不得不提一下前端的实现方式&#xff0c;说来也奇怪&#xff0c;本博主最热门的博客居然也是文件上传&#xff0c;3万多的访问量占了总访问量的一多半&#xff1a;《传统form表单提交方式的文件上传与文件存储》&#xff0c;而且&#xff…

垃圾回收机制和JVM垃圾回收常见算法

垃圾回收机制和JVM垃圾回收常见算法垃圾回收的好处和特点&#xff1a; 好处&#xff1a; 1. 提高编程效率&#xff1b; 2. 垃圾回收机制保护程序的完整性。特点&#xff1a; 1. 只能回收无用对象的内存空间&#xff0c;对其他物理资源无能为力&#xff1b; 2. 为了更快回收不再…

Java 多线程 —— 死锁与锁的错误用法

引言 死锁状态的大致情况是&#xff1a;Thread_1在获得A对象的锁后&#xff0c;紧接着去请求B对象的锁 &#xff0c;Thread_2在获得了B对象的锁后&#xff0c;紧接着又去请求A对象的锁&#xff0c;如下图&#xff1a; 一、模拟一个死锁 public class DeadLockDemo {static cl…

Java提供了哪些IO方式? NIO如何实现多路复用?

Java提供了哪些IO方式&#xff1f; NIO如何实现多路复用&#xff1f;Java IO 方式有很多种&#xff0c;基于不同的 IO 抽象模型和交互方式&#xff0c;可以进行简单区分。 首先&#xff0c;传统的 java.io 包&#xff0c;它基于流模型实现&#xff0c;提供了我们最熟知的一些 I…

Multiple markers at this line

在类的package 关键字上莫名其妙的多了红色波浪线&#xff0c;代码也没有任何问题。 解决方法&#xff1a; 打开项目build path&#xff0c;删除jre system library&#xff0c;然后应用&#xff0c;再次添加一遍jdk1.8&#xff0c;选择工作区默认即可&#xff0c;应用&#…

Java零基础并发编程入门

Java零基础并发编程入门并发编程主要包括&#xff1a; 线程&#xff0c;同步&#xff0c;future&#xff0c;锁&#xff0c;fork/join&#xff0c; volatile&#xff0c;信号量&#xff0c;cas&#xff08;原子性&#xff0c;可见性&#xff0c;顺序一致性&#xff09;&#xf…

Java内置锁——synchronized

一、给对象加把锁 synchronized关键字是Java唯一内置的互斥锁&#xff0c;通过关键字 synchronized 可以保证同一时刻只有一个线程获得某个同步代码块的执行权&#xff0c;但不会导致其他线程执行非同步方法时阻塞。 当获得锁的线程执行完同步代码块后&#xff0c;线程会将锁…

处理对象(toString()方法详解和==与equals方法的区别)

处理对象&#xff08;toString&#xff08;&#xff09;方法详解和与equals方法的区别&#xff09;toString&#xff08;&#xff09;是一个非常特殊的方法&#xff0c;它是一个自我描述的方法。当程序员直接打印该对象的时候&#xff0c;系统会输出该对象的“自我描述”的信息…