利用反射生成JDK动态代理

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

1.使用Proxy和InvocationHandler创建动态代理

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

Proxy提供了如下两个方法用于创建动态代理类和代理对象。

  • static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):创建一个动态代理类的所对应的Class对象,该代理类将实现interface所制定的多个接口。loader参数指定生成动态代理类的类加载器。
  • static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):直接创建一个动态代理对象。该对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。

实际上,第一种方法生成动态代理之后,如果程序需要通过该代理类来创建对象,依然需要传入一个InvocationHandler对象。也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。

创建动态代理对象时需要实现一个或多个接口定义的方法,而InvocationHandler对象的作用是—当执行动态代理对象里的方法时,实际上会替换成调用InvocationHandler对象的invoke方法。

程序中可以采用先生成一个动态代理类,然后通过动态代理类来创建代理对象的方式生成一个动态代理对象,代码如下

  1. package com.gdut.test0516;
  2.  
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. import java.lang.reflect.Proxy;
  7.  
  8. class MyInvocationHandler implements InvocationHandler{
  9.  
  10. @Override
  11. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  12. return null;
  13. }
  14. }
  15. interface Foo{
  16.  
  17. }
  18.  
  19. public class ProxyTest1 {
  20.  
  21. public static void main(String[] args) throws Exception{
  22. InvocationHandler handler = new MyInvocationHandler();
  23. Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(),new Class[]{Foo.class});
  24. //获取ProxyClass中带一个InvocationHandler参数的实例
  25. Constructor ctor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
  26. //调用ctor的newInstance方法创建实例
  27. Foo f = (Foo)ctor.newInstance(new Object[]{handler});
  28. }
  29. }

也可以简化成

  1. package com.gdut.test0516;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6.  
  7. class MyInvocationHandler implements InvocationHandler{
  8.  
  9. @Override
  10. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11. return null;
  12. }
  13. }
  14. interface Foo{
  15.  
  16. }
  17.  
  18. public class ProxyTest1 {
  19.  
  20. public static void main(String[] args) throws Exception{
  21. Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[]{Foo.class},new MyInvocationHandler());
  22. }
  23. }

下面程序示范了使用Proxy和InvocationHandler来生成动态代理对象

  1. package com.gdut.test0516;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6.  
  7. interface Person{
  8. void walk();
  9. void sayHello(String name);
  10. }
  11.  
  12. class MyInvocationHandler2 implements InvocationHandler{
  13. /**
  14. * 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
  15. * @param proxy 代表动态代理对象
  16. * @param method 代表正在执行的方法
  17. * @param args 代表调用目标方法时传入的实参
  18. * @return
  19. * @throws Throwable
  20. */
  21. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  22. System.out.println("-----正在执行的方法:"+method);
  23. if(args != null){
  24. System.out.println("下面执行该方法时传入的实参为:");
  25. for (Object val:args) {
  26. System.out.println(val);
  27. }
  28. }else{
  29. System.out.println("调用该方法没有实参!");
  30. }
  31. return null;
  32. }
  33. }
  34. public class ProxyTest2 {
  35. public static void main(String[] args) throws Exception {
  36. InvocationHandler handler = new MyInvocationHandler();
  37. Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[]{Person.class},new MyInvocationHandler2());
  38. p.walk();
  39. p.sayHello("孙悟空");
  40. }
  41. }

2. 动态代理和AOP

开发实际应用的软件系统时,通常会存在相同代码段重复出现的情况

我们大多数会将深色代码定义成一个方法,然后让另外三段代码段直接调用该方法即可。

但采用这种方式来实现代码复用仍存在一个重要问题,虽然代码段1,代码段2,代码段三和深色代码分开了,但是又和一个特定的方法耦合了!最理想的状态是代码块1,代码块2,代码块3能执行深色代码块部分,又无需以硬代码的方式直接调用深色代码的方法,这时可以通过动态代理达到这种效果。

由于JDK动态代理只能为接口创建代理,下面先提供一个Dog接口

  1. interface Dog{
  2. void info();
  3. void run();
  4. }

实际情况通常时,软件系统会为该接口提供一个或多个实现类,例如:GunDog

  1. public class GunDog implements Dog{
  2. @Override
  3. public void info() {
  4. System.out.println("我是一只猎狗");
  5. }
  6.  
  7. @Override
  8. public void run() {
  9. System.out.println("我迅速奔跑");
  10. }
  11. }

此处假设info(),run()两个方法分别代表代码段1,代码段2,那么要求:程序执行info()、run()方法时能调用某个通用方法,但又不想以硬代码方式调用该方法。下面提供一个DogUtil类,该类里包含两个通用方法。

  1. public class DogUtil {
  2. public void method1(){
  3. System.out.println("=====模拟第一个通用方法=====");
  4. }
  5. public void method2(){
  6. System.out.println("=====模拟第一个通用方法=====");
  7. }
  8. }

借助动态代理,将method1(),method2()两个通用方法分别插入info(),run()方法中执行

这个程序的关键在于下面的MyInvocationHandler3类,该类是InvocationHandler实现类,该实现类的invoke()方法将会作为代理对象的方法实现

  1. package com.gdut.test0516;
  2.  
  3. import java.lang.reflect.InvocationHandler;
  4. import java.lang.reflect.Method;
  5.  
  6. public class MyInvocationHandler3 implements InvocationHandler {
  7. private Object target;
  8. public void setTarget(Object target){
  9. this.target = target;
  10. }
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
  13. DogUtil du = new DogUtil();
  14. du.method1();
  15. Object result = method.invoke(target,args);
  16. du.method2();
  17. return result;
  18. }
  19. }

下面再提供MyProxyFactory类,该对象专为指定的target生成动态代理实例

  1. public class MyProxyFactory {
  2. public static Object getProxy(Object target)throws Exception{
  3. MyInvocationHandler3 handler3 = new MyInvocationHandler3();
  4. handler3.setTarget(target);
  5. return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler3);
  6. }
  7.  
  8. }

上面的动态代理工厂类提供了一个getProxy()方法,该方法为target对象生成一个动态代理对象,这个动态代理对象与target实现了相同接口,所以具有相同的public方法——从这个意义上看,动态代理对象可以当成target对象使用。当程序调用动态代理对象的指定方法时,实际上将变为执行MyInvocationHandler3对象的invoke方法。

下面是测试效果

利用反射生成JDK动态代理的更多相关文章

  1. JavaSE---使用反射生成JDK动态代理

    1.概述 1.1 在Java.lang.reflect包下,提供了Proxy类.InvocationHandler接口,使用它们可以生成JDK动态代理类或动态代理对象: 1.2 [Proxy类] 1. ...

  2. Java基础之反射生成JDK动态代理

    在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口.通过这个类和接口可以生成JDK动态代理类或动态代理对象. JDK动态代理例子: / ...

  3. 静态代理和利用反射形成的动态代理(JDK动态代理)

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

  4. MyBatis之反射技术+JDK动态代理+cglib代理

    一.反射 引用百度百科说明: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功 ...

  5. Java 反射之JDK动态代理

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

  6. MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)(转)

    在介绍MyBATIS插件原理前我们需要先学习一下一些基础的知识,否则我们是很难理解MyBATIS的运行原理和插件原理的. MyBATIS最主要的是反射和动态代理技术,让我们首先先熟悉它们. 1:Jav ...

  7. 【Java入门提高篇】Day11 Java代理——JDK动态代理

    今天来看看Java的另一种代理方式--JDK动态代理 我们之前所介绍的代理方式叫静态代理,也就是静态的生成代理对象,而动态代理则是在运行时创建代理对象.动态代理有更强大的拦截请求功能,因为可以获得类的 ...

  8. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer { public Object execut ...

  9. JDK动态代理[4]----ProxyGenerator生成代理类的字节码文件解析

    通过前面几篇的分析,我们知道代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成 ...

随机推荐

  1. 股票的历史市盈率查询PE

    浦发银行的历史市盈率PE查询:https://androidinvest.com/Stock/History/SH600000/ 白云机场的历史市盈率PE查询:https://androidinves ...

  2. 关于测试中哪些信息需要放到jira上面

    1.每个新需求的功能点,全部在jira上一一呈现 2.每个bug也一样在jira上一一呈现 3.任务一个需要优化改进的点也一一在jira上呈现 然后程序员一直开发新功能和修改新bug,测试人员负责bu ...

  3. apache 配置会话保持

    1.修改apache_home/conf/httpd.conf,增加以下模块(取消注释,如有其他依赖, 则相应取消注释) LoadModule proxy_module modules/mod_pro ...

  4. .NET Framework 4.0源代码

    原文出处:http://blogs.microsoft.co.il/blogs/arik/archive/2010/07/12/step-into-net-framework-4-0-source-c ...

  5. error C4996: Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct

    使用VS13 跟  google protocbuf时出现了这个问题:真蛋疼,用别人的东西你就说不安全,用你自己的东西时你怎么不说不安全来着! 解决方案 在protoc   生成的头文件中加上 #pr ...

  6. 【代码审计】DouPHP_v1.3代码执行漏洞分析

      0x00 环境准备 DouPHP官网:http://www.douco.com/ 程序源码下载:http://down.douco.com/DouPHP_1.3_Release_20171002. ...

  7. Robot Framework进行web ui自动化测试,浏览器配置说明

    转载请注明出处,谢谢: chrome浏览器: 1.从如下地址下载与本地浏览器版本号一致的chromedriver.exe驱动文件: http://chromedriver.storage.google ...

  8. js防止表单重复提交

    1.表单 <form id="addForm" onsubmit="getElementById('submitInput').disabled=true;retu ...

  9. Kafka 0.11客户端集群管理工具AdminClient

    很多用户都有直接使用程序API操作Kafka集群的需求.在0.11版本之前,kafka的服务器端代码(即添加kafka_2.**依赖)提供了AdminClient和AdminUtils可以提供部分的集 ...

  10. Suggestion: add 'tools:replace="android:value"' to <meta-data> element at AndroidManifest.xml:25:5-27:41 to override.

    记录下来少走些坑吧 一:不管用 tools:replace="android:icon,android:theme" xmlns:tools="http://schema ...