首先我们要区分两个含义上的静态代理和动态代理。
从设计模式上来讲:静态代理和动态代理都属于代理模式。
静态代理模式下,代码本身的适用性受到局限,我们可以说是一种"硬编码"的方式:代理类通过构造函数引用**特定的被代理的类
**,并在执行被代理类的方法时执行一些代理逻辑,达到扩展的目的,因此静态代理的扩展成本较高,不够具有通用性,而且一定程度的通用必须依赖接口。
为了解决静态代理的通用性问题,产生了动态代理模式,动态代理的实现底层我们不会自己去写,通常是使用已有的动态代理技术,在Java生态中,目前普遍使用的是JDK自带的代理和CGLib提供的类库。
代码示例
public class JDKProxy implements InvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson person) {
this.target = person;
Class<? extends IPerson> targetClass = target.getClass();
return (IPerson) Proxy.newProxyInstance(targetClass.getClassLoader(),targetClass.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.before();
Object invoke = method.invoke(this.target, args);
this.after();
return invoke;
}
private void before() {
log.info("洗手");
}
private void after() {
log.info("收拾");
}
}
public class LiSiBaby implements IPerson{
@Override
public void eat() {
log.info("大口吃饭");
}
}
测试
JDKProxy jdkProxy = new JDKProxy();
IPerson instance = jdkProxy.getInstance(new LiSiBaby());
instance.eat();
/**
* design_model.proxy_model.JDKProxy - 洗手
* design_model.proxy_model.LiSiBaby - 大口吃饭
* design_model.proxy_model.JDKProxy - 收拾
*/
可见JDK动态代理的强大之处在于只要规范了接口定义,就可以动态扩展所有实现类,而不需要每个去单独编码。
代码示例
public class CGlibProxy implements MethodInterceptor {
public Object getInstance(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
this.before();
Object invokeSuper = methodProxy.invokeSuper(o, objects);
this.after();
return invokeSuper;
}
private void before() {
log.info("洗手");
}
private void after() {
log.info("收拾");
}
}
测试
CGlibProxy cGlibProxy = new CGlibProxy();
LiSiBaby instance = (LiSiBaby) cGlibProxy.getInstance(LiSiBaby.class);
instance.eat();
/**
* design_model.proxy_model.CGlibProxy.CGlibProxy - 洗手
* design_model.proxy_model.LiSiBaby - 大口吃饭
* design_model.proxy_model.CGlibProxy.CGlibProxy - 收拾
*/
当然了,CGlib并不依赖接口,你可以传入任意对象,并对其进行扩展。
(1)JDK动态代理实现了被代理对象的接口,CGLib动态代理继承了被代理对象。
(2)JDK动态代理和CGLib动态代理都在运行期生成字节码,JDK动态代理直接写Class字节码,CGLib动态代理使用ASM框架写Class字节码。CGLib动态代理实现更复杂,生成代理类比JDK动态代理效率低。
(3)JDK动态代理调用代理方法是通过反射机制调用的,CGLib动态代理是通过FastClass机制(索引分配直接调用)直接调用方法的,因此CGLib动态代理的执行效率更高。
开发中,我们不必太过纠结性能问题,可以根据自己的实际需求灵活选择。