深入代理模式
代理模式简述
卖手机
- 小米公司生产了手机,要卖掉获取利润。
- 现在有很多小米之家(代理商)可以帮助小米公司卖手机。
- 小米之家在卖手机的时候可以提供发票、真机演示、讲解等方面的服务,帮助消费者在买手机的过程中更愉快。
代理模式
为其它对象提供一种代理以控制这个对象的访问。(请注意它跟装饰器模式Decorator的区别)
优点
- 职责清晰(解耦):
- 小米公司负责生产手机,包括屏幕、系统、充电器等等。
- 小米之家负责卖手机,包括提供发票服务、售前售后服务等。
- 可扩展性:
- 小米公司不仅可以生产手机还可以生产小米手环、小米路由器、小米电饭煲等等。
- 小米之家不仅可以卖手机还可以卖小米手环、小米路由器、小米电饭煲等等。
- 功能增强:
- 如果消费者从工厂买手机,那么工厂给你个手机,你就不能享受到小米之家给你的服务。
缺点
- 系统复杂:
小米以前是一个小公司,自己生产手机自己卖;后来逐渐被大众所接受,业务量增大,开始力不从心,于是把生产手机和卖手机变成两个方向去发展,各管各的互不影响;但是小米要统筹生产和销售的关系才能获得最大利润。 - 增加了调用链、处理速度变慢:
消费者去小米之家买一个很火的手机,小米之家告诉消费者,这手机已经卖完了;需要等待下一批;下一批生产的手机需要给到小米之家,小米之家拿到手机后,通知消费者;消费者才能拿到手机。如果消费者直接从手机生产的厂商买,那么只要有货就能拿到货,就很快。
静态代理
UML类图
基于接口的静态代理
/**
* 小米手机
*/
class MiPhone {
public void call() {
System.out.println("我能打电话");
}
public void sendMessage() {
System.out.println("我能发短信");
}
}
/**
* 卖手机服务
*/
interface SellMiPhone {
public abstract MiPhone sell();
}
/**
* 小米工厂卖手机
*/
class MiFactory implements SellMiPhone {
public MiPhone sell() {
System.out.println("This is a MiPhone!");
return new MiPhone();
}
}
/**
* 小米之家卖手机
*/
class MiHome implements SellMiPhone {
private MiFactory factory;
public MiHome(MiFactory factory) {
this.factory = factory;
}
public MiPhone sell() {
System.out.println("售前服务");
MiPhone miPhone = factory.sell();
System.out.println("售后服务");
return miPhone;
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
SellMiPhone sell1= new MiFactory();
sell1.sell(); // 在小米工厂买的手机就是一个裸机
SellMiPhone sell2= new MiHome((MiFactory) sell1);
sell2.sell(); // 在小米之家代理下买的手机具有售前和售后服务
}
}
基于接口的静态代理中,MiHome 和 MiFacotry 是聚合关系;小米之家维护着小米工厂的关系。
为什么叫做静态呢?因为它的类型是事先预定好的,比如上面代码中的 MiHome 这个类。
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。
值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个抽象类(否则在调用的过程中无法确定其类型)。
基于接口的动态代理
/**
* 小米手机
*/
class MiPhone {
public void call() {
System.out.println("我能打电话");
}
public void sendMessage() {
System.out.println("我能发短信");
}
}
/**
* 卖手机服务
*/
interface SellMiPhone {
public abstract MiPhone sell();
}
/**
* 小米工厂卖手机
*/
class MiFactory implements SellMiPhone {
public MiPhone sell() {
return new MiPhone();
}
}
/**
* 小米之家卖手机的逻辑(调用处理器)
*/
class MiHome implements InvocationHandler {
private MiFactory factory; // 小米之家代理的工厂
public void setFactory(MiFactory factory) {
this.factory = factory;
}
/**
* 小米之家的方法调用处理逻辑
*
* @param proxy 当前代理对象
* @param method 当前方法对象
* @param args 当前方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("售前服务");
Object res = method.invoke(factory, args); // 要手机的时候,找被代理的工厂要
System.out.println("售后服务");
return res;
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
MiFactory beiJingMiFactory = new MiFactory(); // 北京小米工厂
MiHome miHome = new MiHome();
miHome.setFactory(beiJingMiFactory); // 这个小米之家卖手机逻辑代理了北京小米工厂
SellMiPhone sell = (SellMiPhone) Proxy.newProxyInstance(SellMiPhone.class.getClassLoader(),
new Class[]{SellMiPhone.class},
miHome); // 现在是小米之家在买手机了
}
}
综述:
我们现在通过JDK动态代理实现了一种代理的方案,我们只需要编写我们自定义的代理逻辑(调用处理器)就可以完成我们的目标。
但是JDK动态代理的实现方式是基于接口的,也就是返回的结果只能是接口类型。
现在我们对比一下静态代理有什么优点?
- 现在我们只需要关注我们代理逻辑(增强的功能),而无需编写特定的代理类代码
- 代理逻辑可以复用,可以代理北京小米工厂也可以代理天津小米工厂
现在我们再思考一个问题:Proxy.newProxyInstance 这个方法是如何帮助我们生成了一个代理类?这个代理类具有什么特点?为什么只能基于接口?(不好意思,一个问题衍生多个问题)
Proxy 源码分析(JDK1.8.0_251)
package java.lang.reflect;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import sun.misc.ProxyGenerator;
import sun.misc.VM;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants;
/**
* Proxy provides static methods for creating dynamic proxy
* classes and instances, and it is also the superclass of all
* dynamic proxy classes created by those methods.
* Proxy提供了创建按代理对象类和实例的静态方法,它也是由这些方法创建的所有动态代理类的父类。
*
* To create a proxy for some interface {@code Foo}:
* 为 Foo 接口创建代理对象的例子:
* InvocationHandler handler = new MyInvocationHandler(...);
* Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
* Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
* newInstance(handler);
* or more simply:
* 或者更简便的方法:
* Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
* new Class<?>[] { Foo.class },
* handler);
*
* A dynamic proxy class (simply referred to as a proxy
* class below) is a class that implements a list of interfaces
* specified at runtime when the class is created, with behavior as
* described below.
* 动态代理类 (以下简称为代理类 )是一个实现了指定的接口列表的类,具有如下的表现。
*
* A proxy interface is such an interface that is implemented
* by a proxy class.
* 代理接口是由代理类实现的接口
*
* A proxy instance< is an instance of a proxy class.
* 代理实例是代理类的一个实例
*
* Each proxy instance has an associated <i>invocation handler</i>
* object, which implements the interface {@link InvocationHandler}.
* A method invocation on a proxy instance through one of its proxy
* interfaces will be dispatched to the {@link InvocationHandler#invoke
* invoke} method of the instance's invocation handler, passing the proxy
* instance, a {@code java.lang.reflect.Method} object identifying
* the method that was invoked, and an array of type {@code Object}
* containing the arguments. The invocation handler processes the
* encoded method invocation as appropriate and the result that it
* returns will be returned as the result of the method invocation on
* the proxy instance.
* 每个代理实例都有一个关联的调用处理对象,它实现了接口InvocationHandler。
* 通过代理接口调用代理实例的方法会分派到 InvokeHandler。
*
* <p>A proxy class has the following properties:
* 代理类具有以下属性:
*
* <ul>
* <li>Proxy classes are <em>public, final, and not abstract</em> if
* all proxy interfaces are public.</li>
* 如果所有的代理接口是public,那么代理类是 public、final、和 非abstract
*
* <li>Proxy classes are <em>non-public, final, and not abstract</em> if
* any of the proxy interfaces is non-public.</li>
* 如果有一个代理接口是非public,那么代理类是 非public、final 和 非abstract
*
* <li>The unqualified name of a proxy class is unspecified. The space
* of class names that begin with the string {@code "$Proxy"}
* should be, however, reserved for proxy classes.
* 代理类的名称是以 $Proxy 开头
*
* <li>A proxy class extends {@code java.lang.reflect.Proxy}.
* 一个代理类继承自 Proxy
*
* <li>A proxy class implements exactly the interfaces specified at its
* creation, in the same order.
* 代理类按照相同的顺序实现了器创建时指定的代理接口
*
* <li>If a proxy class implements a non-public interface, then it will
* be defined in the same package as that interface. Otherwise, the
* package of a proxy class is also unspecified. Note that package
* sealing will not prevent a proxy class from being successfully defined
* in a particular package at runtime, and neither will classes already
* defined by the same class loader and the same package with particular
* signers.
* 如果一个代理类实现了一个 非public 接口,那么它将被定义在与该接口相同的包中,
* 否则代理类的包是未指定的。包的密封性、已经被同一个类加载器定义的类、具有特定签名相同的包,
* 都不会限制运行时定义代理类。
*
* <li>Since a proxy class implements all of the interfaces specified at
* its creation, invoking {@code getInterfaces} on its
* {@code Class} object will return an array containing the same
* list of interfaces (in the order specified at its creation), invoking
* {@code getMethods} on its {@code Class} object will return
* an array of {@code Method} objects that include all of the
* methods in those interfaces, and invoking {@code getMethod} will
* find methods in the proxy interfaces as would be expected.
* 因为一个代理类在创建时实现了指定的所有接口,所以在调用它 Class 对象的 getInterfaces 方法时,
* 会返回一个创建时指定的接口数组;调用它 Class 对象的 getMethods 方法时,会返回它所有接口中的
* 方法;调用它 Class 对象的 getMethod 方法可以找到它代理的接口的方法。
*
* <li>The {@link Proxy#isProxyClass Proxy.isProxyClass} method will
* return true if it is passed a proxy class-- a class returned by
* {@code Proxy.getProxyClass} or the class of an object returned by
* {@code Proxy.newProxyInstance}-- and false otherwise.
* Proxy 中 isProxyClass 方法可以检查一个类是否是代理类:
* 通过 Proxy.getProxyClass 或 Proxy.newProxyInstance 得到的对象,返回是
* 否则返回否
*
* <li>The {@code java.security.ProtectionDomain} of a proxy class
* is the same as that of system classes loaded by the bootstrap class
* loader, such as {@code java.lang.Object}, because the code for a
* proxy class is generated by trusted system code. This protection
* domain will typically be granted
* {@code java.security.AllPermission}.
* 代理类和被 bootstrap classloader 加载器加载的系统类,如 java.lang.Object, 具有相同的
* java.security.ProtectionDomain ;因为代理类的字节码是被受信任的系统字节码生成的。
* 这个 protection domain 自然地被授予 java.security.AllPermission
*
* <li>Each proxy class has one public constructor that takes one argument,
* an implementation of the interface {@link InvocationHandler}, to set
* the invocation handler for a proxy instance. Rather than having to use
* the reflection API to access the public constructor, a proxy instance
* can be also be created by calling the {@link Proxy#newProxyInstance
* Proxy.newProxyInstance} method, which combines the actions of calling
* {@link Proxy#getProxyClass Proxy.getProxyClass} with invoking the
* constructor with an invocation handler.
* 每一个代理类都有一个带有一个参数的 public constructor,一个 InvocationHandler 的实现,
* 用来对代理实例设置 invocation handler
* 一个代理对象的实例不是必须要使用反射 API 来访问公共的构造器,也可以通过调用 Proxy.newProxyInstance
* 方法,这个方法结合了 Proxy.getProxyClass 和 通过 invocation handler 调用构造器
*
* <p>A proxy instance has the following properties:
* 代理实例具有以下属性:
*
* <ul>
* <li>Given a proxy instance {@code proxy} and one of the
* interfaces implemented by its proxy class {@code Foo}, the
* following expression will return true:
* <pre>
* {@code proxy instanceof Foo}
* </pre>
* and the following cast operation will succeed (rather than throwing
* a {@code ClassCastException}):
* <pre>
* {@code (Foo) proxy}
* </pre>
* proxy instanceof Foo 和 (Foo) Proxy 都是可以的
*
* <li>Each proxy instance has an associated invocation handler, the one
* that was passed to its constructor. The static
* {@link Proxy#getInvocationHandler Proxy.getInvocationHandler} method
* will return the invocation handler associated with the proxy instance
* passed as its argument.
* 一个代理对象和一个 InvocationHandler 相关,Proxy.getInvocationHandler(proxy)
* 会返回相关的 InvocationHandler
*
* <li>An interface method invocation on a proxy instance will be
* encoded and dispatched to the invocation handler's {@link
* InvocationHandler#invoke invoke} method as described in the
* documentation for that method.
* 间接调用
*
* <li>An invocation of the {@code hashCode},
* {@code equals}, or {@code toString} methods declared in
* {@code java.lang.Object} on a proxy instance will be encoded and
* dispatched to the invocation handler's {@code invoke} method in
* the same manner as interface method invocations are encoded and
* dispatched, as described above. The declaring class of the
* {@code Method} object passed to {@code invoke} will be
* {@code java.lang.Object}. Other public methods of a proxy
* instance inherited from {@code java.lang.Object} are not
* overridden by a proxy class, so invocations of those methods behave
* like they do for instances of {@code java.lang.Object}.
* </ul>
* 调用 hashCode、equals、toString 会通过代理对象处理,其它的在 java.lang.Object
* 中定义的方法被调用时不会调用代理对象处理
*
* <h3>Methods Duplicated in Multiple Proxy Interfaces</h3>
* 多代理接口中重复的方法
*
* <p>When two or more interfaces of a proxy class contain a method with
* the same name and parameter signature, the order of the proxy class's
* interfaces becomes significant. When such a <i>duplicate method</i>
* is invoked on a proxy instance, the {@code Method} object passed
* to the invocation handler will not necessarily be the one whose
* declaring class is assignable from the reference type of the interface
* that the proxy's method was invoked through. This limitation exists
* because the corresponding method implementation in the generated proxy
* class cannot determine which interface it was invoked through.
* Therefore, when a duplicate method is invoked on a proxy instance,
* the {@code Method} object for the method in the foremost interface
* that contains the method (either directly or inherited through a
* superinterface) in the proxy class's list of interfaces is passed to
* the invocation handler's {@code invoke} method, regardless of the
* reference type through which the method invocation occurred.
* 当代理类的两个或多个接口包含具有相同名称和参数签名的方法时,代理类接口的顺序变得重要。
* 当在代理实例上调用这种重复方法时,传递给调用处理程序的方法对象不一定是其声明类可以通过调用代理方法的接口的引用类型进行分配的对象。
* 这种限制的存在是因为生成的代理类中的相应方法实现无法确定其调用的接口。
* 因此,当在代理实例上调用重复的方法时,代理类的方法列表中包含方法(直接或通过超级接口继承)的最重要的接口中的方法的Method对象被传递给调用处理程序的invoke方法,而不管方法调用发生的引用类型。
*
* <p>If a proxy interface contains a method with the same name and
* parameter signature as the {@code hashCode}, {@code equals},
* or {@code toString} methods of {@code java.lang.Object},
* when such a method is invoked on a proxy instance, the
* {@code Method} object passed to the invocation handler will have
* {@code java.lang.Object} as its declaring class. In other words,
* the public, non-final methods of {@code java.lang.Object}
* logically precede all of the proxy interfaces for the determination of
* which {@code Method} object to pass to the invocation handler.
* 如果代理接口包含具有相同的名称和参数签名的方法hashCode , equals ,或toString的方法java.lang.Object ,
* 当这种方法在代理实例调用时,方法传递到调用处理程序对象将有java.lang.Object作为申明类。
* 换句话说,java.lang.Object的公共非最终方法java.lang.Object逻辑上先于所有代理接口,以确定哪个方法对象传递给调用处理程序。
*
* <p>Note also that when a duplicate method is dispatched to an
* invocation handler, the {@code invoke} method may only throw
* checked exception types that are assignable to one of the exception
* types in the {@code throws} clause of the method in <i>all</i> of
* the proxy interfaces that it can be invoked through. If the
* {@code invoke} method throws a checked exception that is not
* assignable to any of the exception types declared by the method in one
* of the proxy interfaces that it can be invoked through, then an
* unchecked {@code UndeclaredThrowableException} will be thrown by
* the invocation on the proxy instance. This restriction means that not
* all of the exception types returned by invoking
* {@code getExceptionTypes} on the {@code Method} object
* passed to the {@code invoke} method can necessarily be thrown
* successfully by the {@code invoke} method.
* 还要注意,当将一个重复的方法分派到调用处理程序时,
* invoke方法可能只会将可分配给所有可以调用的所有代理接口中的方法的throws子句中的一种异常类型的检查异常类型抛出通过。
* 如果invoke方法抛出经过检查的异常是不能分配给任何通过它可以通过调用代理接口中的方法声明的异常类型,那么选中UndeclaredThrowableException将通过代理实例调用抛出。
* 此限制意味着,并非所有通过调用返回的异常类型getExceptionTypes上方法传递给对象invoke方法一定可以成功地抛出invoke方法。
*
* @author Peter Jones
* @see InvocationHandler
* @since 1.3
*/
public class Proxy implements java.io.Serializable {
private static final long serialVersionUID = -2222568056686623797L;
/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
/**
* Prohibits instantiation.
*/
private Proxy() {
}
/**
* Constructs a new {@code Proxy} instance from a subclass
* (typically, a dynamic proxy class) with the specified value
* for its invocation handler.
*
* @param h the invocation handler for this proxy instance
*
* @throws NullPointerException if the given invocation handler, {@code h},
* is {@code null}.
*/
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
/**
* Returns the {@code java.lang.Class} object for a proxy class
* given a class loader and an array of interfaces. The proxy class
* will be defined by the specified class loader and will implement
* all of the supplied interfaces. If any of the given interfaces
* is non-public, the proxy class will be non-public. If a proxy class
* for the same permutation of interfaces has already been defined by the
* class loader, then the existing proxy class will be returned; otherwise,
* a proxy class for those interfaces will be generated dynamically
* and defined by the class loader.
*
* <p>There are several restrictions on the parameters that may be
* passed to {@code Proxy.getProxyClass}:
*
* <ul>
* <li>All of the {@code Class} objects in the
* {@code interfaces} array must represent interfaces, not
* classes or primitive types.
*
* <li>No two elements in the {@code interfaces} array may
* refer to identical {@code Class} objects.
*
* <li>All of the interface types must be visible by name through the
* specified class loader. In other words, for class loader
* {@code cl} and every interface {@code i}, the following
* expression must be true:
* <pre>
* Class.forName(i.getName(), false, cl) == i
* </pre>
*
* <li>All non-public interfaces must be in the same package;
* otherwise, it would not be possible for the proxy class to
* implement all of the interfaces, regardless of what package it is
* defined in.
*
* <li>For any set of member methods of the specified interfaces
* that have the same signature:
* <ul>
* <li>If the return type of any of the methods is a primitive
* type or void, then all of the methods must have that same
* return type.
* <li>Otherwise, one of the methods must have a return type that
* is assignable to all of the return types of the rest of the
* methods.
* </ul>
*
* <li>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 {@code interfaces} array must not
* exceed 65535.
* </ul>
*
* <p>If any of these restrictions are violated,
* {@code Proxy.getProxyClass} will throw an
* {@code IllegalArgumentException}. If the {@code interfaces}
* array argument or any of its elements are {@code null}, a
* {@code NullPointerException} will be thrown.
*
* <p>Note that the order of the specified proxy interfaces is
* significant: two requests for a proxy class with the same combination
* of interfaces but in a different order will result in two distinct
* proxy classes.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @return a proxy class that is defined in the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access.</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf}.</li>
* </ul>
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}
*/
@CallerSensitive
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
return getProxyClass0(loader, intfs);
}
/*
* Check permissions required to create a Proxy class.
*
* To define a proxy class, it performs the access checks as in
* Class.forName (VM will invoke ClassLoader.checkPackageAccess):
* 1. "getClassLoader" permission check if loader == null
* 2. checkPackageAccess on the interfaces it implements
*
* To get a constructor and new instance of a proxy class, it performs
* the package access check on the interfaces it implements
* as in Class.getConstructor.
*
* If an interface is non-public, the proxy class must be defined by
* the defining loader of the interface. If the caller's class loader
* is not the same as the defining loader of the interface, the VM
* will throw IllegalAccessError when the generated proxy class is
* being defined via the defineClass0 method.
*/
private static void checkProxyAccess(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader ccl = caller.getClassLoader();
if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
}
}
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
/*
* a key used for proxy class with 0 implemented interfaces
*/
private static final Object key0 = new Object();
/*
* Key1 and Key2 are optimized for the common use of dynamic proxies
* that implement 1 or 2 interfaces.
*/
/*
* a key used for proxy class with 1 implemented interface
*/
private static final class Key1 extends WeakReference<Class<?>> {
private final int hash;
Key1(Class<?> intf) {
super(intf);
this.hash = intf.hashCode();
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
Class<?> intf;
return this == obj ||
obj != null &&
obj.getClass() == Key1.class &&
(intf = get()) != null &&
intf == ((Key1) obj).get();
}
}
/*
* a key used for proxy class with 2 implemented interfaces
*/
private static final class Key2 extends WeakReference<Class<?>> {
private final int hash;
private final WeakReference<Class<?>> ref2;
Key2(Class<?> intf1, Class<?> intf2) {
super(intf1);
hash = 31 * intf1.hashCode() + intf2.hashCode();
ref2 = new WeakReference<Class<?>>(intf2);
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
Class<?> intf1, intf2;
return this == obj ||
obj != null &&
obj.getClass() == Key2.class &&
(intf1 = get()) != null &&
intf1 == ((Key2) obj).get() &&
(intf2 = ref2.get()) != null &&
intf2 == ((Key2) obj).ref2.get();
}
}
/*
* a key used for proxy class with any number of implemented interfaces
* (used here for 3 or more only)
*/
private static final class KeyX {
private final int hash;
private final WeakReference<Class<?>>[] refs;
@SuppressWarnings("unchecked")
KeyX(Class<?>[] interfaces) {
hash = Arrays.hashCode(interfaces);
refs = (WeakReference<Class<?>>[])new WeakReference<?>[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
refs[i] = new WeakReference<>(interfaces[i]);
}
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
return this == obj ||
obj != null &&
obj.getClass() == KeyX.class &&
equals(refs, ((KeyX) obj).refs);
}
private static boolean equals(WeakReference<Class<?>>[] refs1,
WeakReference<Class<?>>[] refs2) {
if (refs1.length != refs2.length) {
return false;
}
for (int i = 0; i < refs1.length; i++) {
Class<?> intf = refs1[i].get();
if (intf == null || intf != refs2[i].get()) {
return false;
}
}
return true;
}
}
/**
* A function that maps an array of interfaces to an optimal key where
* Class objects representing interfaces are weakly referenced.
*/
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
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) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
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");
}
/*
* 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.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;
/*
* 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 (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();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
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());
}
}
}
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access;</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf};</li>
* <li> any of the given proxy interfaces is non-public and the
* caller class is not in the same {@linkplain Package runtime package}
* as the non-public interface and the invocation of
* {@link SecurityManager#checkPermission s.checkPermission} with
* {@code ReflectPermission("newProxyInPackage.{package name}")}
* permission denies access.</li>
* </ul>
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
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 (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
private static void checkNewProxyPermission(Class<?> caller, Class<?> proxyClass) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (ReflectUtil.isNonPublicProxyClass(proxyClass)) {
ClassLoader ccl = caller.getClassLoader();
ClassLoader pcl = proxyClass.getClassLoader();
// do permission check if the caller is in a different runtime package
// of the proxy class
int n = proxyClass.getName().lastIndexOf('.');
String pkg = (n == -1) ? "" : proxyClass.getName().substring(0, n);
n = caller.getName().lastIndexOf('.');
String callerPkg = (n == -1) ? "" : caller.getName().substring(0, n);
if (pcl != ccl || !pkg.equals(callerPkg)) {
sm.checkPermission(new ReflectPermission("newProxyInPackage." + pkg));
}
}
}
}
/**
* Returns true if and only if the specified class was dynamically
* generated to be a proxy class using the {@code getProxyClass}
* method or the {@code newProxyInstance} method.
*
* <p>The reliability of this method is important for the ability
* to use it to make security decisions, so its implementation should
* not just test if the class in question extends {@code Proxy}.
*
* @param cl the class to test
* @return {@code true} if the class is a proxy class and
* {@code false} otherwise
* @throws NullPointerException if {@code cl} is {@code null}
*/
public static boolean isProxyClass(Class<?> cl) {
return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
}
/**
* Returns the invocation handler for the specified proxy instance.
*
* @param proxy the proxy instance to return the invocation handler for
* @return the invocation handler for the proxy instance
* @throws IllegalArgumentException if the argument is not a
* proxy instance
* @throws SecurityException if a security manager, <em>s</em>, is present
* and the caller's class loader is not the same as or an
* ancestor of the class loader for the invocation handler
* and invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to the invocation
* handler's class.
*/
@CallerSensitive
public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException
{
/*
* Verify that the object is actually a proxy instance.
*/
if (!isProxyClass(proxy.getClass())) {
throw new IllegalArgumentException("not a proxy instance");
}
final Proxy p = (Proxy) proxy;
final InvocationHandler ih = p.h;
if (System.getSecurityManager() != null) {
Class<?> ihClass = ih.getClass();
Class<?> caller = Reflection.getCallerClass();
if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(),
ihClass.getClassLoader()))
{
ReflectUtil.checkPackageAccess(ihClass);
}
}
return ih;
}
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
}
Proxy 这个类是如何帮助我们生成代理对象的?(两种方式 Proxy.getProxyClass 和 Proxy.newProxyInstance)
Proxy.getProxyClass(ClassLoader loader, Class<?>… interfaces)
@CallerSensitive
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone(); // 克隆接口列表
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs); // 校验权限等
}
return getProxyClass0(loader, intfs);
}
根据API文档了解信息如下:
给定类加载器和接口数组的代理类的java.lang.Class对象。 代理类将由指定的类加载器定义,并将实现所有提供的接口。 如果任何给定的接口是非公开的,则代理类将是非公开的。 如果类加载器已经定义了接口相同置换的代理类,那么将返回现有的代理类; 否则,这些接口的代理类将被动态生成并由类加载器定义。
对可能传递给Proxy.getProxyClass的参数有几个Proxy.getProxyClass :
interfaces数组中的所有类对象都必须表示接口,而不是类或原始类型。
interfaces数组中没有两个元素可能是指相同的类对象。
所有的接口类型必须通过指定的类加载器的名称可见。 换句话说,对于类加载器cl和每个接口i ,以下表达式必须为真:
Class.forName(i.getName(), false, cl) == i 所有非公共接口必须在同一个包中; 否则代理类将不可能实现所有接口,而不管其中定义了什么包。
对于具有相同签名的指定接口的任何成员方法集合:
如果任何方法的返回类型是原始类型或void,则所有方法必须具有相同的返回类型。
否则,其中一个方法必须具有一个返回类型,该类型可以分配给其余方法的所有返回类型。
生成的代理类不能超过虚拟机对类施加的任何限制。 例如,VM可以将类可以实现的接口数量限制为65535; 在这种情况下, interfaces阵列的大小不得超过65535。
如果任何这些限制被违反, Proxy.getProxyClass将抛出一个IllegalArgumentException 。 如果interfaces数组参数或其任何元素为null ,则将抛出一个NullPointerException 。
请注意,指定的代理接口的顺序是重要的:具有相同组合的接口但不同顺序的代理类的两个请求将导致两个不同的代理类。
- ClassLoader 参数必须能够得到接口列表中的接口,否则会抛出异常
- 指定的接口列表顺序不同会得到不同的代理对象
接下来看 getProxyClass0(loader, intfs)
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
这是一个懒加载的实现方式,当你获取了一次后就会保存在WeakCache中,下次传同样的参数就从cache中获取不用再构造。
返回了 proxyClassCache.get(loader, interfaces),proxyClassCache是Proxy中的一个属性
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
WeakCache 是 java.lang.util 包下的一个类,提供了(key, sub-key) -> value的map
它的构造器
/**
* Construct an instance of {@code WeakCache}
*
* @param subKeyFactory a function mapping a pair of
* {@code (key, parameter) -> sub-key}
* @param valueFactory a function mapping a pair of
* {@code (key, parameter) -> value}
* @throws NullPointerException if {@code subKeyFactory} or
* {@code valueFactory} is null.
*/
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
它的get方法
/**
* Look-up the value through the cache. This always evaluates the
* {@code subKeyFactory} function and optionally evaluates
* {@code valueFactory} function if there is no entry in the cache for given
* pair of (key, subKey) or the entry has already been cleared.
*
* @param key possibly null key
* @param parameter parameter used together with key to create sub-key and
* value (should not be null)
* @return the cached value (never null)
* @throws NullPointerException if {@code parameter} passed in or
* {@code sub-key} calculated by
* {@code subKeyFactory} or {@code value}
* calculated by {@code valueFactory} is null.
*/
public V get(K key, P parameter) {
//这里要求实现的接口不能为空
Objects.requireNonNull(parameter);
//清除过期的缓存
expungeStaleEntries();
//将ClassLoader包装成CacheKey, 作为一级缓存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
//获取得到二级缓存
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
//如果根据ClassLoader没有获取到对应的值
if (valuesMap == null) {
//以CAS方式放入, 如果不存在则放入,否则返回原先的值
ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
//如果oldValuesMap有值, 说明放入失败
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//根据代理类实现的接口数组来生成二级缓存key, 分为key0, key1, key2, keyx
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//这里通过subKey获取到二级缓存的值
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//这个循环提供了轮询机制, 如果条件为假就继续重试直到条件为真为止
while (true) {
//如果通过subKey取出来的值不为空
if (supplier != null) {
//在这里supplier可能是一个Factory也可能会是一个CacheValue
//在这里不作判断, 而是在Supplier实现类的get方法里面进行验证
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
//新建一个Factory实例作为subKey对应的值
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
//到这里表明subKey没有对应的值, 就将factory作为subKey的值放入
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
//到这里表明成功将factory放入缓存
supplier = factory;
}
//否则, 可能期间有其他线程修改了值, 那么就不再继续给subKey赋值, 而是取出来直接用
} else {
//期间可能其他线程修改了值, 那么就将原先的值替换
if (valuesMap.replace(subKey, supplier, factory)) {
//成功将factory替换成新的值
supplier = factory;
} else {
//替换失败, 继续使用原先的值
supplier = valuesMap.get(subKey);
}
}
}
}
这里调用了subKeyFactory.apply(key, parameter)获取subKey(这里是Proxy中的KeyFactory)
根据key,parameter,subKey,valuesMap 获得WeakCache中的实现了Supplier接口的内部类Factory。
它的get方法调用了valueFactory.apply(key, parameter) 获取value(这里是Proxy中的ProxyClassFactory),
并替换valuesMap中对应的 key。
(读到这里时我邪恶的笑了,如果面试官问你JDK 动态代理,你可以反手问他一个问题:Proxy创建代理对象是否线程安全?)
WeakCache的get方法并没有用锁进行同步,那它是怎样实现线程安全的呢?因为它的所有会进行修改的成员变量都使用了ConcurrentMap,这个类是线程安全的。因此它将自身的线程安全委托给了ConcurrentMap, get方法尽可能的将同步代码块缩小,这样可以有效提高WeakCache的性能。
所以我们这里要关注两个点:Proxy中的 KeyFactory 和 ProxyClassFactory,ProxyClassFactory才是真正创建代理对象的地方。
Proxy#KeyFactory
/**
* A function that maps an array of interfaces to an optimal key where
* Class objects representing interfaces are weakly referenced.
*/
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
根据接口列表长度返回了对应的WeakCache的子类。
我们看到ClassLoader作为了一级缓存的key,这样可以首先根据ClassLoader筛选一遍,因为不同ClassLoader加载的类是不同的。然后它用接口数组来生成二级缓存的key,这里它进行了一些优化,因为大部分类都是实现了一个或两个接口,所以二级缓存key分为key0,key1,key2,keyX。key0到key2分别表示实现了0到2个接口,keyX表示实现了3个或以上的接口,事实上大部分都只会用到key1和key2。这些key的生成工厂是在Proxy类中,通过WeakCache的构造器将key工厂传入。这里的二级缓存的值是一个Factory实例,最终代理类的值是通过WeakCache#Factory这个工厂来获得的。
WeakCache#Factory
private final class Factory implements Supplier<V> {
//一级缓存key, 根据ClassLoader生成
private final K key;
//代理类实现的接口数组
private final P parameter;
//二级缓存key, 根据接口数组生成
private final Object subKey;
//二级缓存
private final ConcurrentMap<Object, Supplier<V>> valuesMap;
Factory(K key, P parameter, Object subKey,
ConcurrentMap<Object, Supplier<V>> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
@Override
public synchronized V get() {
//这里再一次去二级缓存里面获取Supplier, 用来验证是否是Factory本身
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
//在这里验证supplier是否是Factory实例本身, 如果不则返回null让调用者继续轮询重试
//期间supplier可能替换成了CacheValue, 或者由于生成代理类失败被从二级缓存中移除了
return null;
}
V value = null;
try {
//委托valueFactory去生成代理类, 这里会通过传入的ProxyClassFactory去生成代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
//如果生成代理类失败, 就将这个二级缓存删除
if (value == null) {
valuesMap.remove(subKey, this);
}
}
//只有value的值不为空才能到达这里
assert value != null;
//使用弱引用包装生成的代理类
CacheValue<V> cacheValue = new CacheValue<>(value);
//将包装后的cacheValue放入二级缓存中, 这个操作必须成功, 否则就报错
if (valuesMap.replace(subKey, this, cacheValue)) {
//将cacheValue成功放入二级缓存后, 再对它进行标记
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
//最后返回没有被弱引用包装的代理类
return value;
}
}
可以看到它的get方法是使用synchronized关键字进行了同步。
进行get方法后首先会去验证subKey对应的suppiler是否是工厂本身,如果不是就返回null,而WeakCache的get方法会继续进行重试。
如果确实是工厂本身,那么就会委托ProxyClassFactory生成代理类,ProxyClassFactory是在构造WeakCache的时候传入的。
所以这里解释了为什么最后会调用到Proxy的ProxyClassFactory这个内部工厂来生成代理类。
生成代理类后使用弱引用进行包装并放入reverseMap中,最后会返回原装的代理类。
Proxy#ProxyClassFactory
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
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) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
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");
}
/*
* 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.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;
/*
* 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 (Class<?> intf : interfaces) {
//1.intf是否可以由指定的类加载进行加载
//2.intf是否是一个接口
//3.intf在数组中是否有重复
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();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
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());
}
}
}
- 在代码中可以看到JDK生成的代理类的类名是“$Proxy”+序号。
- 如果接口是public的,代理类默认是public final的,并且生成的代理类默认放到com.sun.proxy这个包下。
- 如果接口是非public的,那么代理类也是非public的,并且生成的代理类会放在对应接口所在的包下。
- 如果接口是非public的,并且这些接口不在同一个包下,那么就会报错。
真正起作用的是:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
ProxyGenerator.generateProxyClass
private byte[] generateClassFile() {
//第一步, 将所有的方法组装成ProxyMethod对象
//首先为代理类生成toString, hashCode, equals等代理方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
//对于具有相同签名的代理方法, 检验方法的返回值是否兼容
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//第二步, 组装要生成的class文件的所有的字段信息和方法信息
try {
//添加构造器方法
methods.add(generateConstructor());
//遍历缓存中的代理方法
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//添加代理类的静态字段, 例如:private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
//添加代理类的代理方法
methods.add(pm.generateMethod());
}
}
//添加代理类的静态字段初始化方法
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//验证方法和字段集合不能大于65535
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//第三步, 写入最终的class文件
//验证常量池中存在代理类的全限定名
cp.getClass(dotToSlash(className));
//验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
cp.getClass(superclassName);
//验证常量池存在代理类接口的全限定名
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
//接下来要开始写入文件了,设置常量池只读
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
//1.写入魔数
dout.writeInt(0xCAFEBABE);
//2.写入次版本号
dout.writeShort(CLASSFILE_MINOR_VERSION);
//3.写入主版本号
dout.writeShort(CLASSFILE_MAJOR_VERSION);
//4.写入常量池
cp.write(dout);
//5.写入访问修饰符
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
//6.写入类索引
dout.writeShort(cp.getClass(dotToSlash(className)));
//7.写入父类索引, 生成的代理类都继承自Proxy
dout.writeShort(cp.getClass(superclassName));
//8.写入接口计数值
dout.writeShort(interfaces.length);
//9.写入接口集合
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
}
//10.写入字段计数值
dout.writeShort(fields.size());
//11.写入字段集合
for (FieldInfo f : fields) {
f.write(dout);
}
//12.写入方法计数值
dout.writeShort(methods.size());
//13.写入方法集合
for (MethodInfo m : methods) {
m.write(dout);
}
//14.写入属性计数值, 代理类class文件没有属性所以为0
dout.writeShort(0);
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//转换成二进制数组输出
return bout.toByteArray();
}
Proxy#defineClass0
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
这个方法是Proxy的静态方法,通过JNI由其它语言实现。
至此,我们已经拿到了代理类Class对象,然后调用其带有一个参数InvocationHandler的构造器实例化对象就可以获取代理对象的实例。
接下来看看newProxyInstance方法是如何做的:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
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 (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
同样的,也是先获取代理类的Class对象,然后获取构造器,实例化。
扩展
我们可以拿到字节码(ProxyGenerator.generateProxyClass),写入文件中,反编译(这里用的别人的):
public final class proxy extends Proxy implements MyService {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
public proxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void test01() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void test02(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("com.dmz.proxy.target.MyService").getMethod("test01");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.dmz.proxy.target.MyService").getMethod("test02", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
获得如下信息:
- 如Proxy的API描述,代理类继承自Proxy,实现了接口。
- 如Proxy的API描述,代理类的equals、hashCode、toString调用InvocationHandler处理。
如果你在自定义InvocationHandler的 invoke 方法中 输出proxy,那么你会得到栈溢出异常,这是为什么呢?
因为你在调用 System.out.println(proxy) 其实是调用了 System.out.println(proxy.toString())
proxy.toString() 又调用了自定义的 InvocationHandler 的 invoke 方法
invoke 又调用了 proxy.toString() …
基于类的动态代理
通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是 CGLIB 的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。
package org;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.*;
import java.lang.reflect.Method;
/**
* 小米手机
*/
class MiPhone {
public void call() {
System.out.println("我能打电话");
}
public void sendMessage() {
System.out.println("我能发短信");
}
}
/**
* 小米工厂卖手机
*/
class MiFactory {
public MiPhone sell() {
return new MiPhone();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
// 打开调试模式,输出代理类字节码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MiFactory.class); // 设置被代理的父类
// /**
// * 可以代理接口
// */
// enhancer.setInterfaces();
// /**
// * 可以通过 CallbackFilter 和 Callbacks 控制哪一个方法用哪一个回调
// */
// enhancer.setCallbackFilter();
// enhancer.setCallbacks();
// /**
// * For simplicity and performance, additional
// * specialized callback types, such as {@link LazyLoader} are also available.
// *
// * API 提供了 LazyLoader 的回调
// */
// enhancer.setCallback(new LazyLoader() {
// @Override
// public Object loadObject() throws Exception {
// return null;
// }
// });
/**
* API提供了 MethodInterceptor 的回调
*/
enhancer.setCallback(new MethodInterceptor() {
/**
* 代理类和被代理类之间是继承关系,使用时注意死循环
*
* @param obj
* 代理对象
* @param method
* 被代理类的方法引用
* @param args
* 方法参数列表
* @param proxy
* 代理类的方法引用;该参数有两个方法,invoke 和 invokeSuper
* invoke 就是调用代理对象(类)的方法,会调用 intercept 的逻辑(递归)
* invokeSuper 就是调用代理对象父类的方法,被代理对象(类)的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("售前服务");
Object res = proxy.invokeSuper(obj, args);
System.out.println("售后服务");
return res;
}
});
// /**
// * API提供了 InvocationHandler 的回调,类似于 JDKProxy 中的 InvocationHandler
// */
// enhancer.setCallback(new MyInvocationHandler(new MiFactory()));
MiFactory superFactory = (MiFactory) enhancer.create();
superFactory.sell();
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
*
* @param proxy
* 代理对象(注意循环问题)
* @param method
* 被代理类方法引用
* @param args
* 方法参数列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("售前服务");
Object res = method.invoke(target, args);
System.out.println("售后服务");
return res;
}
}
这里要注意一个点:
当调用代理对象的方法是父类(被代理类)的final方法,那么直接调用父类的方法。
CGLIB会生成3个类,这里的源码等有空再分析吧
JDK动态代理和CGLIB动态代理的区别
- JDK动态代理主要是基于接口,CGLIB是基于类和接口
- 从性能的角度:能用JDK动态代理的情况下优先考虑JDK动态代理(别人说的)
- 它们都可以可以应用于 AOP,并在 Spring 框架中有应用,底层都是动态生成字节码。
彩蛋
正向代理和反向代理的区别
代理模式的应用
Spring AOP源码