动态代理模式

自我修养之动态代理模式

什么是代理?
代理:顾名思义就是代为处理的意思,在日常生活中,代理无处不在,比如说代购,代课等等

如何去使用代理以及分析动态代理是如何实现的,下面我将从静态代理开始分析:

场景:
	陈老师是教语文的,有一天陈老师生病了,没办法就请了隔壁老王去代课

首先来看下代理有哪些角色

在这里插入图片描述

Subject 是顶层接口,RealSubject 是真实对象(被代理对象),Proxy Subject是代理对象,代

理对象持有被代理对象的引用,客户端调用代理对象方法,同时也调用被代理对象的方

法,但是在代理对象前后增加一些处理。

静态代理
/**
 * @ Author     :kwj.
 * @ Date       :Created in 22:24 2020/4/5 0005
 * @ Description:顶层接口
 */
public interface Teacher {
    //教书
    public void doTeach();
    
}
/**
 * @ Author     :kwj.
 * @ Date       :Created in 22:24 2020/4/5 0005
 * @ Description:陈老师上课
 */
public class ChenTeacher implements Teacher {
    @Override
    public void doTeach() {
        System.out.println("上课。。。");
    }
}

由于陈老师生病了,需要找隔壁老王代理。

/**
 * @ Author     :kwj.
 * @ Date       :Created in 22:27 2020/4/5 0005
 * @ Description:隔壁老王去代课
 */
public class GeBiLaoWangTeacher implements Teacher {
    //拿到目标对象的引用
    private Teacher targetTeacher;

    public GeBiLaoWangTeacher(Teacher targetTeacher) {
        this.targetTeacher = targetTeacher;
    }

    //代理老师代课
    @Override
    public void doTeach() {
        System.out.println("课前 大家好  我是代理老师 我叫隔壁老王");
        targetTeacher.doTeach();
        System.out.println("同学们下课了");
    }
}

客户端调用

/**
 * @ Author     :kwj.
 * @ Date       :Created in 22:31 2020/4/5 0005
 * @ Description:客户端调用
 */
public class StaticProxyClient {

    public static void main(String[] args) {
        //拿到目标对象
        Teacher targetTeacher = new ChenTeacher();
        //创建代理对象
        Teacher proxyTeacher = new GeBiLaoWangTeacher(targetTeacher);
        //代理上课
        proxyTeacher.doTeach();

    }
}

测试结果如下:

在这里插入图片描述

看到这里,其实我们可以想到一个问题,就是如果有多个目标对象,对目标对象进行代理就需要不断的创建代理类,这样系统中的类就会太多不易维护,那如何去解决这个问题呢?接下来引入动态代理

动态代理

什么是代理代理?

动态代理是java在运行时通过字节码重组技术动态生成class类的技术

目前有两种实现动态代理的方式

  • JDK 特点:目标类需要实现一个接口,并且代理类需要实现InvocationHandler接口
  • Cglib 原理:是通过继承的方式进行实现的

我们主要讨论使用JDK的方式实现动态代理。为了让代理类可以代理所有的目标对象,我们在代理类中的引用则需要设置为Object类型,如下:

/**
 * @ Author     :kwj.
 * @ Date       :Created in 22:42 2020/4/5 0005
 * @ Description:代理类
 * @ Modified By:
 */
public class DynamicProxy implements InvocationHandler {
    private Object target;
    public Object getProxyInstance(Object target){
        this.target = target;
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //参数一 传入当前类的类加载器
        //参数二 目标类使用的接口(可以是多个)
        //参数三 使用了InvocationHandler 也就是当前类对象
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行目标对象方法前
        before();
        Object result = method.invoke(target, args);
        //执行目标对象方法后
        after();
        return result;
    }

    private void after() {
        System.out.println("after...");
    }

    private void before() {
        System.out.println("before...");
    }
}

客户端进行测试:

/**
 * @ Author     :kwj.
 * @ Date       :Created in 22:51 2020/4/5 0005
 * @ Description:客户端测试类
 * @ Modified By:
 */
public class DynamicProxyTest {

    public static void main(String[] args) {
        DynamicProxy teacherProxy = new DynamicProxy();
        Teacher proxyInstance = (Teacher) teacherProxy.getProxyInstance(new ChenTeacher());
        proxyInstance.doTeach();
    }

}

在这里插入图片描述

ok,到这一步为止,动态代理就算讲完了,可能小伙伴就会有很好奇,这个代理类到底是个什么东西,长什么样子呢?下面我们一探究竟:

我们通过从内存中的对象字节码通过文件流输出到一个新的 class 文件(也就是代理类)

public class DynamicProxyTest {

    public static void main(String[] args) throws IOException {
        DynamicProxy teacherProxy = new DynamicProxy();
        Teacher proxyInstance = (Teacher) teacherProxy.getProxyInstance(new ChenTeacher());
        proxyInstance.doTeach();

        //生成代理类的class文件
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Teacher.class});
        FileOutputStream fos = new FileOutputStream("E:\\$Proxy0.class");
        fos.write(bytes);
        fos.flush();
        fos.close();
    }

}

在E盘下找到这个$Proxy0.class文件,直接拖到idea中进行反编译

public final class $Proxy0 extends Proxy implements Teacher {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void doTeach() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.wukong.pattern.代理模式.静态代理.Teacher").getMethod("doTeach");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到代理类去继承了Proxy并实现了目标类的接口,对目标类中的doTeach进行了重写从而达到代理的目的。

结合Mybatis来看动态代理在框架中的应用

已SqlSession.getMapper(xxx.class)为例,我们知道这里是去生成一个接口的代理类,那么内部到底是如何出来处理的呢?

首先是调用DefaultSqlSession#getMapper,如下:

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

接下来就是到Configuration#getMapper方法

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

MapperRegistry

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

最终就调用到了MapperProxyFactory,顾名思义就是用来生产MapperProxy(即Mapper的代理类)

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
      //标注:mapperProxy就是我们的代理类,那么既然是JDK代理,肯定会实现InvocationHandler
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

如标注部分说明MapperProxy果然实现了InvocationHandler接口

在这里插入图片描述

这样小伙伴们想对动态代理认识是不是又多了一份兴趣呢,欢迎批评指教哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值