代理一词含义十分宽泛,例如金融领域的股票发行代理、营销领域的销售代理、以及计算机领域中的代理设计模式等。尽管代理一词被使用的领域如此普遍,可是代理一词的大体的抽象含义是类似的或者说是相同的。代理是一个被委托人委托其执行以下活动:参加活动、行驶权力、执行任务等。这样理解的话,计算机中某个对象或组件的代理就很是好理解了。html
计算机领域中代理的概念是一个十分重要的概念,常见的有代理服务器、代理设计模式等。在软件开发发展成熟的今天,每一个工程的代码量也愈来愈庞大,带来的一个问题就是一次小小的需求修改就会引发很大的变化,从而可能引进新的BUG.
所以程序员对需求修改都深恶痛绝,而代理设计模式在某种程度上能够缓解这种问题。代理设计模式能够实如今不破坏原有代码的状况下,对原有代码添加额外的功能,从而实现以低的侵入完成原有系统功能的扩展,这种设计也符合里氏替换原则(对修改关闭,对扩展开放)。java
编程语言中的代理分为静态代理和动态代理程序员
像大多数其余语言同样,Java能够轻松的实现静态代理。具体来说有两种形式:express
abstract class AbstractProxy {
AbstractProxy real;
public AbstractProxy() {
// TODO Auto-generated constructor stub
}
public AbstractProxy(AbstractProxy real) {
this.real = real;
};
//被代理的方法
public abstract void foolbar(String str);
}
//某个被代理类
class RealClass extends AbstractProxy {
public RealClass() {
super();
}
public RealClass(AbstractProxy real) {
super(real);
// TODO Auto-generated constructor stub
}
@Override
public void foolbar(String str) {
// TODO Auto-generated method stub
System.out.println("out>>" + str);
}
}
final AbstractProxy realObj = new RealClass();
AbstractProxy proxy = new AbstractProxy() {
@Override
public void foolbar(String str) {
// TODO Auto-generated method stub
System.out.println("you are proxied by me!");
realObj.foolbar(str);
}
};
该形式能够实现一个代理,看似是在运行时生成的一个匿名内部类,可是经过测试发现匿名内部类是在编译时期生成的类,这个能够经过匿名内部类类文件来观察,所以其属于静态代理。这种形式的代理看起来不太正常,并且一个代理类只能代理一个接口或者一个抽象类,若是代理多个就必须新增长多个匿名内部类。编程
//被代理的接口
interface IReal {
public void doSomeThing(String str);
}
//某个被代理的类
class RealClass implements IReal {
@Override
public void doSomeThing(String str) {
// TODO Auto-generated method stub
System.out.println("doSomeThing " + str);
}
}
class ProxyClass implements IReal {
IReal realObj;
public ProxyClass(IReal realObj) {
this.realObj = realObj;
}
@Override
public void doSomeThing(String str) {
// TODO Auto-generated method stub
System.out.println("you are proxied by me!");
realObj.doSomething();
}
}
RealClass realObj = new RealClass();
RealClass proxy = new ProxyClass(realObj);
这种形式的代理类型须要在编译时期肯定,所以属于静态类型。从某种程度上来看,这种形式和装饰器模式、适配器模式的设计思路类似。缺点同第一种形式同样,若是多个须要代理多个接口就须要重写代理类,让其实现多个被代理的接口;同时在类型转换的时候也会很麻烦。设计模式
java
的动态代理是运行时动态的根据须要被代理的接口列表interfaces生成一个代理类,该代理类实现了接口列表interfaces
中的全部方法,而后在方法的内部实际是讲该方法的调用转发给了实现了InvocationHandler
接口的对象,顾名思义,该对象让包含代理时被代理方法的代理逻辑。api
用法举例:编写一个代理类实现拦截某个被代理方法
具体的使用代码:数组
interface IProxied1 {
public void proxiedMethod1(String str);
}
interface IProxied2 {
public void proxiedMethod2(String str);
}
class Proxied implements IProxied1, IProxied2 {
@Override
public void proxiedMethod2(String str) {
// TODO Auto-generated method stub
System.out.println("proxiedMethod2 " + str);
}
@Override
public void proxiedMethod1(String str) {
// TODO Auto-generated method stub
System.out.println("proxiedMethod1 " + str);
}
}
class Interceptor implements InvocationHandler {
Object proxied;
public Interceptor() {
// TODO Auto-generated constructor stub
}
public Interceptor(Object proxied) {
// TODO Auto-generated constructor stub
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("I am watching...");
//判断是否拦截
if(method.getName().equals("proxiedMethod1")) {
System.out.println("you are intercepted!!!!!");
return null;
}
return method.invoke(proxied, args);
}
}
执行代码缓存
public class DynamicProxyDemo {
public static void main(String[] str) {
IProxied1 proxiedObj = new Proxied();
Object proxyObj = Proxy.newProxyInstance(IProxied1.class.getClassLoader(), new Class<?>[]{IProxied1.class, IProxied2.class}, new Interceptor(proxiedObj));
((IProxied1)proxyObj).proxiedMethod1("Hello, World!");
System.out.println("-------");
((IProxied2)proxyObj).proxiedMethod2("Hello, World!");
}
}
输出以下:bash
I am watching... you are intercepted!!!!! ------- I am watching... proxiedMethod2 Hello, World!
通常来说,静态代理是硬编码去实现一个代理类,若是须要被代理的接口有变更,则须要从新编码代理类;静态绑定的过程将代理类的代理逻辑和代理类的生成绑定到一块儿了,
因此修改起来不是很方便(解耦不完全)。其实咱们最关注的是代理类的代理逻辑,所以若是将代理的生成自动化(由于代理类的生成的规则是general
的,能够泛化。先实现被代理的接口、而后方法转发,就是这么简单。),
而将代理逻辑分离出来,全部的代理逻辑所有发生在这里;经过这样的解耦,代码可维护性会加强、侵入性会减少,这就是动态代理的思想。
具体来说区别以下图:
静态代理的代理类多处出现代理逻辑的代码,而且同时静态代理的代理类须要本身硬编码。
动态代理的代理类相似于一个方法路由,对被代理对象的任何被代理方法的调用,都会被该路由转发到InvocationHandler代理逻辑处理类中,从而将代理类的生成和代理类的代理逻辑分开。
Java
动态代理生成的代理类是直接在内存中按照class
文件格式生成了一个二进制文件,而后类加载器加载该二进制类文件,最后实例化一个代理类的实例。
前面已经介绍了Java
动态代理的基本用法,主要涉及到以下几个类和方法以下:(JDK7
)
java.lang.reflect.Proxy
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
sun.misc.ProxyGenerator
public static byte[] generateProxyClass(final String name, Class[] interfaces)
java.lang.reflect.InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
具体源代码以下:
/** * Returns an instance of a proxy class for the specified interfaces * that dispatches method invocations to the specified invocation * handler. This method is equivalent to: * <pre> * Proxy.getProxyClass(loader, interfaces). * getConstructor(new Class[] { InvocationHandler.class }). * newInstance(new Object[] { handler }); * </pre> * * <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 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} */
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);
/* * Invoke its constructor with the designated invocation handler. */
try {
Constructor cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[] { h });
} 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());
}
}
/** * 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 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 NullPointerException if the {@code interfaces} array * argument or any of its elements are {@code null} */
//根据一组接口列表interfaces,返回一个由类加载器loader加载的实现了全部interfaces的类对象
//若是实现接口列表interfaces的类已经被加载过了;则直接返回缓存的类对象
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 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<>();
//验证接口列表的接口对于参数给定的类加载器loader是不是可见的;
//同时检查接口列表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);
/* * 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. */
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 {
//cache 的key是接口名数组生成的list;value是一个代理类对象的弱引用
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;//被唤醒后;继续check是否存在类对象
} 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. */
//不存在代理类的时候,须要本身去生成代理类;生成代理类以前先置一个状态标志对象pendingGenerationMarker
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
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;
/* * Verify that the class loader hasn't already * defined a class with the chosen name. */
/* * Generate the specified proxy class. */
//重点在这里;调用ProxyGenerator的静态方法去生成一个代理类,获得类文件的字节数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
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. */
synchronized (cache) {
if (proxyClass != null) {
//加入缓存
cache.put(key, new WeakReference<Class<?>>(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();//无论建立代理类是否成功,唤醒在cache上面等待的线程
}
}
return proxyClass;
}
/** * Generate a class file for the proxy class. This method drives the * class file generation process. */
//根据获得的方法信息、类信息;按照JVM的规范动态的生成一个代理类的二进制文件
//该过程比较复杂涉及到许多JVM的指令
private byte[] generateClassFile() {
/* ============================================================ * Step 1: Assemble ProxyMethod objects for all methods to * generate proxy dispatching code for. */
/* * Record that proxy methods are needed for the hashCode, equals, * and toString methods of java.lang.Object. This is done before * the methods from the proxy interfaces so that the methods from * java.lang.Object take precedence over duplicate methods in the * proxy interfaces. */
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
/* * Now record all of the methods from the proxy interfaces, giving * earlier interfaces precedence over later ones with duplicate * methods. */
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 each set of proxy methods with the same signature, * verify that the methods' return types are compatible. */
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
/* ============================================================ * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. */
try {
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
/* ============================================================ * Step 3: Write the final class file. */
/* * Make sure that constant pool indexes are reserved for the * following items before starting to write the final class file. */
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
/* * Disallow new constant pool additions beyond this point, since * we are about to write the final constant pool table. */
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
/* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */
// u4 magic;
dout.writeInt(0xCAFEBABE);
// u2 minor_version;
dout.writeShort(CLASSFILE_MINOR_VERSION);
// u2 major_version;
dout.writeShort(CLASSFILE_MAJOR_VERSION);
cp.write(dout); // (write constant pool)
// u2 access_flags;
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
// u2 this_class;
dout.writeShort(cp.getClass(dotToSlash(className)));
// u2 super_class;
dout.writeShort(cp.getClass(superclassName));
// u2 interfaces_count;
dout.writeShort(interfaces.length);
// u2 interfaces[interfaces_count];
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(
dotToSlash(interfaces[i].getName())));
}
// u2 fields_count;
dout.writeShort(fields.size());
// field_info fields[fields_count];
for (FieldInfo f : fields) {
f.write(dout);
}
// u2 methods_count;
dout.writeShort(methods.size());
// method_info methods[methods_count];
for (MethodInfo m : methods) {
m.write(dout);
}
// u2 attributes_count;
dout.writeShort(0); // (no ClassFile attributes for proxy classes)
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
return bout.toByteArray();
}
经过反编译生成的动态代理类的文件,能够获得
final class $Proxy0
extends Proxy
implements IReal
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doSomeThing(String paramString)
{
try
{
this.h.invoke(this, m3, new Object[] { paramString });//方法的转发;反射调用InvocationHandler的invoke方法
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("trick.IReal").getMethod("doSomeThing", new Class[] { Class.forName("java.lang.String") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
java
动态代理的应用在框架中十分普遍,例如Spring
框架用动态代理来实现AOP
、Struts2
框架利用动态代理实现拦截器。
AOP
中的代理逻辑点
又称为切面的切入点(cut point)
。另外,实现AOP
概念的方式是动态代理,但动态代理的形式有不少种,JDK
提供的这种只是其中一种,还有涉及到类加载器加载类前、加载类后植入字节码等形式。
[1] 完全理解JAVA动态代理
[2] JDK动态代理实现原理
[3]AOP动态代理的实现机制