评论

收藏

[Java] 详解JAVA动态代理

编程语言 编程语言 发布于:2021-09-18 10:05 | 阅读数:195 | 评论:0

这篇文章主要介绍了JAVA动态代理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
文档更新说明
2018年09月24日 v1.0 初稿
代理在生活中很常见,比如说婚介网站,其实就是找对象的代理;还有社保代理、人事代理;还有找黄牛抢票,其实也是一种代理;而这些代理,在java中也是有对应实现的。
1、为什么要动态代理
动态代理的作用其实就是在不修改原代码的前提下,对已有的方法进行增强。
关键点:
不修改原来已有的代码(满足设计模式的要求)
对已有方法进行增强
2、举个栗子
我们用一个很简单的例子来说明:hello类,有一个introduction方法。
现在我们的需求就是不修改hello类的introduction方法,在introduction之前先sayhello,在introduction之后再saygoodbye
3、实现方式
java中,实现动态代理有两种方式,一种是jdk提供的,一种是第三方库cglib提供的。特点如下:
jdk动态代理:被代理的目标类需要实现接口
cglib方式:可以对任意类实现动态代理
3.1、jdk动态代理
jdk动态代理需要实现接口,然后通过对接口方法的增强来实现动态代理
所以要使用jdk动态代理的话,我们首先要创建一个接口,并且被代理的方法要在这个接口里面
3.1.1、创建一个接口
我们创建一个接口如下:
personal.java
public interface personal {
 
  /**
   * 被代理的方法
   */
  void introduction();
 
}
3.1.2、实现接口
创建接口实现类,并且完成introduction方法
personalimpl.java
public class personalimpl implements personal {
  @override
  public void introduction() {
  system.out.println("我是程序员!");
  }
}
3.1.3、创建代理类
jdk代理的关键就是这个代理类了,需要实现invocationhandler
在代理类中,所有方法的调用都好分发到invoke方法中。我们在invoke方法完成对方法的增强即可
jdkproxyfactory.java
import java.lang.reflect.invocationhandler;
import java.lang.reflect.method;
import java.lang.reflect.proxy;
 
public class jdkproxyfactory<t> implements invocationhandler {
 
  /**
   * 目标对象
   */
  private t target;
 
  /**
   * 构造函数传入目标对象
   *
   * @param target 目标对象
   */
  public jdkproxyfactory(t target) {
  this.target = target;
  }
 
  /**
   * 获取代理对象
   *
   * @return 获取代理
   */
  public t getproxy() {
  return (t) proxy.newproxyinstance(target.getclass().getclassloader(), target.getclass().getinterfaces(), this);
  }
 
  @override
  public object invoke(object proxy, method method, object[] args) throws throwable {
  // 对方法增强
  system.out.println("大家好!");
  // 调用原方法
  object result = method.invoke(target, args);
  // 方法增强
  system.out.println("再见!");
  return result;
  }
}
就这样,jdk动态代理的代码就完成了,接下来写一份测试代码
3.1.4、编写测试代码
为了方便测试,我们编写一个test方法
同时为了查看class文件,还添加了一个generatorclass方法,这个方法可以将动态代理生成的.class输出到文件
proxytest.java
import org.junit.test;
import sun.misc.proxygenerator;
 
import java.io.fileoutputstream;
import java.io.ioexception;
 
public class proxytest {
 
  @test
  public void testjdkproxy() {
  // 生成目标对象
  personal personal = new personalimpl();
  // 获取代理对象
  jdkproxyfactory<personal> proxyfactory = new jdkproxyfactory<>(personal);
  personal proxy = proxyfactory.getproxy();
 
  // 将proxy的class字节码输出到文件
  generatorclass(proxy);
 
  // 调用代理对象
  proxy.introduction();
  }
 
  /**
   * 将对象的class字节码输出到文件
   *
   * @param proxy 代理类
   */
  private void generatorclass(object proxy) {
  fileoutputstream out = null;
  try {
    byte[] generateproxyclass = proxygenerator.generateproxyclass(proxy.getclass().getsimplename(), new class[]{proxy.getclass()});
    out = new fileoutputstream(proxy.getclass().getsimplename() + ".class");
    out.write(generateproxyclass);
  } catch (exception e) {
    e.printstacktrace();
  } finally {
    if (out != null) {
    try {
      out.close();
    } catch (ioexception e) {
      // todo auto-generated catch block
    }
    }
  }
 
  }
 
}
3.1.5、查看运行结果
可以看到,运行test方法之后,控制台打印出如下:
大家好!
我是程序员!
再见!
我们在introduction方法前和后都成功增加了功能,让这个程序员的自我介绍瞬间变得更加有礼貌了。
3.1.6、探探动态代理的秘密
动态代理的代码并不多,那么jdk底层是怎么帮我们实现的呢?
在测试的时候我们将动态生成的代理类的class字节码输出到了文件,我们可以反编译看看。
结果有点长,就不全部贴出来了,不过我们可以看到,里面有一个introduction方法如下:
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected invocationhandler h;
 
protected proxy(invocationhandler h) {
  objects.requirenonnull(h);
  this.h = h;
}
 
public final void introduction() throws {
  try {
    super.h.invoke(this, m3, (object[])null);
  } catch (runtimeexception | error var2) {
    throw var2;
  } catch (throwable var3) {
    throw new undeclaredthrowableexception(var3);
  }
  }
原来,生成的代理对象里面,引用了我们的invocationhandler,然后在将introduction方法里面调用了invocationhandler的introduction,而invocationhandler是由我们编写的代理类,在这里我们增加了sayhello和saygoodbye操作,然后还调用了原对象的introduction方法,就这样完成了动态代理。
3.2、cglib动态代理
cglib动态
3.2.1、创建被代理对象
由于cglib不需要实现接口,所以我们不需要创建接口文件了(当然,你要有接口也没有问题)
直接创建目标类,实现introduction方法
personalimpl.java
public class personalimpl {
  public void introduction() {
  system.out.println("我是程序员!");
  }
}
3.2.2、创建代理类
同样,我们也需要创建代理类,并且在这里实现增强的逻辑,这次我们不是实现invocationhandler接口了,而是实现cglib提供的接口methodinterceptor,都是类似的,methodinterceptor中,全部方法调用都会交给intercept处理,我们在intercept添加处理逻辑即可。
cglibproxyfactory.java
import net.sf.cglib.proxy.enhancer;
import net.sf.cglib.proxy.methodinterceptor;
import net.sf.cglib.proxy.methodproxy;
 
import java.lang.reflect.method;
 
public class cglibproxyfactory<t> implements methodinterceptor {
 
  /**
   * 获取代理对象
   *
   * @param tclass 被代理的目标对象
   * @return 代理对象
   */
  public t getproxybycglib(class<t> tclass) {
  // 创建增强器
  enhancer enhancer = new enhancer();
 
  // 设置需要增强的类的类对象
  enhancer.setsuperclass(tclass);
 
  // 设置回调函数
  enhancer.setcallback(this);
 
  // 获取增强之后的代理对象
  return (t) enhancer.create();
  }
 
   /**
   * 代理类方法调用回调
   * 
   * @param obj 这是代理对象,也就是[目标对象]的子类
   * @param method [目标对象]的方法
   * @param args 参数
   * @param proxy 代理对象的方法
   * @return 返回结果,返回给调用者
   * @throws throwable
   */
  @override
  public object intercept(object obj, method method, object[] args, methodproxy proxy) throws throwable {
 
  system.out.println("大家好!");
 
  object result = proxy.invokesuper(obj, args);
 
  system.out.println("再见!");
 
  return result;
  }
}
3.2.3、编写测试代码
在刚才的测试方法中,我们添加一个cglib的测试方法:
@test
public void testcglibproxy() {
  // 生成被代理的目标对象
  personalimpl personal = new personalimpl();
  
  // 获取代理类
  cglibproxyfactory<personalimpl> proxyfactory = new cglibproxyfactory<>();
  personalimpl proxy = proxyfactory.getproxybycglib((class<personalimpl>) personal.getclass());
 
  // 将proxy的class字节码输出到文件
  generatorclass(proxy);
 
  // 调用代理对象
  proxy.introduction();
}
3.2.4、查看运行结果
运行测试用例,可以看到跟jdk的实现一样的效果
大家好!
我是程序员!
再见!
3.2.5、探探动态代理的秘密
跟jdk的测试一样,我们也来看看生成的class文件
public final void introduction() throws {
  try {
  super.h.invoke(this, m7, (object[])null);
  } catch (runtimeexception | error var2) {
  throw var2;
  } catch (throwable var3) {
  throw new undeclaredthrowableexception(var3);
  }
}
可以发现,与jdk的动态代理并没有区别。
4、如何选择
既然有两种实现方式,那么到底应该怎么选择呢?
就两个原则:
目标类有接口实现的,jdk和cglib都可以选择,你开心就好
目标类没有实现任何接口,那只能用cglib了
5、后记
其实在第一次看到动态代理的时候,我就想不明白,我们都把目标类new出来了,为什么还要将目标类丢给代理类呢?为什么不直接调用目标类对应的方法呢?
后来才发现,原来我没搞清楚动态代理的使用场景,场景很清晰,就是:
不修改原来已有的代码(满足设计模式的要求)
对已有方法进行增强
关键是增强,代理类里面我们是可以添加很多处理逻辑的,从而实现增强效果。就像黄牛抢票比我们厉害些一样。
以上所述是小编给大家介绍的java动态代理详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对CodeAE代码之家网站的支持!
原文链接:https://www.fengqiangboy.com/15377761043880.html
关注下面的标签,发现更多相似文章