设计模式(1-2)-动态代理(newProxyInstance)
上节设计模式(1-1)-代理模式,讲了代理模式的静态代理与动态代理的写法。本节,会从Proxy.newProxyInstance() 这个方法开始讲,上一节文末的那个class文件怎么一步步的来的。
UpanSell proxy = (UpanSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler
);
上面的方法会返回一个指定接口的代理类实例
newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
...
// 验证权限
...
/*
* 这里是重中之重, 这里会生成或去缓存中拿指定的代理类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
...
// 下面的逻辑就是拿到代理类的构造器 -> 检测代理类的类修饰符是否是public, 不是就设置为可访问 -> 返回代理类的实例
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch...
....
}
我们debug到getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 根据loader、interfaces有对应缓存,就直接返回缓存的复制
// 否则,就通过ProxyClassFactory生成代理类
return proxyClassCache.get(loader, interfaces);
}
可能你会问为什么有个65535的限制,类中的注释就解释了为什么
The resulting proxy class must not exceed any limits imposed on classes by the virtual machine.
For example, the VM may limit the number of interfaces that a class may implement to 65535; in that case, the size of the interfaces array must not exceed 65535.
大致意思就是JVM限制了一个类实现的方法最多是65535(JVM这块确实头疼,还没开始去学习...)
继续debug到ProxyFactory,现在不用关心怎么从get方法到的ProxyFactory,把下一篇文章看完就明白了
ProxyClassFactory,根据指定的classLoader和interfaces生成和返回代理类的一个工厂方法
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 所有代理的前缀名, 看到这个玩意儿就证明该类是代理类
private static final String proxyClassNamePrefix = "$Proxy";
// 生成唯一的代理类数字,每个代理类有一个 eg, $Proxy0
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* 验证使用接口名得到的类对象与接口对象是否一样, 这个不太清楚为啥要这么验证。。。。
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* 验证是否是接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 验证有没有重复
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* 记录且验证非public的代理接口是在同一个包下的
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
// 最后的全限定名, eg: com.sun.proxy.$Proxy0
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 生成指定的代理类.重点
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return 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());
}
}
}
下一篇文章会讲为什么proxyClassCache.get(loader, interfaces);
不存在对应loader与interfaces的缓存时,会调用到ProxyFactory的apply()方法。
再下一篇揭露JDK动态代理是如何生成代理类的 -> ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
设计模式(1-2)-动态代理(newProxyInstance)的更多相关文章
- 设计模式之jdk动态代理模式、责任链模式-java实现
设计模式之JDK动态代理模式.责任链模式 需求场景 当我们的代码中的类随着业务量的增大而不断增大仿佛没有尽头时,我们可以考虑使用动态代理设计模式,代理类的代码量被固定下来,不会随着业务量的增大而增大. ...
- Java设计模式系列之动态代理模式(转载)
代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Sub ...
- 设计模式之Jdk动态代理
什么是动态代理呢?就是在java的运行过程中,动态的生成的代理类.(为了更熟悉的了解动态代理,你必须先熟悉代理模式,可点击设计模式之代理模式 阅读)我们知道java属于解释型语言,是在运行过程中,寻找 ...
- java设计模式中的动态代理
Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...
- Java设计模式之JDK动态代理原理
动态代理核心源码实现public Object getProxy() { //jdk 动态代理的使用方式 return Proxy.newProxyInstance( this.getClass(). ...
- 设计模式学习——JAVA动态代理原理分析
一.JDK动态代理执行过程 上一篇我们讲了JDK动态代理的简单使用,今天我们就来研究一下它的原理. 首先我们回忆下上一篇的代码: public class Main { public static v ...
- Android开发中无处不在的设计模式——动态代理模式
继续更新设计模式系列.写这个模式的主要原因是近期看到了动态代理的代码. 先来回想一下前5个模式: - Android开发中无处不在的设计模式--单例模式 - Android开发中无处不在的设计模式-- ...
- 《Java设计模式》之代理模式 -Java动态代理(InvocationHandler) -简单实现
如题 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式可细分为如下, 本文不做多余解释 远程代理 虚拟代理 缓冲代理 保护代理 借鉴文章 ht ...
- mybatis 插件的原理-责任链和动态代理的体现
目录 1 拦截哪些方法 2 如何代理 3 代理对象 4 责任链设计模式 @ 如果没有自定义过拦截器, 可以看我前面的文章.如果不知道 JDK 动态代理怎么使用的, 可以看我这文章. 责任链设计模式理解 ...
随机推荐
- 使用ECS和OSS搭建个人网盘
体验简介 本场景将提供一台配置了Centos 7.7版本的ECS实例(云服务器)和对象存储OSS实例.通过本教程的操作,您可以基于ECS和OSS快速搭建一个个人网盘. 体验此场景后,可以掌握的知识有: ...
- NIO.2中Path,Paths,Files类的使用
Java NIO Java NIO概述 Java NIO(New IO(新io),Non-Blocking IO(非阻塞的io))是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的 ...
- ClickOnce手动更新
if (ApplicationDeployment.IsNetworkDeployed == true) { ApplicationDeploy ...
- JetBrains 系列软件汉化包 2017.3-2018.1
JetBrains 系列软件汉化包 关键字: Android Studio 3.0-3.1.3 汉化包 CLion 2018.1-2018.2 汉化包 GoLand 2017.3.2-2018.2 汉 ...
- 学习PDO中的错误与错误处理模式
在 PDO 的学习过程中,我们经常会在使用事务的时候加上 try...catch 来进行事务的回滚操作,但是大家有没有注意到默认情况下 PDO 是如何处理错误语句导致的数据库操作失败问题呢?今天,我们 ...
- TP5增加扩展配置目录
ThinkPHP5.0.1版本开始增加了扩展配置目录的概念,在应用配置目录或者模块配置目录下面增加extra子目录,下面的配置文件都会自动加载,无需任何配置. 这极大的方便了我们进行扩展配置,比如在a ...
- 开源ASR服务器vosk
概述 近几年由于AI的迅速发展,语音相关的自然语言处理NLP项目也变多了,新的技术也越来越成熟,其中TTS(语音生成)和ASR(语音识别)是NLP中非常重要的环节. 今天我们介绍一个开源的ASR项目v ...
- Pycharm 使用问题一览
1. I'm not sure if it is the problem of Pycharm or any other IDE. 需要从本地文件中导入文件,但总是出现波浪线,按ctril点进去发现是 ...
- php 常用算法与函数
1.一群猴子排成一圈,按1,2,-,n依次编号.然后从第1只开始数,数到第m只,把它踢出圈,从它后面再开始数,再数到第m只,在把它踢出去-,如此不停的进行下去,直到最后只剩下一只猴子为止,那只猴子就叫 ...
- c++ 的学习 第二集函数的重载之3 -利用IDA分析bebug里面
1. 对项目右击,在文件资源管理器中打开文件夹 2.看debug里面的.exe 这个文件 函数的真实的名字 打开.exe文件就是还是显示,,, 3.debug模式有太多的断点信息还有许多不精简 ...