最近在系统的啃一啃设计模式相关的东西, 其中对代理模式尤为感兴趣(主要也觉得它很神奇),而且代理模式的用途实在是太广泛了
说代理之前,从一个需求入手:
有一个tank类,作为一个坦克,肯定可以移动,也就是肯定会有一个移动的方法,我想记录一下它的move方法的执行时间,该怎么做?
interface Movable {
void move();
}
public class Tank implements Movable {
@Override
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
最low的可能想到是手动在move方法前后手动添加执行代码。。。
@Override
public void move() {
long start = System.currentTimeMillis();
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
但这种方式,总是要修改源代码,那我要是之后不想要了,是不是还得手动删除这段代码呢?
而且,如果是无法修改源码的情况下呢? 例如引入的是第三方的类。
代理就是用来解决这种方式的。
代理大致分为两种:
1、静态代理
2、动态代理
我们一个一个说。
引入一个代理类,通过持有原对象
class TankTimeProxy implements Movable {
Movable m;
public TankTimeProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
long start = System.currentTimeMillis();
m.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
}
此时可以:
public static void main(String[] args) {
new TankTimeProxy(new Tank()).move();
}
好像到此已经实现了需求,也比较优雅的实现了,但想一想,如果我不光想记录执行时间,还想在前后做些其他事情呢?简单嘛,再来个代理类。
如果这样的各式需求比较多的话,那么将产生许多的代理对象,好像事情变得比较麻烦了。。。
所以,静态代理的缺点可以总结下:
代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
静态代理的问题就是每一个代理都只能针对一个类型的事务。
那如果我们将代理行为与被代理对象进行分离呢?
public class Tank implements Movable {
@Override
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Tank tank = new Tank();
//reflection 通过二进制字节码分析类的属性和方法
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class}, //tank.class.getInterfaces()
new LogHander(tank)
);
m.move();
}
}
class LogHander implements InvocationHandler {
Tank tank;
public LogHander(Tank tank) {
this.tank = tank;
}
//getClass.getMethods[]
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method " + method.getName() + " start..");
Object o = method.invoke(tank, args);
System.out.println("method " + method.getName() + " end!");
return o;
}
}
interface Movable {
void move();
}
JDK的动态代理需要被代理类必须是有实现接口的,因为它的动态代理是基于接口进行代理的。
调用原理为了看清这个过程,我们以上面代码为例,直接来看一下它生成的代理类是长什么样子的
先要开启一个设置:
public static void main(String[] args) {
Tank tank = new Tank();
//这个参数名称可能会根据版本有所不同
//可以在java.lang.reflect.ProxyGenerator类中找到字段saveGeneratedFiles进行查看
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");//在使用代理的时候,生成文件
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class}, //tank.class.getInterfaces()
new TimeProxy(tank)
);
m.move();
}
运行程序,发现项目根目录生成了一个文件夹,打开,看到Proxy类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.peng.dp.proxy.v10;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements Movable {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(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);
}
}
//发现果然这个代理类有个move方法
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null); //调用指定的代理处理类的invoke方法(也就是我们先前写入逻辑的那个方法)
} 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 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"));
m3 = Class.forName("com.peng.dp.proxy.v10.Movable").getMethod("move"); //得到被代理对象指定接口的方法
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
关注代理类的move方法
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null); //调用指定的代理处理类的invoke方法(也就是我们先前写入逻辑的那个方法)
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
h就是我们之前传入的InvocationHandler ,调用它的invoke方法。
m3是谁?
观察代理类的静态代码块:
m3 = Class.forName("com.peng.dp.proxy.v10.Movable").getMethod("move"); //得到被代理对象指定接口的方法
可以看到,就是被代理对象的那个move方法。。
是不是看到代理类,一切就都串起来了呢?
源码分析这里以JDK8为基础,进行源码分析。
首先从生成代理对象的这个方法作为入口:
Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
new Class[]{Movable.class}, //tank.class.getInterfaces()
new TimeProxy(tank)
进入newProxyInstance方法:
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);
}
//根据注释,我们可以看到,这里就是生成代理的方法了
Class> cl = getProxyClass0(loader, intfs);
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() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//对代理类进行实例化,并传入我们指定的handler
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);
}
}
我们看到getProxyClass0就是生成代理的方法。
进入getProxyClass0方法:
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);
}
这里发现没几行代码,最终是使用了proxyClassCache类,我们来看下这个类是个啥:
private static final WeakCache[], Class>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
可以发现,它是一个WeakCache类,它的构造方法分别传入了KeyFactory和ProxyClassFactory,这里埋一笔,其实它最终要使用ProxyClassFactory进行对代理类的生成。
我们继续。。。
进入proxyClassCache的get方法:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap
继续的入口点是这里,调用了Factory类的get方法:
V value = supplier.get();
我们继续深入:
进入get方法:
@Override
public synchronized V get() { // serialize access
// re-check
Supplier supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a Cachevalue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
//划重点,这里用到了valueFactory,也就是我们先前看到的那个ProxyClassFactory类,用于生成代理类的
//所以这里就是我们的入口
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with Cachevalue (WeakReference)
Cachevalue cachevalue = new Cachevalue<>(value);
// try replacing us with Cachevalue (this should always succeed)
if (valuesMap.replace(subKey, this, cachevalue)) {
// put also in reverseMap
reverseMap.put(cachevalue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new Cachevalue -> return the value
// wrapped by it
return value;
}
看到了valueFactory,我们的代理对象的生成就是它操控的:
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
继续深入,进入它的apply方法:
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
Map, 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;
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 + ".";
}
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//根据注释,我们就知道,这里就是生成代理类的核心方法了。。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//通过生成的代理类的字节数组内容,生成Class对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
可以看到我们的生成代理类的核心方法了:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
继续进入:
public static byte[] generateProxyClass(final String var0, Class>[] var1, int var2) {
//注意这个类,是不是就是我们先前为了显示代理类文件的生成,设置参数名找寻的那个类呢?
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
//向字节数组写入我们要生成的代理类的class内容
final byte[] var4 = var3.generateClassFile();
//如果开启了我们那个设置
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
//写入文件
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
final byte[] var4 = var3.generateClassFile();
继续进入generateClassFile:
private byte[] generateClassFile() {
//给代理类添加基础方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
//给代理添加接口方法
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
这个方法其实就涵盖了整个代理类的生成过程,对字段,方法的填充。。。
但我们抱着深入到底的心态,看看到底是咋实现的,继续再深入一步。。
进入addProxyMethod方法:
private void addProxyMethod(Method var1, Class> var2) {
String var3 = var1.getName();
Class[] var4 = var1.getParameterTypes();
Class var5 = var1.getReturnType();
Class[] var6 = var1.getExceptionTypes();
String var7 = var3 + getParameterDescriptors(var4);
Object var8 = (List)this.proxyMethods.get(var7);
if (var8 != null) {
Iterator var9 = ((List)var8).iterator();
while(var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
if (var5 == var10.returnType) {
ArrayList var11 = new ArrayList();
collectCompatibleTypes(var6, var10.exceptionTypes, var11);
collectCompatibleTypes(var10.exceptionTypes, var6, var11);
var10.exceptionTypes = new Class[var11.size()];
var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
return;
}
}
} else {
var8 = new ArrayList(3);
this.proxyMethods.put(var7, var8);
}
//包装好方法,调用add。 重点是这个add方法 这是谁的add方法?
((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2));
}
实际上,它是MethodNode的add方法:
再向里面点,发现不能定位到对应方法了,只能定位到类里面,但是没关系,因为我们已经知道,到底幕后主使是谁了:
package jdk.internal.org.objectweb.asm.tree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.TypePath;
public class MethodNode extends MethodVisitor {
public int access;
public String name;
public String desc;
public String signature;
public List exceptions;
public List parameters;
public List visibleAnnotations;
public List invisibleAnnotations;
public List visibleTypeAnnotations;
public List invisibleTypeAnnotations;
public List attrs;
public Object annotationDefault;
没错,从包名,就知道了,代理类生成的底层调用其实是ASM!
ASM是一款强大的操纵二进制class文件的工具包。
如果你了解设计模式,应该知道里面有一种模式叫访问者模式。
ASM内部就是运用了这种模式,暴露一个类的各个部分(比如说参数,方法体等等)来给调用者操作。
上面的类继承了MethodVisitor,我们来看看MethodVisitor的部分方法:
如图:
整体大致流程
JDK的动态代理需要被代理类必须是有实现接口的。
而我们的类没有实现接口的时候怎么办呢?
可以使用cglib
引入pom:
cglib cglib 3.3.0
public class Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Tank.class);
enhancer.setCallback(new TimeMethodInterceptor());
Tank tank = (Tank)enhancer.create();
tank.move();
}
}
class TimeMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(o.getClass().getSuperclass().getName());
System.out.println("before");
Object result = null;
result = methodProxy.invokeSuper(o, objects);//调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)
System.out.println("after");
return result;
}
}
class Tank {
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
cglib产生的动态对象是被代理对象的子类,因此final修饰的class是无法生成的。
扩展不管是jdk的动态代理还是cglib,其实底层调用都是基于ASM。。。
基于ASM的使用其实都是基于API的,还有更强大的一种:instrument ,它是JDK自带的,可以在class被装载前进行拦截,对其进行字节码修改。



