前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你做过代理吗?

你做过代理吗?

原创
作者头像
简单的程序员
修改2021-03-15 10:09:16
2550
修改2021-03-15 10:09:16
举报
文章被收录于专栏:奕仁专栏奕仁专栏奕仁专栏

今天是公历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,内部也是这样子,它的使用很方便,即创建一个接口,然后用方法调用即可…懂行的小伙伴一看这种模式就知道肯定是用了动态代理了

forest
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生成的代理类

如果想看jdk帮我们生成的代理类,应该咋做呢?

其实不用背,这种网上都很多,添加一个vm的环境变量

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

这个命令什么意思,从字面意思上看就是调用ProxyGenerator类的saveGeneratedFiles字段为true

image.png
image.png

然后追踪到sun包下的源码,如上图,其实就是生成以“.class”结尾的代理类,这种就是我们说的动态编译, 通过动态生成二进制码然后让vm运行

当然,更底层的我也没看过,所以点到为止!

好了,接着上面的那个例子继续说,当我们配置了上面生成代理类的配置之后,然后运行main方法,得到下面这张截图

image.png
image.png

然后看到我们的项目生成一个目录——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整理了一张图,如下

image.png
image.png

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动态代理的使用
  • 自己实现一个代理
  • 查看jdk生成的代理类
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档