代理模式分为静态代理和动态代理,在《Java设计模式八》一文中描述的是静态代理的实现方法,静态代理也带来了一些局限性:
1、可扩展性差,表现在每增加一个被代理对象,就需要增加一个代理类,被代理对象每实现一个方法,代理类就需要实现一个方法,即横向扩展和纵向扩展皆差。
2、可维护性差
鉴于这些局限性,Java为我们实现了动态代理的机制。
动态代理,顾名思义就是可以在程序运行时动态创建一个代理类来代理我们的对象,每次不管被代理对象如何实现,代理类都无需关心。在Java里面,通过JDK封装的类库,它会在运行时动态生成class字节码加载到内存中,由虚拟机执行,内部也由反射来实现实例化对象,下面一张图清晰的描述了动态代理的原理:
那么JDK具体又是怎么实现的呢?
这里列出两个核心类:InvocationHandler和Proxy,通过这两个类就能很方便的实现我们的动态代理,下面请看代码:
public class InvocationHandlerImpl implements InvocationHandler {
private Object obj;
public InvocationHandlerImpl(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj,args);
}
}
public interface Proxy {
public void doSomething();
}
/**
* 真正被代理的对象
*/
public class ConrectProxy implements Proxy {
@Override
public void doSomething() {
System.out.println("这是真正被代理的对象!");
}
}
public class Client {
public static void main(String[] args) {
ConrectProxy proxy = new ConrectProxy();
InvocationHandler handler = new InvocationHandlerImpl(proxy);
Proxy p = (Proxy)java.lang.reflect.Proxy.newProxyInstance(proxy.getClass().getClassLoader(),proxy.getClass().getInterfaces(),handler);
p.doSomething();
}
}
运行结果:这是真正被代理的对象!
这样就实现了动态代理,我们每次只需要增加被代理对象或被代理对象的方法就行了,无需额外同时增加代理类或代理方法,因为他是动态生成的。但是,JDK动态代理也有一些局限性:1、它是通过interface代理的,所以一个对象要被代理,必须实现接口2、由于它底层是通过反射实现的,所以在运行时性能较差。
那我们能不能有另一种动态代理方式,让程序运行时性能较高,而且不需要接口也能代理呢,那就是CGLib,它是一个强大的可以在程序运行时动态改变类状态的类库,通过它实现动态代理的原理和JDK类似,我直接贴上代码:
public class SayHello {
public void say(){
System.out.println("这是cglib动态代理的被代理对象");
}
}
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz){
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
//实现MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
//通过生成子类的方式创建代理类
SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
proxyImp.say();
}
}
运行结果:这是cglib动态代理的被代理对象
通过cglib轻松实现了动态代理,这样我们代理一个对象的时候无需额外定义一个接口,它的局限性表现在由于它是通过继承实现的,因此无法代理final标识的类和方法。
两种代理对象都有优劣,我们在实际应用场景中应该根据实际情况采用不同的动态代理类库。