Java动态代理分析及简单实例
发布时间 - 2026-01-10 22:51:46 点击率:次Java动态代理

要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式。
在我看来所谓的代理模式,和23种设计模式中的“装饰模式”是一个东西。23种设计模式中将它们作为两种模式,网上也有些文章讲这两种模式的异同,从细节来看,确实可以人为地区分这两种模式,但是抽象到一定高度后,我认为这两种模式是完全一样的。因此学会了代理模式,也就同时掌握了装饰模式。
代理模式
代理模式简单来说,就是对一个对象进行包装,包装后生成的对象具有和原对象一样的方法列表,但是每个方法都可以是被包装过的。
静态代理
让我们先来看一段代码:
package common;
public class Test {
static interface Subject{
void sayHi();
void sayHello();
}
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class SubjectImplProxy implements Subject{
private Subject target;
public SubjectImplProxy(Subject target) {
this.target=target;
}
@Override
public void sayHi() {
System.out.print("say:");
target.sayHi();
}
@Override
public void sayHello() {
System.out.print("say:");
target.sayHello();
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=new SubjectImplProxy(subject);
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}
这段代码中首先定义了一个Subject接口,接口中有两个方法。
然后定义了SubjectImpl类实现Subject接口并实现其中的两个方法,到这里肯定是没问题的。
现在再定义一个SubjuectImplProxy类,也实现Subject接口。这个SubjectImplProxy类的作用是包装SubjectImpl类的实例,它的内部定义一个变量target来保存一个SubjectImpl的实例。SubjectImplProxy也实现了接口规定的两个方法,并且在它的实现版本中,都调用了SubjectImpl的实现,但是又添加了自己的处理逻辑。
相信这段代码不难理解,它通过对SubjectImpl进行包装,达到了给输出内容添加前缀的功能。这种代理方式叫做静态代理。
动态代理
从上面的演示中我们不难看出静态代理的缺点:我们对SubjectImpl的两个方法,是进行的相同的包装,但是却要在SubjectImplProxy里把相同的包装逻辑写两次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必须要添加新的实现,尽管SubjectImplProxy对所有方法的包装可能都是一样的。
下面我把上面例子的静态代理改成动态代理,我们来看一下区别:
package common;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
static interface Subject{
void sayHi();
void sayHello();
}
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class ProxyInvocationHandler implements InvocationHandler{
private Subject target;
public ProxyInvocationHandler(Subject target) {
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("say:");
return method.invoke(target, args);
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}
只看main方法的话,只有第二行和之前的静态代理不同,同样是生成一个subjectProxy代理对象,只是生成的代码不同了。静态代理是直接new 一个SubjectImplProxy的实例,而动态代理则调用了java.lang.reflect.Proxy.newProxyInstance()方法,我们来看一下这个方法的源码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass(loader, interfaces); //获取代理类的Class
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams); //constructorParams是写死的:{ InvocationHandler.class },上边返回的代理类Class一定是extends Proxy的,而Proxy有一个参数为InvocationHandler的构造函数
return cons.newInstance(new Object[] { h }); //这里通过构造函数将我们自己定义的InvocationHandler的子类传到代理类的实例里,当我们调用代理类的任何方法时,实际上都会调用我们定义的InvocationHandler子类重写的invoke()函数
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
上面的 Class<?> cl = getProxyClass(loader, interfaces); 调用的getProxyClass方法:
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 65535) { //因为在class文件中,一个类保存的接口数量是用2个字节来表示的,因此java中一个类最多可以实现65535个接口
throw new IllegalArgumentException("interface limit exceeded");
}
Class<?> proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
// for detecting duplicates
Set<Class<?>> interfaceSet = new HashSet<>();
//验证interfaces里的接口是否能被类加载器加载,是否是接口,是否有重复的
for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
/*
* Using string representations of the proxy interfaces as
* keys in the proxy class cache (instead of their Class
* objects) is sufficient because we require the proxy
* interfaces to be resolvable by name through the supplied
* class loader, and it has the advantage that using a string
* representation of a class makes for an implicit weak
* reference to the class.
*/
List<String> key = Arrays.asList(interfaceNames); //使用interfaces列表作为key缓存在cache里,也就是实现了相同interfaces的代理类只会创建加载一次
/*
* Find or create the proxy class cache for the class loader.
*/
Map<List<String>, Object> cache;
synchronized (loaderToCache) {
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
/*
* This mapping will remain valid for the duration of this
* method, without further synchronization, because the mapping
* will only be removed if the class loader becomes unreachable.
*/
}
/*
* Look up the list of interfaces in the proxy class cache using
* the key. This lookup will result in one of three possible
* kinds of values:
* null, if there is currently no proxy class for the list of
* interfaces in the class loader,
* the pendingGenerationMarker object, if a proxy class for the
* list of interfaces is currently being generated,
* or a weak reference to a Class object, if a proxy class for
* the list of interfaces has already been generated.
*/
//看看缓存里有没有,如果有就直接取出来然后return,否则判断根据pendingGenerationMarker判断是否有其它线程正在生成当前的代理类,如果有则cache.wait()等待,如果没有则创建。
synchronized (cache) {
/*
* Note that we need not worry about reaping the cache for
* entries with cleared weak references because if a proxy class
* has been garbage collected, its class loader will have been
* garbage collected as well, so the entire cache will be reaped
* from the loaderToCache map.
*/
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
/*
* The class generation that we are waiting for should
* take a small, bounded time, so we can safely ignore
* thread interrupts here.
*/
}
continue;
} else {
/*
* No proxy class for this list of interfaces has been
* generated or is being generated, so we will go and
* generate it now. Mark it as pending generation.
*/
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
//确认要生成的代理类所属的包,如果interfaces里所有接口都是public的,代理类所属包就是默认包;如果有interface不是public,那么所有不是public的interface必须在一个包里否则报错。
try {
String proxyPkg = null; // package to define proxy class in
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) { // if no non-public proxy interfaces,
proxyPkg = ""; // use the unnamed package
}
{
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成代理类的名字,proxyPkg是上面确定下来的代理类所在的包名,proxyClassNamePrefix是写死的字符串“$Proxy”,num是一个全局唯一的long型数字,从0开始累积,每次生成新的代理类就+1,从这里也能看出生成的动态代理类的数量不能超过Long.maxValue
/*
* Verify that the class loader hasn't already
* defined a class with the chosen name.
*/
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces); //生成一个以proxyName为类名的,实现了Interfaces里所有接口的类的字节码
try {
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length); //加载生成的类
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);
} finally {
/*
* We must clean up the "pending generation" state of the proxy
* class cache entry somehow. If a proxy class was successfully
* generated, store it in the cache (with a weak reference);
* otherwise, remove the reserved entry. In all cases, notify
* all waiters on reserved entries in this cache.
*/
//创建成功,则将cache中该key的pendingGenerationMarker替换为实际的代理类的弱引用,否则也要清除pendingGenerationMarker标记;不管是否成功,都要执行cache.notifyAll(),让其它要创建相同代理类并且执行了cache.wait()的线程恢复执行。
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference<Class<?>>(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass; //最后返回代理类Class
}
到这里,我们已经把动态代理的java源代码都解析完了,现在思路就很清晰了:
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法简单来说执行了以下操作:
1.生成一个实现了参数interfaces里所有接口且继承了Proxy的代理类的字节码,然后用参数里的classLoader加载这个代理类。
2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,将我们自定义的InvocationHandler的子类传入。
3.返回这个代理类实例,因为我们构造的代理类实现了interfaces(也就是我们程序中传入的subject.getClass().getInterfaces())里的所有接口,因此返回的代理类可以强转成Subject类型来调用接口中定义的方法。
现在我们知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功强转成Subject类型来调用接口中定义的方法了,那么在调用方法后,代理类实例怎么进行处理的呢,这就需要看一下代理类的源码了。但是代理类是程序动态生成字节码加载的,怎么看源码呢?没关系,可以在main方法中加入System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),这样就会把生成的代理类Class文件保存在本地磁盘上,然后再反编译可以得到代理类的源码:
package common;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements Test.Subject
{
private static Method m4;
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
static
{
try {
m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("Test$Subject").getMethod("sayHi", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final void sayHello()
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sayHi()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
}
我们可以看到代理类内部实现比较简单,在调用每个代理类每个方法的时候,都用反射去调h的invoke方法(也就是我们自定义的InvocationHandler的子类中重写的invoke方法),用参数传递了代理类实例、接口方法、调用参数列表,这样我们在重写的invoke方法中就可以实现对所有方法的统一包装了。
总结:
动态代理相对于静态代理在使用上的优点主要是能够对一个对象的所有方法进行统一包装,而且后期被代理的类添加方法的时候动态代理类不需要改动。
缺点是要求被代理的类必须实现了接口,因为动态代理类在实现的时候继承了Proxy类,java不支持多继承,因此动态代理类只能根据接口来定义方法。
最后动态代理之所以叫做动态代理是因为java在实现动态代理的时候,动态代理类是在运行时动态生成和加载的,相对的,静态代理类和其他普通类一下,在类加载阶段就加载了。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
# Java动态代理
# Java动态代理分析
# Java动态代理详解及实例代码
# java使用动态代理来实现AOP(日志记录)的实例代码
# Java JDK动态代理(AOP)的实现原理与使用详析
# Java动态代理实现AOP
# 十分钟理解Java中的动态代理
# Java中反射动态代理接口的详解及实例
# java实现动态代理方法浅析
# Java动态代理的应用详解
# 深入理解java动态代理的两种实现方式(JDK/Cglib)
# Java实现AOP面向切面编程的实例教程
# 体验Java 1.5中面向(AOP)编程
# Java动态代理和AOP应用示例
# 加载
# 实现了
# 子类
# 重写
# 是一个
# 这两种
# 这段
# 自定义
# 可以实现
# 转成
# 自己的
# 都是
# 是在
# 是因为
# 都要
# 让我们
# 也就
# 最多
# 也要
# 不需要
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验
非常酷的网站设计制作软件,酷培ai教育官方网站?
如何快速搭建高效简练网站?
nginx修改上传文件大小限制的方法
Laravel如何使用Contracts(契约)进行编程_Laravel契约接口与依赖反转
如何为不同团队 ID 动态生成多个独立按钮
php json中文编码为null的解决办法
Laravel如何生成API文档?(Swagger/OpenAPI教程)
Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践
java获取注册ip实例
公司网站制作需要多少钱,找人做公司网站需要多少钱?
Laravel如何使用Gate和Policy进行授权?(权限控制)
Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南
如何在宝塔面板中创建新站点?
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】
jquery插件bootstrapValidator表单验证详解
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
laravel怎么使用数据库工厂(Factory)生成带有关联模型的数据_laravel Factory生成关联数据方法
Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层
Laravel如何实现API速率限制?(Rate Limiting教程)
教你用AI将一段旋律扩展成一首完整的曲子
如何挑选优质建站一级代理提升网站排名?
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】
微信小程序 wx.uploadFile无法上传解决办法
西安专业网站制作公司有哪些,陕西省建行官方网站?
香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化
Laravel如何实现URL美化Slug功能_Laravel使用eloquent-sluggable生成别名【方法】
微信公众帐号开发教程之图文消息全攻略
Laravel Blade组件怎么用_Laravel可复用视图组件的创建与使用
Mybatis 中的insertOrUpdate操作
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
微信小程序 闭包写法详细介绍
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
JavaScript中的标签模板是什么_它如何扩展字符串功能
浅谈javascript alert和confirm的美化
Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
PHP 实现电台节目表的智能时间匹配与今日/明日轮播逻辑
详解jQuery中基本的动画方法
Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】
HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】
Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives
如何在Tomcat中配置并部署网站项目?
如何用已有域名快速搭建网站?
微信推文制作网站有哪些,怎么做微信推文,急?
Laravel如何实现全文搜索功能?(Scout和Algolia示例)

