java动态代理技术解析
2019-05-29 本文已影响0人
braveheart075
jdk动态代理
所谓的动态代理,就是在运行时生成一个代理类,来执行被代理类的方法。
使用
1、创建一个接口对象
public interface Subject {
void subject();
}
2、创建一个接口对象的实现类
public class RealSubject implements Subject {
@Override public void subject() {
System.out.println("real subject");
}
}
3、创建一个InvocationHandler
这个handler就是代理subject对象。至于是哪个对象,就看有哪些实现。
public class Handler implements InvocationHandler {
Subject subject;
public Handler(Subject subject) {
this.subject = subject;
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("handler invoke method.");
method.invoke(subject,args);
return subject;
}
}
4、测试
public class Demo {
public static void main(String[] args) {
Subject sub = new RealSubject();
Subject subject = (Subject) Proxy.newProxyInstance(sub.getClass().getClassLoader(),sub.getClass().getInterfaces(),new Handler(sub));
System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(Subject.class.getClassLoader(), Subject.class));
subject.subject();
}
}
原理
这里,我们可以看下生成的代理类,网络上搜索到的基本都是错误的方法,很多都是照抄的,我之前写过一篇文章,论技术人员的严谨性问题,说的就是这个现象。
1、这个$Proxy0的生成,是ProxyGenerator这个jdk包中的类操作的,我们看下他的方法:
很明显,有个saveGeneratedFiles判断,那么这个从哪里来?
static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Path.of(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
2、看下这段代码:
/** debugging flag for saving generated class files */
private static final boolean saveGeneratedFiles =
java.security.AccessController.doPrivileged(
new GetBooleanAction(
"jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();
3、所以,正确的方式是在环境变量里加-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true。
4、执行main方法,就能看到$Proxy0.class文件了。
5、看下这个class文件:
基本明白几点:
- 动态代理必须要实现接口,很多人问为什么需要接口,因为代理类不能多继承,只能实现接口。
- 代理类会继承Proxy类。
- 如果执行了具体的代理类的方法,代理类会调用invocationHandler的invoke方法。所以invocationHandler很重要。
- 这个代理类中会有四个Method,代理类生成的时候,其中三个是hashcode,tostring,equals,剩下的就是被代理的方法。会直接初始化。
public final class $Proxy0 extends Proxy implements Subject {
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);
}
}
public final void subject() throws {
try {
super.h.invoke(this, m3, (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 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("proxy.Subject").getMethod("subject");
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());
}
}
}
以上就是动态代理的本质,有兴趣的同学可以继续到super中去看看。
tips:知识贵在积累。