自我修养之动态代理模式
什么是代理?
代理:顾名思义就是代为处理的意思,在日常生活中,代理无处不在,比如说代购,代课等等
如何去使用代理以及分析动态代理是如何实现的,下面我将从静态代理开始分析:
场景:
陈老师是教语文的,有一天陈老师生病了,没办法就请了隔壁老王去代课
首先来看下代理有哪些角色
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接口
这样小伙伴们想对动态代理认识是不是又多了一份兴趣呢,欢迎批评指教哦