上节设计模式(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)的更多相关文章

  1. 设计模式之jdk动态代理模式、责任链模式-java实现

    设计模式之JDK动态代理模式.责任链模式 需求场景 当我们的代码中的类随着业务量的增大而不断增大仿佛没有尽头时,我们可以考虑使用动态代理设计模式,代理类的代码量被固定下来,不会随着业务量的增大而增大. ...

  2. Java设计模式系列之动态代理模式(转载)

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Sub ...

  3. 设计模式之Jdk动态代理

    什么是动态代理呢?就是在java的运行过程中,动态的生成的代理类.(为了更熟悉的了解动态代理,你必须先熟悉代理模式,可点击设计模式之代理模式 阅读)我们知道java属于解释型语言,是在运行过程中,寻找 ...

  4. java设计模式中的动态代理

    Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩 ...

  5. Java设计模式之JDK动态代理原理

    动态代理核心源码实现public Object getProxy() { //jdk 动态代理的使用方式 return Proxy.newProxyInstance( this.getClass(). ...

  6. 设计模式学习——JAVA动态代理原理分析

    一.JDK动态代理执行过程 上一篇我们讲了JDK动态代理的简单使用,今天我们就来研究一下它的原理. 首先我们回忆下上一篇的代码: public class Main { public static v ...

  7. Android开发中无处不在的设计模式——动态代理模式

    继续更新设计模式系列.写这个模式的主要原因是近期看到了动态代理的代码. 先来回想一下前5个模式: - Android开发中无处不在的设计模式--单例模式 - Android开发中无处不在的设计模式-- ...

  8. 《Java设计模式》之代理模式 -Java动态代理(InvocationHandler) -简单实现

    如题 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式可细分为如下, 本文不做多余解释 远程代理 虚拟代理 缓冲代理 保护代理 借鉴文章 ht ...

  9. mybatis 插件的原理-责任链和动态代理的体现

    目录 1 拦截哪些方法 2 如何代理 3 代理对象 4 责任链设计模式 @ 如果没有自定义过拦截器, 可以看我前面的文章.如果不知道 JDK 动态代理怎么使用的, 可以看我这文章. 责任链设计模式理解 ...

随机推荐

  1. ASP.NET Core Web API 教程 - Project Configuration

    ASP.NET Core Web API 教程 本系列文章主要参考了<Ultimate ASP.NET Core 3 Web API>一书,我对原文进行了翻译,同时适当删减.修改了一部分内 ...

  2. asp.net 工具

    http://www.jb51.net/article/92465.htm 这篇文章列出了针对ASP.NET开发人员的有用工具. 工具 1.Visual Studio Visual Studio Pr ...

  3. Javascript 判断 iframe 中的变量是否为对象

    Javascript 判断 iframe 中的变量是否为对象 前言 公司之前的项目中,为了实现模块化,在 web 后端使用了 iframe 来组织框架和页面.由于当时没有很好地规划,iframe 子页 ...

  4. 【MySQL】MySQL基础(SQL语句、约束、数据类型)

    数据库的基本概念 什么是数据库? 用于存储和管理数据的仓库 英文单词为:DataBase,简称DB 数据库的好处? 可以持久化存储数据 方便存储和管理数据 使用了统一的方式操作数据库 -- SQL 常 ...

  5. 机器学习——正则化方法Dropout

    1 前言 2012年,Dropout的想法被首次提出,受人类繁衍后代时男女各一半基因进行组合产生下一代的启发,论文<Dropout: A Simple Way to Prevent Neural ...

  6. java 小算法

    //鸡兔同笼 20个头 58腿 for(int a=0;a<=20;a++) { int b = 20-a; if((2*b+4*a)==58) { System.out.println(a+& ...

  7. IOS 安装网页

    一般使用企业签名的iOS安装包 都需要一个下载地址,像蒲公英什么的 都有下载限制,所以求人不如求己. 内容也是复制别人的 :https://lzw.me/a/itms-services-ios-ins ...

  8. 三步搞定IDEA集成热部署

    第一步.在你的SpringBoot项目中添加DevTools依赖 <!-- 热部署DevTools --> <dependency> <groupId>org.sp ...

  9. mysql int(3)与int(10)的数值范围相同吗?

    提问: mysql的字段,unsigned int(3), 和unsinged int(6), 能存储的数值范围是否相同.如果不同,分别是多大? 回答: 不同,int(3)最多显示3位无符号整体,in ...

  10. 使用CEF(四)— 在QT中集成CEF(1):基本集成

    QT作为C++下著名的跨平台软件开发框架,实现了一套代码可以在所有的操作系统.平台和屏幕类型上部署.我们前几篇文章讲解了如何构建一款基于CEF的简单的样例,但这些样例的GUI都是使用的原生的或者是控件 ...