java动态代理详解

简介: java动态代理详解摘要本文动态代理得意义、主要介绍动态代理得实现原理以及由动态代理引申出来的一些知识点。插曲最近在研究javamelody实现的原理,发现他对JDBC的监控就是通过动态代理实现的。

java动态代理详解
摘要
本文动态代理得意义、主要介绍动态代理得实现原理以及由动态代理引申出来的一些知识点。

插曲
最近在研究javamelody实现的原理,发现他对JDBC的监控就是通过动态代理实现的。由于之前对于动态代理只是大概知道怎么回事,没有细致的去研究,所以上网百度了一下。发现网上的东西要么注重原理而忽略应用场景导致空泛、要么注重场景而忽略原理、要么就是只有基于接口的动态代理而没有基于cglib的。因此这里本文尽量做到大而全。其实想总结一下的原因是公司进行代码review的时候,老大提出同一个类中一个方法调用本类其他方法,其他方法的事务不会生效,本质上我是持怀疑态度的。当时我是出于基于Cglib代理的角度考虑,而实际不会生效是基于动态代理的方式,采用cglib还是会生效,后面会讲到。本人作文比较推崇简约易懂的方式,尽量避免过于斯文的名词出现。

一、动态代理的意义
首先明白一点,动态代理就是用来生成代理对象的。我们知道传统的代理模式,通常是先定义一个代理类,该代理类需要持有目标对象(也有叫被代理对象,我觉得都行吧)。假设我们有1000个不同的目标对象(这1000个对象不是同一个类),那么我们需要预先定义1000个代理类,这是我们不能容忍的。于是乎,动态代理就出现了,它本质上是生成一个外表上和目标对象一样的代理对象,然后当我们调用代理对象的方法的时候,实际上它在他的方法里面去调用了目标对象对应的同名方法。

二、动态代理设计的核心思想
其实不要把这些设计想得多么高尚,假如我是动态代理设计的作者,由动态代理的意义部分我们知道,我们就是要想尽一切办法,通过目标对象生成代理对象,然后让代理对象的方法调用作用到目标对象的方法调用。没错动态代理的核心思想就是这么简单。比如目标类为Person,Person有一个方法叫做purchase(),此方法用于购物。我们期望purchase()方法有代理类去做处理,比如在购物前记录下购买了哪些东西。我们知道在使用一个类之前,是需要创建一个对象的,我们就在创建的地方动手脚。所以你看到了JDK动态Proxy.newInstance()的方式,也领略过Spring的Enhancer.create()。个人比较喜欢cglib的优雅、干净、利落。吐槽一下JDK的InvocationHandler像极了恶心的中间商。下面是JDK动态代理UML示意图

三、JDK动态代理
1,原理

在了解动态代理之前,我们需要了解Java字节码。如果不熟悉Java字节码,你可以理解为通过代码动态生成一个.java文件,然后将其编译为class文件加载到内存中。接下来JDK中的动态代理要做的事情就是怎么去生成一个ProxyPerson字节码文件。其实它就是在生成字节码的时候,持有了InvocationHandler对象,然后去实现了ProxyPerson对应的接口。在该接口的所有实现方法中,只做了一件事情就是调用invocationHandler.invoke()方法。从代码层面来看如下所示:

复制代码
public class ProxyPerson implements Purchase{

static{
Method method;// 接口的方法
Object[] args;// 接口参数
}

InvocationHandler handler;
public ProxyPerson(InvocationHandler handler){

this.handler = handler;

}

@overrde
public purchage(){
this,handler.invoke(this,method,args);
}

}
复制代码

那么上面这段代码是在什么时候生成的呢?

Proxy.newProxyInstance()
在我们调用JDK上面的这个方法的时候,底层就会去生成一个ProxyPerson字节码。知道了原理我们来解答一下JDK动态代理为何只能基于接口代理而不能基于类呢?

1),受限于字节码的生成方式,JDK本身就是基于InvocationHandler去做的代理中转。我们看到代理对象的方法调用于目标对象的调用没有半毛球关系,调用目标对象是我们自己在invoke方法里面完成的。

2),受限于同名的方法只能被向上转型成功的对象调用。比如有两个类Boy与Girl,他们都实现了接口Purchase,如果我们先获取到Girl的purchase()方法method,我们通过method.invoke(new Boy())这样必定会报错。但是如果我们获取到Purchase接口purchase()方法method,我们通过method.invoke(new Boy())这样是ok的,因为new Boy()可以向上转型为Purchase。

2,应用

比如无论是传统的MVC模型还是DDD模型,都离不开Service。我们知道Service方法使用@Transactional是可以开启事务控制的。那么这种注解式事务是如何实现的呢? 其实在工程启动的时候,我们就会有一个Bean的后置处理器去检查所有Bean一旦发现Bean的方法上有事务注解,他就通过Proxy.newInstance()去创建一个代理对象,将代理对象进行返回注入,而抛弃原本应该注入到容器的对象。所以我们看起来通过容器拿到的Service其实已经是代理对象了。在调用目标对象前,开启编程式事务即可。

四、cglib动态代理
有了上面的知识,我们要有对于cglib而言只是在生成字节码上面动手脚的觉悟。下面直观感受与一下生成过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public static void main(final String[] args) {

   Enhancer enhancer = new Enhancer();
   enhancer.setSuperclass(Boy.class);
   enhancer.setCallback(new MethodInterceptor(){

       @Override
       public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
           System.out.println("proxy method "+ method.getName());
           if(method.getAnnotation(Transactional.class)!=null){
               System.out.println(method.getName()+"发现注解");
           }
           return methodProxy.invokeSuper(o,args);
       }
   });
   Boy proxy = (Boy) enhancer.create();
   proxy.test();

}

public static class Boy{

   public void run(){
       System.out.println("run...");
   }
   @Transactional
   public void walk(){
       System.out.println("walk...");
   }
   @Transactional
   public void test(){
       System.out.println("test...");
       walk();
   }

}
可以看到cglib是基于继承的方式进行字节码动态生成。它在子类的实现中,只是调用了注入的methodIntercptor.interceptor()方法。具体字节码实现细节,这里不在深究。我们在这里探讨一下,为什么cglib可以使同一个service方法中的其他带有事务注解的事务生效?因为基于继承的动态代理,本质发起上调用的代理对象可以向上转型为原本的目标对象,所以它可以直接通过代理对象去调目标对象方法。
原文地址https://www.cnblogs.com/enjoyall/p/11324671.html

相关文章
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
25 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
19 1
|
2月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
2月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
5月前
|
缓存 Java 测试技术
day27:Java零基础 - 动态代理
【7月更文挑战第27天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
43 2
day27:Java零基础 - 动态代理
|
4月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
236 0
|
4月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
190 0
|
5月前
|
开发框架 Java Android开发
Java中的类反射与动态代理详解
Java中的类反射与动态代理详解
|
5月前
|
Java 数据安全/隐私保护
Java中的动态代理机制详解
Java中的动态代理机制详解
|
5月前
|
Java
Java中的反射与动态代理机制详解
Java中的反射与动态代理机制详解
下一篇
无影云桌面