一、先看一个动态代理的小例子
大家对动态代理应该挺熟了,如果不熟,请绕行(开玩笑,不懂,这篇文章也让你懂)
这是一个接口和一个实现类!
public interface Target {
int test(int i);
}
public class TargetImpl implements Target {
@Override
public int test(int i) {
System.out.println("进入方法 +++++++++++++++===");
return i + 1;
}
}
动态代理的中间类,这个类负责对原有的TargetImpl 对象进行增强。啥?听不懂?
好,比如我现在要给每一个类都增加一条日志信息,开始前记录时间,开始后记录时间,要统计最少时间的执行方法。如何按操作?每一个类都copy相同的代码?一万个类呢,就没办法copy了是吧。而且,要是这么干,就是非常高的耦合度!假设现在不统计时间了,要统计参数个数。每个类都得手动改吗?如何解决?
这个时候aop就上场了,跟大家说easy问题。那么aop如何解决?就是利用的动态代理。代理出的对象就是增强后的对象!!
说白了之前的TargetImpl只是一个普通对象,而通过代理出来的对象,就是一个吃了伟哥的对象。我们通过Proxy.newProxyInstance得到的对象就是一个代理对象。如下。
public class JdkDynamic implements InvocationHandler {
private Target target;
public JdkDynamic(Target target){
this.target = target;
}
public static Target getTarget(Target target){
return (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class<?>[]{Target.class},new JdkDynamic(target));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result = method.invoke(target,args);
System.out.println("after");
return result;
}
}
实际上代理有两种方式:组合和继承。这里JdkDynamic和TargetImpl 采用的是组合,为啥不采用继承?之后我们能够知道为啥。我们将原对象当作参数放到了中间类中,在调用代理对象的方法时候会执行invoke方法。学过反射的同学们应该对Method的这个类比较熟悉,这是一个方法的类,当调用invoke方法的时候这个就是我们通过反射拿到的method作为参数传入到invoke中。proxy就是一个代理对象。args就是一个参数。method.invoke就是一个反射方法对象的调用方式。
如果看过SpringMVC源码的同学,就会发现我们通过uri调用@Controller的@RequestMapping的对应方法,实际上就是通过invoke实现的,在框架中动态代理和反射有着至关重要的作用。
public class Main {
public static void main(String[] args) {
Target target = JdkDynamic.getTarget(new TargetImpl());
target.test(1);
}
}
内容输出:
二、如果让我们来设计一个动态代理,怎么做?
我的思路是:
(1)用字符串拼接成一个代理类,这个字符串就像我们平时看到的类的字符串,用一个Method保存,作为属性,调用方法例如test方法,在这个类中也有InvocationHandler的实现类属性h。有一个方法就是test。
(2)我们将这个字符串保存到一个文件中,后缀名是.java。这就相当于生成了一个类,然后调用第三方编译器,编译成.class字节码文件。
(3)用类加载器去动态加载这个class。然后获得一个Class对象。
(4)然后通过反射创建一个Constructor对象,用这个调用构造器方法,将中间类传入进来。
(5)代理类的test方法,里面调用h.invoke(this,method,int)第三个属性是传入的参数,这样就可以完成动态代理的调用,达到目的了。
实际上源码也是这么做的,这块看不懂,等看到源码的反编译文件就可以理解了。
三、源码分析
点进Proxy.newProxyInstance方法:
public class Proxy implements java.io.Serializable{
//第一个参数是指定代理类的类加载器(我们传入当前测试类的类加载器)
//第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)
//第三个参数是invocation handler,用来处理方法的调用。这里传入我们自己实现的handler
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//检验h不为空,h为空抛异常
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);
}
//得到代理类对象的构造函数,这个构造函数的参数由constructorParams指定
//参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class };
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);
}
}
}
先重点看一下getProxyClass0(loader, intfs);这个方法,这个方法调用成功之后就会是:
这个时候c1实际上就是增长对象的字节码对象了。所以呢我们需要点进去看看如何形成的。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
载点进去get方法。这里只看value的那一块,因为这个字节码对象就是从这获取的。supplier.get();方法。
final class WeakCache<K, P, V> {
public V get(K key, P parameter) {
............
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
}
.............
}
}
再点进去:这里面重要的方法也是赋值给value的那个,key是类加载器,parameter是对应的接口class。
final class WeakCache<K, P, V>{
public synchronized V get() { // serialize access
......
// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
............
return value;
}
}
再点进去:proxyName 就是生成对应类的全限定类名,通过ProxyGenerator.generateProxyClass就能够获取了二进制流。这里相当于生成了一.$Proxy0.class文件,之后又获取这个文件的二进制流。
ProxyGenerator.generateProxyClass这个方法并未开源,所以我们看不到这个方法的逻辑。我们只需要知道获取了二进制流就可以了。实际上我们通过这个二进制流输出一个文件里,就是对应反编译的java类。我们最后会看!!
这个方法实际上就将我们的invoke方法的那些内容都放到了新的代理类里面,我们只需要传入一个封装普通类的InvocationHandler对象就可以了,就可以了。之后调用方法就是调用这个增强类的方法。也就是通过中间类调用普通类的方法。
public class Proxy implements java.io.Serializable{
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
....
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);这是一个本地的方法,底层实际上是用c++来实现,我可以理解为将增强类的二进制流创建了一个Class对象,这个通过类加载器去加载二进制流,然后成为了一个Class,一直向上返回。
一直返回到newProxyInstance方法!
//得到代理类对象的构造函数,这个构造函数的参数由constructorParams指定
//参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class };
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});
这里获取了增强类的构造器,通过调用构造器的newInstance方法,将对应参数传递进去,这其实是我们反射的知识!
四、我们来看生成的代理类
public static void main(String[] args) {
Target target = new TargetImpl();
Target targetest = JdkDynamic.getTarget(target);
String path = "E:\\$Proxy18.class";
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy18", TargetImpl.class.getInterfaces());
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
生成class对象后再反编译。之前我们说jdk动态代理为什么不能用继承?因为生成的代理类默认继承Proxy,如果采用继承,而不是组合,那么就报错了,因为java是单继承
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy18
extends Proxy
implements Target
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy18(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
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 int test(int paramInt)
throws
{
try
{
return ((Integer)this.h.invoke(this, m3, new Object[] { Integer.valueOf(paramInt) })).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
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()
throws
{
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("Target").getMethod("test", new Class[] { Integer.TYPE });
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());
}
}
}
当我们执行test方法的时候实际上执行的是InvocationHandler的invoke方法。传入了对应的invoke所需要的代理对象、方法都西昂和参数。