Java的代理模式
分为静态代理和动态代理。
在客户类不能或不想直接引用委托类,代理类可以起到中介的作用。类似于我想买车,但我不想去做市场调查学习车配置知识以及购车后的办证交税等一系列行为,那我可以委托中介。这个中介就是代理类,买车这件事则是委托类。
代理模式符合代码的开闭原则,可以增加委托类的功能。例如买车事件中,中介可以帮我在买车前,收集各种车辆信息做对比;买车后,帮我办行驶证和缴纳购置税。这就是买车的额外功能。
放在代码上,可以是给业务类实现缓存、日志等功能。
静态代理:直接用代码创建或者使用工具生成源代码,然后编译。在程序运行之前,代理类.class已经创建。
动态代理:在程序运行时通过反射动态创建。
静态代理,写一个服务接口(买车),实现服务接口的服务类(买车),创建代理类(买车中间),代理类实现服务接口,重写买车方法,在买车方法内写好买车前的准备,然后调用服务类的买车方法,最后写买车后的行为。
优点:符合开闭原则,对目标类进行功能扩展。
缺点:每一个目标类都需要创建一个代理类,工作量大,不易管理。接口改变,代理类也要变。
动态代理:不需要手动创建代理类,只需要编写一个动态处理器即可。
JDK动态代理:创建服务类接口,实现服务接口,编写动态处理器。代理对象会在JDK运行时动态创建。
public class DynamicProxyHandler implements InvocationHandler { private Object object; public DynamicProxyHandler(final Object object) { this.object=object; } @Override public Object invoke(Object proxy, Method method,Object[] args) throws Throwable { System.out.println("买车前准备"); Object result = method.invoke(object,args); System.out.println("买车后购置税"); return result; } }
调用:
BuyCar buyCar =new BuyCarImpl(); BuyCar proxyBuyCar = (BuyCar) Proxy.newProxyInstance( BuyCar.class.getClassLoader(), new Class[]{BuyCar.class}, new DynamicProxyHandler(buyCar) ); proxyBuyCar.buyCar();
Proxy是所有动态生成代理的共同父类,这个类有一个静态方法Proxy.newProxyInstance(),调用时动态创建代理类,接受三个参数:
·ClassLoader loader:指定当前目标对象的类加载器,获得加载器的方法是固定的
·Class<?>[] interfaces:指定目标对象实现的接口类型(可以多个,所以用数组),用泛型确认类型。
·InvocationHandler:指定动态处理器,如上述实现InvocationHandler接口的类,调用代理对象时会执行动态处理器中的invoke方法。
优点:相比静态代理,JDK动态代理无需创建为每个服务类创建代理类,只需要调用时使用Proxy类动态生成代理类。
缺点:Proxy是所有动态生成的代理类共同的父类,Java不支持多继承,只能通过接口的形式实现。JDK动态代理生成的代理类强制继承了java.lang.reflect.Proxy类,所以无法继承目标业务类,所以只能通过实现目标业务类的接口来间接代理。
CGLib动态代理:
对于没有接口的类,就不适用JDK动态代理了。CGLib采用字节码技术,通过底层字节码创建一个子类,并在子类中采用拦截的方式拦截所有父类方法的调用,再插入拦截逻辑。因为需要创建子类,所以不能使用final修饰的类进行代理(final修饰的类无法被继承)。
JDK动态代理和CGLib动态代理是实现Spring AOP的基础。
创建服务类(类,不是接口):
public class BuyCar2 { public void buyCar() { System.out.println("我要买车"); } }
创建CGLib代理类:
public class CgLibProxy implements MethodInterceptor { private Object target; public CgLibProxy(Object target) { this.target = target; } public Object getProxyInstance(){ Enhancer enhancer =new Enhancer();//工具类 enhancer.setSuperclass(target.getClass());//设置父类 enhancer.setCallback(this);//设置回调函数 return enhancer.create();//创建子类(代理对象) } public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("买车前准备"); Object result = method.invoke(target,args); System.out.println("买车后购置税"); return result; } }
最后调用:
BuyCar2 target new BuyCar2(); CgLibProxy cgLibProxy =new CgLibProxy(target); BuyCar2 buyCarCglibProxy =(BuyCar2) cgLibProxy.getProxyInstance(); buyCarCglibProxy.buyCar();
CGLib创建的动态代理对象要比JDK动态代理对象的性能更高。但是CGLib创建代理对象所需的时间要比JDK长得多。所以对于单例对象(无需频繁创建对象)则使用CGLib更合适。反之使用JDK方法。
CGLib是动态创建子类的方法,因此无法代理final修饰的方法。(static方法也不行,因为static方法不属于实例,不能被覆盖。私有方法也不行,因为子类无法访问私有方法)
浙公网安备 33010602011771号