反射

反射赋予了我们在运行时分析类和执行类中方法的能力。

通过反射,可以获取任意一个类的所有属性和方法,还可以调用这些方法和属性。

使用反射生成 JDK 动态代理

JDK 动态代理只能为接口创建动态代理。

在 java.lang.reflect 包下提 供 了 一 个 Proxy 类 和 一 个 InvocationHandler 接口,通过使用这个类和接口可以生成 JDK 动态代理类或动态代理对象。

创建动态代理

Proxy 提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父 类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用 Proxy 来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用 Proxy 来创建动态代理实例。

Proxy 提供了如下方法来创建动态代理对象:

  • static Object newProxyInstance(ClassLoader loader, Class< ? > [] interfaces, InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了 interfaces 指定的系列接口,执行代理对象的每个方法时都会被替换执行 InvocationHandler 对象的 invoke 方法

每个代理对象都有一个与之关联的 InvocationHandler 对象。

例子:

package proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface Person{void walk();void sayHello(String name);
}class MyInvokationHandler implements InvocationHandler{/*** @param proxy 代理对象* @param method 正在执行的方法* @param args 调用目标方法时传入的实参*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("-----正在执行的方法:" + method);if (args != null) {System.out.println("下面是执行该方法时传入的实参:");for (var val : args) {System.out.println(val);}} else {System.out.println("该方法没有实参");}return null;}
}public class ProxyTest {public static void main(String[] args) {InvocationHandler handler = new MyInvokationHandler();Class[] interfaces = {Person.class};//创建动态代理对象var p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), interfaces, handler);p.walk();p.sayHello("代理");}
}
-----正在执行的方法:public abstract void proxy.Person.walk()
该方法没有实参
-----正在执行的方法:public abstract void proxy.Person.sayHello(java.lang.String)
下面是执行该方法时传入的实参:
代理

动态代理和 AOP

背景

原始:开发实际应用的软件系统时,通常会存在相同代码段重复出现的情况,在这种情况下,对于许多刚开始从事软件开发的人而言,他们的做法是:选中那些代码,一路“复制”“粘贴”,立即实现了系统功能,如果仅仅从软件功能上来看,他们确实已经完成了软件开发。通过这种“复制”“粘贴”方式开发出来的软件如图18.4所示。

缺点:采用图18.4所示结构实现的软件系统,在软件开发期间可能会觉得无所谓,但如果有一天需要修改程序的深色代码的实现,则意味着打开三份源代码进行修改。如果有100个地方甚至1000个地方使用了这段深色代码段,那么修改、维护这段代码的工作量将变成噩梦。
改进1:在这种情况下,大部分稍有经验的开发者都会将这段深色代码段定义成一个方法,然后让另外三段代码段直接调用该方法即可。在这种方式下,软件系统的结构如图18.5所示。

对于如图18.5所示的软件系统,如果需要修改深色部分的代码,则只要修改一个地方即可,而调用该方法的代码段,不管有多少个地方调用了该方法,都完全无须任何修改,只要被调用方法被修改了,所有调用该方法的地方就会自然改变——通过这种方式,大大降低了软件后期维护的复杂度。

缺点:但采用这种方式来实现代码复用依然产生一个重要问题:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、代码段2和代码段3又和一个特定方法(调用深色代码段的方法)耦合了!

应用动态代理:最理想的效果是:代码段1、代码段2和代码段3既可以执行深色代码部分,又无须在程序中以硬编码方式直接调用深色代码的方法,这时就可以通过动态代理来达到这种效果。

例子:

  1. 首先定义一个接口。

    public interface Dog{void info();void run();
    }
    

    如果直接使用 Proxy 为该接口创建动态代理对象,则动态代理对象的所有方法的执行效果将完全一样。

    实际情况通常是,软件系统会为该接口提供一个或多个实现类。

  2. 定义一个简单的实现类。

    public class GunDog implements Dog{@Overridepublic void info() {System.out.println("我是一直猎狗");}@Overridepublic void run() {System.out.println("我奔跑迅速");}
    }
    

    假设 info()、run() 两个方法代表代码段1、代码段2,那么要求:程序执行 info()、run() 方法时能调用某个通用方法,但又不想以硬编码方式调用该方法。

  3. 定义一个 DogUtil 类,该类里包含两个通用方法。

    public class DogUtil {//第一个拦截器方法public void method1() {System.out.println("=====模拟第一个通用方法=====");}//第二个拦截器方法public void method2() {System.out.println("=====模拟第二个通用方法=====");}
    }
    

    借助于 Proxy 和 InvocationHandler 就可以实现:当程序调用 info()、run() 方法时,系统可以自动将 method1() 和 method2() 两个通用方法插入 info()、run()方法中执行。

  4. InvocationHandler 实现类

    class MyInvocationHandler implements InvocationHandler {//需要被代理的对象private Object target;public void setTarget(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {var du = new DogUtil();du.method1();//通过反射以target作为主调来执行method方法Object result = method.invoke(target, args);du.method2();return result;}
    }
    
  5. 定义一个动态代理工厂类,为指定的 targe t生成动态代理对象,这个动态代理对象与 target 实现了相同的接口。

    public class MyProxyFactory {public static Object getProxy(Object target) {MyInvocationHandler handler = new MyInvocationHandler();handler.setTarget(target);return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);}
    }
    
  6. 测试

    public class ProxyTest2 {public static void main(String[] args) {//创建一个GunDog对象,作为被代理对象Dog target = new GunDog();var dog = (Dog)MyProxyFactory.getProxy(target);dog.info();dog.run();}
    }
    
    =====模拟第一个通用方法=====
    我是一直猎狗
    =====模拟第二个通用方法=====
    =====模拟第一个通用方法=====
    我奔跑迅速
    =====模拟第二个通用方法=====
    

    当使用动态代理对象来代替被代理对象时,代理对象的方法就实现了前面的要求:程序执行info()、run() 方法时既能插入 method1()、method2() 通用方法,但 GunDog 类中的方法又没有以硬编码的方式调用 method1()、method2() 通用方法。

AOP

这种动态代理在 AOP(Aspect Orient Programming,面向切面编程)中被称为 AOP 代理,AOP 代理可代替目标对象,AOP 代理包含了目标对象的全部方法。但 AOP 代理中的方法与目标对象的方法存在差异:AOP 代理里的方法可以在执行目标方法之前、之后插入一些通用处理。

ref:《疯狂Java讲义(第五版)》

使用反射生成 JDK 动态代理相关推荐

  1. java基础5:工厂模式、单例模式、File文件类、递归、IO流、Properties配置文件、网络编程、利用IO流模拟注册登录功能、关于反射、JDK动态代理

    1.工厂模式 23种java设计模式之一 1)提供抽象类(基类) 2)提供一些子类,完成方法重写 3)提供一个接口:完成具体子类的实例化对象的创建,不能直接new子类,构造函数私有化. 优点:具体的子 ...

  2. Java设计模式(五)代理设计模式—静态代理—JDK动态代理—Cglib动态代理

    文章目录 什么是代理模式 代理模式应用场景 代理的分类 静态代理 什么是静态代理 深入解析静态代理 小结 动态代理 什么是动态代理 JDK动态代理 原理和实现方式 代码实现 优缺点 Cglib动态代理 ...

  3. Cglib和jdk动态代理

    前言:动态代理解决了方法之间的紧耦合,IOC解决了类与类之间的紧耦合. Cglib和jdk动态代理的区别? 1.Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成 ...

  4. JDK动态代理的底层实现原理

    JavaEE的开发中,许多框架用到了动态代理机制,例如Spring的AOP编程. 这里不介绍动态代理和静态代理概念,有兴趣的朋友自行百度. Java中的动态代理一般就两种:1. JDK自带 : 2. ...

  5. Cglib和jdk动态代理的区别

    Cglib和jdk动态代理的区别 动态代理解决了方法之间的紧耦合, IOC解决了类与类之间的紧耦合! Cglib和jdk动态代理的区别? 1.Jdk动态代理:利用拦截器(必须实现InvocationH ...

  6. Arthas查看JDK动态代理生成的class文件

    基本代码准备 假设有一场演唱会,由司仪进行串场处理 核心类 对唱歌做代理-增强类 详细代码如下 public interface SingAble {void singASong(String son ...

  7. 深入理解JDK动态代理

    点击关注公众号,Java干货及时送达 代理模式的目的是在不修改原有类方法设计的基础上,对方法行为进行增强. 为了好理解,举个实际场景,我们业务场景中经常有限流的需求,常规操作是在需要限流的接口代码前加 ...

  8. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

  9. java jdk动态代理学习记录

    转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...

最新文章

  1. 浅谈C/C++中的typedef和#define
  2. 学习Java,容易被你忽略的小细节(2)
  3. python爬虫学习之页面登陆
  4. win7 linux双系统win7启动不了怎么办,双系统windows打不开怎么办|苹果双系统win7打不开怎么解决|mac双系统打不开解决方法-系统城...
  5. 常用的分隔符有哪三种_加固博士:常用防水材料大比拼,究竟花落谁家?
  6. python列表字典_Python常用对字典、列表的操作
  7. 软件工程知识——软件配置管理
  8. 机器学习的挑战:黑盒模型正面临这3个问题
  9. Linux查看:crontab开启状态
  10. 在 Redis 上实现的分布式锁
  11. 细数人们对安卓的误解
  12. rx文件管理器window_像浏览器一样管理你的文件!
  13. SQL Server索引简介:SQL Server索引级别1
  14. VMware安装Ubuntu配置NAT模式下静态IP,解决访问外网问题
  15. 飞翔到你希望的遥远的地方
  16. python使用循环打印九九乘法表python中的99乘法表
  17. 计算机专业的研究方向
  18. 数学建模案例--基于微分方程的酒后驾车问题浅析
  19. 簇(cluster)
  20. ETIMEDOU 104.16.20.35:443(已解决)

热门文章

  1. python如何打包成egg和安装egg(使用开源库openunreid-maser时要用此工具会生成egg、build文件夹)
  2. PWM方波的理解浅谈
  3. linux代码之spin lock
  4. 零基础入门金融风控-贷款违约预测-机器学习-数据分析
  5. Win10电脑怎么恢复出厂设置
  6. iCollections for Mac(桌面文件整理软件)
  7. ESPG和OGC、SRS、SRID指的是什么
  8. 键盘鼠标是计算机标准输入输出设备,微型计算机输入输出设备之键盘和鼠标(ppt 32页).ppt...
  9. 血泪总结:如何从微信小程序的坑跳进支付宝小程序的大坑
  10. spring boot自动化配置