代理,你可以想成显示生活中的代理,就是在被代理对象执行之前,代理进行相关工作的过程,Java中有静态代理和动态代理。
1、静态代理
静态代理比较简单,举例如下:
/**
* 被代理接口类
*/
public interface Rent {
void rentHouse();
String findHouse(String position);
}
/**
* 被代理对象实现类
*/
public class RentInGZ implements Rent {
@Override
public void rentHouse() {
System.out.println("rent this house, i am fulfilled");
}
@Override
public String findHouse(String position) {
System.out.println("the position is :" + position);
return position;
}
public void time(int time){
System.out.println("租期是:" + time + "days");
}
}
/**
* 静态代理
*/
public class StaticRentProxy implements Rent {
private Rent rent;
public StaticRentProxy(Rent rent) {
this.rent = rent;
}
@Override
public void rentHouse() {
System.out.println("before rent house, i want to clean the house");
rent.rentHouse();
}
@Override
public String findHouse(String position) {
return null;
}
}
测试类如下:
public static void main(String[] args) {
Rent rentInGZ = new RentInGZ();
Rent rent = new StaticRentProxy(rentInGZ);
rent.rentHouse();
}
输出:
静态代理很好理解,其实就是把被代理类当成成员对象放到代理类里面,执行被代理类的方法的时候,因为代理类和被代理类实现的接口一致,所以也调用了和被代理对象一样的方法
但是这样子就有一些问题:
(1)可以看到我们的代理类RentHouseProxy和被代理类RentInGZ实现的要是同一个接口Rent,如果我们想要代理多个接口,并且这些接口的代理动作都是类似的(比如加相同模式的日志),实际上我们只需要写一个代理类就可以的,但是使用静态代理就要为每个接口写一个代理类,这样子就很麻烦
(2)同样也是因为代理类要实现接口Rent,如果接口Rent增加或者删除一些方法,那么代理类也要进行相应的操作,这其实是很难维护的
2、动态代理
基于上面这些问题,动态代理就出现了。动态代理怎么解决上面两个问题的呢,首先多个接口的问题,代理类内部成员对象是Obect类型,表示可以代理各种类型的对象,其次多方法问题,使用反射的方法,找到方法并传入参数进行执行
代理类实现接口InvocationHandler:
public class RentHouseHandler implements InvocationHandler {
private Object object;
public RentHouseHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before rent house");
Object ret = method.invoke(object, args);
return ret;
}
}
测试的时候,怎么获取到代理类呢,如下:
public static void main(String[] args) {
Rent rentInGZ = new RentInGZ();//1行
RentHouseHandler rhHandler = new RentHouseHandler(rentInGZ);//2行
Rent proxy = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), rentInGZ.getClass().getInterfaces(), rhHandler);//3行
proxy.findHouse("Guangzhou");//4行
}
执行之后结果如下:
这里使用的是Proxy.newProxyInstance()方法获取到代理对象,看到这里,你或许有下面疑问:
(1)Proxy.newProxyInstance是干什么的,三个参数代表什么意思
(2)为什么通过newProxyInstance()方法获取的对象可以直接转换为Rent类型
(3)代理类实现的InvocationHandler有什么用,invoke方法三个参数表示什么意思,为什么代理方法invoke()会被执行
…
估计一头雾水吧,哈哈,反正我当时看到这里是一脸懵逼~~~
3、动态代理实现原理
解答一下main()方法工作流程:第1、2行代码分别是生成被代理对象和InvocationHandler,第3行代码生成代理对象,这里生成的proxy实际上是com.sun.proxy.$Proxy
类型,这个类是通过反射的方式动态生成的,它实现了Rent接口(后面解释),所以可以强制转换,另外$Proxy
内部的方法调用了Proxy类的invoke方法,以达到调用InvocationHandler 接口类的invoke()方法,我们在RentHouseHandler 对InvocationHandler实现了,所以就调用了RentHouseHandler的invoke()方法,这样就达到了代理的目的
(1)newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法三个参数:
loader: the class loader to define the proxy class 为了实现我们代理类而需要的ClassLoader,这个参数目前还不知道有什么限制,我试验其实是可以传入Main.class.getClassLoader()、RentInGZ.class.getClassLoader()、Rent.class.getClassLoader(),甚至是可以其他和这里不相干的类的ClassLoader
interfaces: 被代理类实现的接口,这个很好理解啦,你要代理什么类,就传入这个类实现的接口喽
h:就是实现了InvocationHandler的代理handler了
这三个参数在后面生成$Proxy
的时候会被使用到
(2)上面第二第三个问题的解答
在解答上面问题之前,我们先看一下Proxy.newProxyInstance()的源码:
newProxyInstance()方法里面生成实例的关键代码是:
设为代码段A
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;
}
});
}
//这里使用Constructor.newInstance(h)的方法生成代理类的实例,h是构造器的参数
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(loader, intfs);
获取到Class对象,然后Class.getConstructor().newInstance()获取到实例,这样就返回了$Proxy
实例
好,进入内部看看getProxyClass0(loader, intfs);
方法:
设为代码段B
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//省略前面的判断
return proxyClassCache.get(loader, interfaces);
}
设为代码段C
public V get(K key, P parameter) {
......
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
//调用的factory.get()
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
真正核心的代码是supplier.get(),supplier其实是被factory赋值的,上面这段代码给factory实例化:
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
可以看到的是key就是ClassLoader,parameter就是interfaces,当调用supplier.get()的时候,实际上调用的是factory.get():
设为代码段D
public synchronized V get() { // serialize access
......
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
......
return value;
}
这里调用的是Proxy.ProxyClassFactory.apply方法:
设为代码段E
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//(1)--------------------------------------------
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());
}
}
//(2)----------------------------------------------------------------------
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.
* 生成 报名+类似$Proxy0 之类的名字
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
* 这里生成字节码的$Proxy0
*/
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());
}
}
}
这个方法主要干了这2件事:(1)部分就是验证interfaces不要出现相同的类型导致冲突 (2)通过反射生成$Proxy
类
$Proxy
这个类是动态生成的,可以通过保存到文件查看:
public static void main(String[] args) {
Rent rentInGZ = new RentInGZ();
RentHouseHandler rhProxy = new RentHouseHandler(rentInGZ);
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
rhProxy.getClass().getName(), rentInGZ.getClass().getInterfaces());
try {
FileOutputStream out = new FileOutputStream("proxy.class");
out.write(proxyClassFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
然后反编译proxy.class文件,源代码如下:
设为代码段F
package pattern.com.helloAspect.self;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class RentHouseHandler extends Proxy implements Rent {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public RentHouseHandler(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String findHouse(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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 rentHouse() throws {
try {
super.h.invoke(this, m4, (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)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("pattern.com.helloAspect.self.Rent").getMethod("findHouse", new Class[]{Class.forName("java.lang.String")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("pattern.com.helloAspect.self.Rent").getMethod("rentHouse", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
这就是我们生成的$Proxy
类了,可以看到这个类实现了Rent接口,我们测试类中的rent实例,是$Proxy()
类型的,但是仍然可以强制转换为Rent
public static void main(String[] args) {
Rent rentInGZ = new RentInGZ();
RentHouseHandler rhProxy = new RentHouseHandler(rentInGZ);
Rent rent = (Rent) Proxy.newProxyInstance(rhProxy.getClass().getClassLoader(), new RentInGZ().getClass().getInterfaces(), rhProxy);
System.out.println(rent.getClass());//输出 class com.sun.proxy.$Proxy0
}
另外 我们可以看到,这个类的构造器是一个传入了InvocationHandler类型的参数的构造器,这就是为什么我们在Proxy.newInstanceProxy()传入InvocationHandler了,生成的源代码是对应“代码段A”的cons.newInstance(new Object[]{h});
这一段代码,前面已经解释了
还有,这个类实现Rent接口的方法的时候,是直接通过h.invoke(this, m, new Object[]{var1})实现的,这样就可以解释为什么代理类执行被代理对象的方法的时候,会调用InvocationHanlder里面的invoke()方法了,理所当然的,我们也知道了InvocationHandler的invoke()方法的三个参数分别表示:$Proxy,被代理对象传入的方法,被代理对象传入方法的参数
结论:
(1)通过反编译$Proxy
类我们可以知道,$Proxy
继承了Proxy, 由于Java不能多继承,所以动态代理只支持借口的代理
(2)$Proxy
里面有关equals、hansCode、toString方法也是调用的h.invoke(),所以也支持这三个方法的代理
结语
篇幅有点长,写的也不是很简洁,有什么写的不对的忘行内的大师多多指出,共同进步
参考:
https://blog.csdn.net/mhmyqn/article/details/48474815
https://www.jianshu.com/p/23d3f1a2b3c7