概述
代理模式:通过某种方式给某个对象提供一个代理对象,在不改变原有对象代码的前提下对方法的增强。
为什么要使用代理模式?
在项目中如果需要打印方法入参及出参时、需要记录方法执行时间时、需要验证权限时、需要统一异常处理时等等各种场景,基本上都是通过拦截器和过滤器来实现。 这些拦截器过滤器的底层实现其实都是使用了代理模式。
静态代理
静态代理其实就是在程序运行之前,提前写好被代理方法的代理类,编译后运行。
1 2 3 4 5 6 7 8
| public interface MainService { void doSomeThing(); } public class MainServiceImpl implements MainService { public void doSomeThing() { System.out.println("doSomeThing......"); } }
|
如果想在 dosomeTime
方法前后做一些动作,比如记录方法执行时间,我们一般会这么做:
1 2 3 4 5
| public void doSomeThing() { System.out.println("begin time:"+System.currentTimeMillis()); mainService.doSomeThing(); System.out.println("end time:"+System.currentTimeMillis()); }
|
只是这样就改变了原有类的代码结构,这个时候我们就可以用静态代理来解决:
1 2 3 4 5 6 7 8 9 10 11
| public class StaticProxy implements MainService { private MainService mainService; public StaticProxy(MainService mainService){ this.mainService=mainService; } public void doSomeThing() { System.out.println("begin time:"+System.currentTimeMillis()); mainService.doSomeThing(); System.out.println("end time:"+System.currentTimeMillis()); } }
|
这里创建的了一个代理类,代理类持有原对象,把所有新增的需求放到代理类中,这样就不需要修改代码了。 我们可以使用如下代码测试:
1 2 3 4 5
| public static void main (String args[]){ MainService mainService=new MainServiceImpl(); MainService staticProxy=new StaticProxy(mainService); staticProxy.doSomeThing(); }
|
使用静态代理虽然解决了不修改代码的需求,但是如果原对象有多个方法的话就必须全部实现且加上打印的逻辑
动态代理
动态代理主要是通过反射机制,在运行时动态生成所需代理的class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class DynamicProxy { private MainService mainService; public DynamicProxy(MainService mainService){ this.mainService=mainService; }
public Object getProxy() { return Proxy.newProxyInstance( mainService.getClass().getClassLoader(), mainService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("begin time:"+System.currentTimeMillis()); method.invoke(mainService, args); System.out.println("end time:"+System.currentTimeMillis()); return null; } }); } }
|
可以看到上方的getProxy方法是返回的一个代理对象,切是在这个对象的所有方法执行前后都执行了打印执行时间的逻辑 看一下测试代码:
1 2 3 4 5
| public static void main (String args[]){ MainService mainService=new MainServiceImpl(); DynamicProxy dynamicProxy=new DynamicProxy(mainService); ((MainService)dynamicProxy.getProxy()).doSomeThing(); }
|
关于动态代理设及到一些工具类和接口,比如:InvocationHandler接口
在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,这个中介类被要求实现InvocationHandler接口,这个接口的定义如下:
1 2 3 4 5 6
|
public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args); }
|
动态代理不会直接让代理类去调用委托类,实际上是通过调用中介类的 invoke
方法,由invoke决定调用 proxy 哪个委托类。真正的代理内容也是在中介类里定义。
Proxy.newProxyInstance(ClassLoader classloader,Class aclass[],InvocationHandler invocationhandler)方法会生成一个一个对中介类的代理类。
动态代理实现的已经非常优雅了,但是它还是有个缺点,那就是想要实现代理的原对象必须具有顶层接口,对没有实现的接口的类就无能为力了。
Cglib代理
cglib是使用字节码技术直接生成一个子类然后重写父类的方法:
1 2 3 4 5 6 7 8 9 10
| public class CglibInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("begin time:"+System.currentTimeMillis()); Object object = methodProxy.invokeSuper(obj, objects); System.out.println("end time:"+System.currentTimeMillis()); return object; } }
|
看一下测试代码:
1 2 3 4 5 6 7
| public static void main (String args[]){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MainServiceImpl.class); enhancer.setCallback(new CglibInterceptor()); MainService proxy= (MainService)enhancer.create(); proxy.doSomeThing(); }
|
代理对象的生成过程由Enhancer类实现,大概步骤如下:
- 生成代理类Class的二进制字节码;
- 通过Class.forName加载二进制字节码,生成Class对象;
- 通过反射机制获取实例构造,并初始化代理类对象。
总结
DK是基于反射机制,生成一个实现代理接口的匿名类,然后重写方法,实现方法的增强.它生成类的速度很快,但是运行时因为是基于反射,调用后续的类操作会很慢。只能对实现了接口的类生成代理,而不能针对类。因为生成的代理类无法强行转换为被代理类。而由于java是单继承,代理类已经继承了Proxy类,所以只能用接口。
CGLIB是基于继承机制,继承被代理类,所以方法不要声明为final,然后重写父类方法达到增强了类的作用.它底层是基于asm第三方框架,是对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理.生成类的速度慢,但是后续执行类的操作时候很快.