代理模式:通过某种方式给某个对象提供一个代理对象,在不改变原有对象代码的前提下对方法的增强。

在Java中我们最熟悉的使用场景就是SpringAOP,本篇文章即是SpringAOP源码分析的前置文章

为什么要使用代理模式

首先我们知道,在项目中如果需要打印方法入参及出参时、需要记录方法执行时间时、需要验证权限时、需要统一异常处理时等等各种场景是不是都是使用拦截器呀过滤器呀啥的。
这些拦截器过滤器的底层实现其实都是使用了代码模式

本篇文章就基于一个打印方法执行时间的小demo来简单了解一下代理模式的使用

静态代理

首先有一个接口和一个实现类

  1. public interface MainService {
  2. void doSomeThing();
  3. }
  4. public class MainServiceImpl implements MainService {
  5. public void doSomeThing() {
  6. System.out.println("doSomeThing......");
  7. }
  8. }

当我们想要知道doSomeThing方法的执行时间时比较low的解决方案可能就是这样搞

  1. public void doSomeThing() {
  2. System.out.println("begin time:"+System.currentTimeMillis());
  3. mainService.doSomeThing();
  4. System.out.println("end time:"+System.currentTimeMillis());
  5. }

但是这样就不能实现不修改代码就处理问题的初衷了,这个时候就可以使用静态代理来解决

  1. public class StaticProxy implements MainService {
  2. private MainService mainService;
  3. public StaticProxy(MainService mainService){
  4. this.mainService=mainService;
  5. }
  6. public void doSomeThing() {
  7. System.out.println("begin time:"+System.currentTimeMillis());
  8. mainService.doSomeThing();
  9. System.out.println("end time:"+System.currentTimeMillis());
  10. }
  11. }

这里创建的了一个代理类,代理类持有原对象,把所有新增的需求放到代理类中,这样就不需要修改代码了。
我们可以使用如下代码测试

  1. public static void main (String args[]){
  2. MainService mainService=new MainServiceImpl();
  3. MainService staticProxy=new StaticProxy(mainService);
  4. staticProxy.doSomeThing();
  5. }

动态代理

上方使用静态代理虽然解决了不修改代码的需求,但是如果原对象有多个方法的话就必须全部实现且加上打印的逻辑,这样就有点不太优雅了吧
这个时候就到了动态代理出场的时候了

  1. public class DynamicProxy {
  2. private MainService mainService;
  3. public DynamicProxy(MainService mainService){
  4. this.mainService=mainService;
  5. }
  6. public Object getProxy() {
  7. return Proxy.newProxyInstance(
  8. mainService.getClass().getClassLoader(),
  9. mainService.getClass().getInterfaces(),
  10. new InvocationHandler() {
  11. @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. System.out.println("begin time:"+System.currentTimeMillis());
  14. method.invoke(mainService, args);
  15. System.out.println("end time:"+System.currentTimeMillis());
  16. return null;
  17. }
  18. });
  19. }
  20. }

可以看到上方的getProxy 方法是返回的一个代理对象,切是在这个对象的所有方法执行前后都执行了打印执行时间的逻辑
看一下测试代码

  1. public static void main (String args[]){
  2. MainService mainService=new MainServiceImpl();
  3. DynamicProxy dynamicProxy=new DynamicProxy(mainService);
  4. ((MainService)dynamicProxy.getProxy()).doSomeThing();
  5. }

这样就优雅的多了

Cglib代理

动态代理实现的已经非常优雅了,但是它还是有个缺点,那就是想要实现代理的原对象必须具有顶层接口,对没有实现的接口的类就无能为力了。

不过记住一句话,方法总比困难多。对于这种没有接口的类使用cglib代理就可以解决它。
与动态代理创建一个代理类不同的是cglib是使用字节码技术直接生成一个子类然后重写父类的方法

  1. public class CglibInterceptor implements MethodInterceptor {
  2. public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  3. System.out.println("begin time:"+System.currentTimeMillis());
  4. Object object = methodProxy.invokeSuper(obj, objects);
  5. System.out.println("end time:"+System.currentTimeMillis());
  6. return object;
  7. }
  8. }

看一下测试代码

  1. public static void main (String args[]){
  2. Enhancer enhancer = new Enhancer();
  3. enhancer.setSuperclass(MainServiceImpl.class);
  4. enhancer.setCallback(new CglibInterceptor());
  5. MainService proxy= (MainService)enhancer.create();
  6. proxy.doSomeThing();
  7. }