上节设计模式(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. Spring Boot 入门系列(二十八) JPA 的实体映射关系,一对一,一对多,多对多关系映射!

    前面讲了Spring Boot 使用 JPA,实现JPA 的增.删.改.查的功能,同时也介绍了JPA的一些查询,自定义SQL查询等使用.JPA使用非常简单,功能非常强大的ORM框架,无需任何数据访问层 ...

  2. (9)java Spring Cloud+Spring boot+mybatis企业快速开发架构之SpringCloud-搭建Eureka服务注册中心

    ​ 首先创建一个 Maven 项目,取名为 eureka-server,在 pom.xml 中配置 Eureka 的依赖信息,代码如下所示. <!-- Spring Boot --> &l ...

  3. JavaScript循环 — for、for/in、while、do/while

    for 多次遍历代码块 const array = []for (var i = 0; i < 5; i++) { array.push(i)}console.log(array) // [0, ...

  4. Java面向对象系列(12)- Static关键字讲解

    场景一:静态变量 package oop.demo07; public class Student { private static int age;//静态的变量 一般多线程用的比较多 privat ...

  5. 分布式文件系统FastDFS在CentOS7上的安装及与Springboot的整合

    1. 概述 FastDFS 是目前比较流行的分布式文件系统,可以很容易的实现横向扩展.动态扩容.灾备.高可用和负载均衡. FastDFS 的服务分为 tracker 服务 和 storage 服务,  ...

  6. javascript 面向对象 模块

    * module 完成函数 createModule,调用之后满足如下要求:1.返回一个对象2.对象的 greeting 属性值等于 str1, name 属性值等于 str23.对象存在一个 say ...

  7. git pull 时remote: HTTP Basic: Access denied解决方案

    当qian windows用户密码过期更改了密码后,操作git pull 拉取远程仓库代码或git push时报错 如下:remote: HTTP Basic: Access denied  Auth ...

  8. 『GoLang』面向对象

    我们总结一下前面看到的:Go 没有类,而是松耦合的类型.方法对接口的实现. 面向对象语言最重要的三个方面分别是:封装,继承和多态,在 Go 中它们是怎样表现的呢? Go实现面向对象的两个关键是stru ...

  9. Pycharm软件学生和老师可申请免费专业版激活码

    有一种邮箱,叫做教育邮箱,这东西在这个互联网的世界有很大的优惠及特权,在 Jetbrain 这里, 如果你有教育邮箱(没有教育邮箱怎么办?.edu.cn后缀的邮箱)但很多学生.甚至老师都未必有. 你只 ...

  10. TypeScript 枚举指南

    枚举是受 TypeScript 支持的数据类型.枚举允许您定义一组命名常量.使用它们可以更轻松地记录意图或创建一组不同的案例.枚举大多数用于面向对象的编程语言(如 Java 和 C#)中,现在也可以 ...