动态代理 V.S 静态代理
- Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大
- 可以实现AOP编程,这是静态代理无法实现的
- 解耦,如果用在web业务下,可以实现数据层和业务层的分离
- 动态代理的优势就是实现无侵入式的代码扩展。
静态代理这个模式本身有个大问题,若类方法数量越来越多的时候,代理类的代码量十分庞大的。所以引入动态代理
动态代理
Java中动态代理的实现的关键:
- Proxy
- InvocationHandler
InvocationHandler#invoke
-
method
调用的方法,即需要执行的方法
-
args
方法的参数
-
proxy
代理类的实例
-
Java动态代理模式jdk和cglib(上)动态代理 V.S 静态代理动态代理JDK动态代理
JDK动态代理
JDK动态代理模式里有个拦截器,在JDK中,只要实现了InvocationHandler接口的类就是一个拦截器类。
假如写了个请求到action,经过拦截器,然后才会到action,执行后续操作。
拦截器就像一个过滤网,一层层过滤,只有满足一定条件,才能继续向后执行。
拦截器的作用:控制目标对象的目标方法的执行。
拦截器的具体操作步骤:
引入类
目标类和一些扩展方法相关的类
赋值
调用构造器,给相关对象赋值
合并逻辑处理
在invoke方法中把所有的逻辑结合在一起。最终决定目标方法是否被调用
示例
思考如下问题:
代理对象由谁产生
JVM,不像静态代理,我们得自己new个代理对象。
代理对象实现了什么接口
实现的接口是目标对象实现的接口。
同静态代理中代理对象实现的接口。那个继承关系图还是相同的。
代理对象和目标对象都实现一个共同的接口。就是这个接口。
所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。
代理对象的方法体是什么
代理对象的方法体中的内容就是拦截器中invoke方法中的内容。
所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在这个方法里面处理的。
拦截器中的invoke方法中的method参数是在什么时候赋值的
在客户端,代理对象调用目标方法的时候,此实例中为:
proxyObj.business();
实际上进入的是拦截器中的invoke方法,这时拦截器中的invoke方法中的method参数会被赋值。
为啥这叫JDK动态代理
因为该动态代理对象是用JDK相关代码生成。
很多同学对动态代理迷糊,在于proxyObj.business();理解错了,至少是被表面所迷惑,没有发现这个proxyObj和Proxy之间的联系,一度纠结最后调用的这个business()是怎么和invoke()联系上的,而invoke又怎么知道business存在的。
其实上面的true和class P r o x y 0 就 能 解 决 很 多 的 疑 问 , 再 加 上 下 面 将 要 说 的 Proxy0就能解决很多的疑问,再加上下面将要说的Proxy0就能解决很多的疑问,再加上下面将要说的Proxy0的源码,完全可以解决动态代理的疑惑了。
我们并没有显式调用invoke(),但是这个方法确实执行了。下面分析:
从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 查找或生成指定的代理类
* 创建代理类$Proxy0
* $Proxy0类实现了interfaces的接口,并继承了Proxy类
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* 使用指定的调用处理程序调用其构造器
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 形参为InvocationHandler类型的构造器
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});
} ...
}
Proxy.newProxyInstance做了如下事:
根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0。$Proxy0类 实现了interfaces的接口,并继承了Proxy类
实例化$Proxy0,并在构造器把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值
$Proxy0的源码:
package com.sun.proxy;
public final class $Proxy0 extends Proxy implements TargetInterface {
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});
}...
}
public final void business() throws {
try {
super.h.invoke(this, m3, (Object[])null);
}...
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
}...
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
}...
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.javaedge.design.pattern.structural.proxy.dynamicproxy.jdkdynamicproxy.TargetInterface").getMethod("business");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
}...
}
}
接着把得到的$Proxy0实例强转成TargetInterface,并将引用赋给TargetInterface。当执行proxyObj.business(),就调用了$Proxy0类中的business()方法,进而调用父类Proxy中的h的invoke()方法。即InvocationHandler.invoke()。
Proxy#getProxyClass返回的是Proxy的Class类,而非“被代理类的Class类”!