代理(proxy):就是一个“中介”。
现在对象A可以直接调用对象B。
需求来了:在调用B的前后打印日志。
静态代理
创建一个新类(发生在编译时),来作为旧类的代理,从而增加功能。增加新功能的代码无法复用。
public class Hello{
public void sayHello(){
System.out.println("Hello");
}
}
// 代理类
public class ProxyStatic extends Hello{
private Hello hello = new Hello();
public void sayHello(){
System.out.println("START log");
hello.sayHello();
System.out.println("END log");
}
}
public class ProxyStaticTest {
public static void main(String[] args){
// 调用原来的类
Hello hello = new Hello();
hello.sayHello();
// 调用代理类
ProxyStatic helloNew = new ProxyStatic();
helloNew.sayHello();
}
}
输出:
Hello
START log
Hello
END log
动态代理 by JDK
需求增加:有一批被调用对象,给这些调用的前后都加上日志。所以,增加新功能的代码可复用,不可能给每一个类都写一个静态代理类;可在运行时修改,也就是说原来的类已经编译加载到内存了。所以,不能修改原来的类,只能创建一个新类,拦截原来的方法。
// 接口
public interface IHello {
void sayHello();
}
// 实现了接口的类
public class Hello implements IHello{
public void sayHello(){
System.out.println("Hello");
}
}
import java.lang.reflect.*;
// 调用处理器
public class LoggerHandler implements InvocationHandler {
private Object target;
public LoggerHandler(Object target){
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable{
System.out.println("START log"); // 增加新功能
Object result = method.invoke(target, args); // 类中的方法调用
System.out.println("END log"); // 增加新功能
return result;
}
}
import java.lang.reflect.Proxy;
public class ProxyJDKTest {
public static void main(String[] args){
IHello hello = new Hello();
// 直接调用,不使用代理
hello.sayHello();
// 使用代理 by JDK
LoggerHandler handler = new LoggerHandler(hello); // 创建调用处理器
IHello helloNew = (IHello) Proxy.newProxyInstance( // 创建代理类
Thread.currentThread().getContextClassLoader(),
hw.getClass().getInterfaces(),
handler
);
helloNew.sayHello(); // 使用代理类调用
}
}
查看代理类(helloNew.getClass()):
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
public class ProxyJDKTest {
public static void main(String[] args) throws IOException {
IHello hello = new Hello();
// use proxy by JDK
LoggerHandler handler = new LoggerHandler(hello);
IHello helloNew = (IHello) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
hello.getClass().getInterfaces(),
handler
);
helloNew.sayHello();
ProxyJDKTest.saveClass(helloNew.getClass()); // 保存代理类的class文件
}
public static void saveClass(Class cls) throws IOException {
byte[] classFile = ProxyGenerator.generateProxyClass(cls.getName(), cls.getInterfaces());
File file =new File(cls.getName() + ".class");
FileOutputStream fos =new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
}
IntelliJ IDEA下查看生成的 $Proxy0.class,自动翻译为源码(去掉try/catch):
public final class $Proxy0 extends Proxy implements IHello {
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 {
……
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
……
}
public final void sayHello() throws {
……
super.h.invoke(this, m3, (Object[])null);
……
}
public final String toString() throws {
……
return (String)super.h.invoke(this, m2, (Object[])null);
……
}
public final int hashCode() throws {
……
return (Integer)super.h.invoke(this, m0, (Object[])null);
……
}
static {
……
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("proxy.proxyJDK.IHello").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
……
}
}
super.h 是 Proxy类的 protected InvocationHandler h;
可见,代理类
无关于:被代理类、handler(InvocationHandler的具体实现)
有关于:接口数组(动态地实现了接口,实现就是转发给 handler,即调动super.h)
对被代理类调用,由 handler 来管理。
JDK动态代理 总结:
实现机制:代理类主要是实现了接口,在接口的方法实现中,将调用转发给 handler;handler调用被代理类,且在调用前后增加新功能。
局限:只能为接口创建代理,返回的代理对象只能转换为某个接口类型。
不适用的情况:如果一个类没有接口,或希望代理代理的方法不是接口中定义的方法,那就不能使用 JDK动态代理了。
动态代理 by CGLib
通过 JDK 实现动态代理有个限制:原来的类的方法是实现的接口中的方法。也就是需要一个接口。
如果类没有实现接口呢?使用 CGLib。
先导入 com.springsource.net.sf.cglib-2.2.0.jar 命名为 net.sf.cglib。
public class Hello {
public void sayHello(){
System.out.println("Hello");
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LogInterceptor implements MethodInterceptor {
public Object intercept(Object obj,
Method method,
Object[] args,
MethodProxy proxy) throws Throwable{
System.out.println("START log");
Object result = proxy.invokeSuper(obj, args);
System.out.println("END log");
return result;
}
}
import net.sf.cglib.proxy.Enhancer;
public class proxyCGLibTest {
public static void main(String[] args){
// 不使用代理
Hello hello = new Hello();
hello.sayHello();
// 使用代理 by CGLib
Enhancer enhancer = new Enhancer(); // 创建一个增强器,用来在运行时生成类
enhancer.setSuperclass(Hello.class); // 设置要继承的目标类
enhancer.setCallback(new LogInterceptor()); // 设置 logInterceptor
Hello helloNew = (Hello) enhancer.create(); // 生成新的类
helloNew.sayHello(); // 调用代理类的方法
}
}
CGLib动态代理 总结:
实现机制:代理类是被代理类的一个子类,重写了父类的所有 public非final 方法,改为调用 Callback中的相关方法(示例中的 intercept)
JDK动态代理与CGLib动态代理对比
JDK动态代理
面向的是一组接口。
动态地创建一个新类,实现这些接口。接口的具体实现是通过 自定义的InvocationHandler 实现的。
代理的是对象。
先有一个被代理的对象;自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象;客户端访问的是代理对象,代理对象再调用实际对象的方法。
CGLib动态代理
面向的是一个具体的类。
动态地创建一个新类,继承了被代理类。重写方法。
代理的是类。创建的对象只有一个。
另
在Python这种动态语言中,实现“动态代理”太容易了。
使用装饰器:
def proxy(func):
def warp():
print('START log')
result = func()
print('END log')
return result
return warp
@proxy
def say_hello():
print('hello')
if __name__ == '__main__':
say_hello()
在函数 say_hello上@proxy,相当于是 say_hello = proxy(say_hello)
等号右边:原来的函数say_hello作为参数传递给函数proxy,proxy的返回值是函数wrap。
等号以及左边:最后让say_hello变量引用wrap。也就是说函数say_hello名称不变,但函数体是wrap