简单聊聊静态代理与动态代理(JDK动态代理与CGLIB)
代理
代理是什么?我们来看一下百度百科:
由此我们可以联想到我们在java中使用的代理,当我们在操作一个对象的时候,我们不想更改这个对象或者无法直接操作这个对象时,我们就需要一个代理对象来进行操作。就想现实中的代购,代驾等。
java中代理模式又分为静态代理和动态代理。下面我就简单介绍一下:
静态代理
静态代理在程序运行前已经编译好的代理类,可以通过程序员手打或者使用工具生成。下面是一个简单的静态代理实现:
interface Hello{
String sayHello(String hero);
}
class HelloImpl implements Hello{
@Override
public String sayHello(String hero) {
return "HelloImp"+str;
}
}
/**
* 静态代理
*/
class StaticProxyHello implements Hello{
private Hello hello = new HelloImpl();
@Override
public String sayHello(String hero) {
return hello.sayHello(hero);
}
}
动态代理
当我们程序涉及使用到大量的代理类时,单纯的手打或工具类生成不仅会耗费大量的时间,而且会造成大量的代码冗余。使用动态代理,不仅可以实现原先类方法的功能,而且还在原来的基础上添加了额外的功能。由于代理类是动态生成的,即具有解耦意义,灵活并且扩展性强。我们经常使用的SpringAOP, RPC等都使用了动态代理。下面就使用简单的代码来模拟一下两种不同的动态代理实现:
JDK动态代理
java内置的,是基于接口来实现代理的,通过接口继承java.lang.reflect包下的InvocationHandler接口,底层通过反射实现代理。
主要方法:
Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[]{Hello.class},
new LogInvocationHandler(new HelloImpl()));
传入代理对象的类加载器,代理对象需要实现的接口,方法调用的实际处理者 三个参数来返回一个代理对象。这个代理对象包括被代理对象的所有方法,还包括在invoke中对原对象进行扩展、修改的方法。
/**
* JDK动态代理
*/
class LogInvocationHandler implements InvocationHandler{
private Object hello;
public LogInvocationHandler(Object hello) {
this.hello = hello;
}
public LogInvocationHandler() {
}
public Object CreateHelloProxy(){
hello = (Hello) Proxy.newProxyInstance(getClass().getClassLoader(),new Class[]{Hello.class},new LogInvocationHandler(new HelloImpl()));
return hello;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sayHello".equals(method.getName())){
System.out.println("进入代理,可随意操作");
}
return method.invoke(hello,args);
}
}
测试类
public class MyTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\CODEGENERATION");
LogInvocationHandler logInvocationHandler = new LogInvocationHandler();
Hello o = (Hello)logInvocationHandler.CreateHelloProxy();
String s = o.sayHello("你好吗?杰克");
System.out.println(s);
}
}
CGLIB代理
CGLIB(Code Generation Library ):是基于ASM的字节码生成库,通过继承MethodInterceptor实现代理,底层提供了FastClass增强功能,主要是用于调用方法时变量的场景,用于替代反射调用
public class CGLIBLogInvationHanlder implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("进入CGLIB代理类");
return methodProxy.invokeSuper(o,objects);
}
}
class HelleCGLIB{
public String sayHello(String str){
return "HelloCGLIB"+str;
}
}
测试类
public class MyTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\CODEGENERATION");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelleCGLIB.class);
enhancer.setCallback(new CGLIBLogInvationHanlder());
HelleCGLIB helleCGLIB = (HelleCGLIB) enhancer.create();
String cglib = helleCGLIB.sayHello("我是CGLIB");
System.out.println(cglib);
}
}
FastClass机制:
首先,我们创建EnHancer对象,然后通过setSuperClass()设置需要代理的对象,再用setCallback()设置回调函数,也就是你实现MethodInterceptor接口的类,之后用create()创建出代理对象A。当使用A调用sayHello()方法时,首先进入intercept方法,然后进入methodProxy.invokeSuper(o,object)
这里我们可以看到传入的参数是代理对象与Object数组,
然后通过init()
也就是,在我们使用A调用sayHello()方法时,在代理类中会进行判断是否实现了MethodInterceptor,如果没有,则直接调用A的sayHello();如果实现了MethodInterceptor,在MethodInterceptor中会为A中的所有方法建立索引,建立索引底层在FastClass中将每个方法的引用保存在数组中,这样当调用方法时就可以直接通过索引来调用,而不使用反射;这里只有一个方法,可以看到传入的args为Object[1]。
日拱一卒,得寸进尺。
下一篇: MySQL支持IPv6