今天是公历2021年3月14日,难得一遇的日子(201314——爱你一生一世),今天你们有没有出去玩(单身狗的我只能窝在家里撸代码),不过算下来,今天也省了不少钱~(心理mmp)
这是一条优雅的分割线===========================splitter
言归正传,今天早上起床看了一会儿大牛写的代码,一款知名的http框架(forest), 大概加调试看了一会儿,写的确实棒,为作者点个赞!
当然,今天的主题是代理模式,代理模式在项目中或多或少都会用到,如果自己没用过,那你所用的框架底层几乎都用过,在这里随便举几个例子
例如 spring aop底层
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
//如果实现了接口 则创建jdk代理 否则创建cglib
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
再如 Mybatis:
MapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
//为每个mapper.java 生成一个代理
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
等等etc……
但是最近看到了一款http 框架 forest,内部也是这样子,它的使用很方便,即创建一个接口,然后用方法调用即可…懂行的小伙伴一看这种模式就知道肯定是用了动态代理了
public T createInstance() {
//为接口创建代理
synchronized (configuration.getInstanceCache()) {
...
//创建代理
instance = (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass, ForestClientProxy.class}, interfaceProxyHandler);
if (cacheEnabled) {
configuration.getInstanceCache().put(interfaceClass, instance);
}
return instance;
}
}
调用方法 即调用invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ("toString".equals(methodName) && (args == null || args.length == 0)) {
return "{Forest Proxy Object of " + interfaceClass.getName() + "}";
}
//读取缓存的方法 并调用
ForestMethod forestMethod = forestMethodMap.get(method);
return forestMethod.invoke(args);
}
public class ProxyHandler <T> implements InvocationHandler {
private Class<T> tClass;
//获取需要代理的接口对象 即你调用该接口的任何方法都会被拦截
public ProxyHandler(Class<T> tClass){
this.tClass = tClass;
}
//创建动态代理
public <T> T createProxy(){
return(T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, this);
}
//执行invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(args));
//执行其他逻辑 todo
return args;
}
public static void main(String[] args) throws Exception{
ProxyHandler<Food> foodProxyHandler = new ProxyHandler<>(Food.class);
Food proxy = foodProxyHandler.createProxy();
proxy.eat("下午茶");
}
}
至此,动态代理就模拟完了,有没有很简单~当然底层就很复杂了,包含了动态类的生成,这些都是jdk帮我们生成的,如果想看jdk帮我们生成的类,可以继续往下看!!!
如果想看jdk帮我们生成的代理类,应该咋做呢?
其实不用背,这种网上都很多,添加一个vm的环境变量
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
这个命令什么意思,从字面意思上看就是调用ProxyGenerator类的saveGeneratedFiles字段为true
然后追踪到sun包下的源码,如上图,其实就是生成以“.class”结尾的代理类,这种就是我们说的动态编译, 通过动态生成二进制码然后让vm运行
当然,更底层的我也没看过,所以点到为止!
好了,接着上面的那个例子继续说,当我们配置了上面生成代理类的配置之后,然后运行main方法,得到下面这张截图
然后看到我们的项目生成一个目录——com.sun.proxy
底下生成了一个如上图同名同姓的类——$Proxy0
然后点开这个class类,进行反编译,代码我贴到下面:
package com.sun.proxy;
import com.example.demo.proxy.Food;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//显而易见,它集成了Proxy类并实现了Food接口
//为啥要集成Proxy类呢? 答案就在它用到了Proxy基类的InvocationHandler这个对象,
//对咯,这个对象它就是我们配置的代理回调接口
public final class $Proxy0 extends Proxy implements Food {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
//上面例子diamante实现了InvocationHandler,这里自然可以直接当做调用本地方法一样调用它了
public final void eat(String var1) throws {
try {
//实际上这段代码就是调用 InvocationHandler的实现类的invoke方法
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
static {
try {
m4 = Class.forName("com.example.demo.proxy.Food").getMethod("eat", Class.forName("java.lang.String"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
那么,我们现在应该知道是咋回事了吧?
我把这个用processOn整理了一张图,如下
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。