手写一个面向接口的动态代理

本文深入探讨了JDK动态代理的实现原理,通过设置VM参数观察生成的代理类源码,揭示了动态代理在方法调用时的处理流程。文章介绍了如何构造和加载动态代理类的源代码,并提供了使用Freemarker模板生成代理类源代码的示例。此外,还展示了动态代理在AOP切面增强中的应用。
摘要由CSDN通过智能技术生成

如题,手写一个面向接口的动态代理。我们需要先了解jdk中的动态代理是怎么实现的。

理解生成的代码和调用过程

设置vm参数,-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,可以使jdk动态生成的class文件输出到磁盘中。

设置vm options

使用下面代码进行调试

public interface IService {
    public void service(String name )throws Exception;
}

public class ServiceImplA implements IService {
    @Override
    public void service(String name) throws Exception {
        System.out.println("ServiceImplA name" + name);
    }
}

public class DynaProxyServiceA implements InvocationHandler {
    private Object object;
    /**
     *   将目标对象关联到InvocationHandler接口,返回代理对象obj
     *   调用代理对象的方法时,都会自动调用invoke方法
     */

    public Object bind(Object object){
        this.object = object;
        return Proxy.newProxyInstance(
                this.object.getClass().getClassLoader(),
                this.object.getClass().getInterfaces(),
                this);
    }

    @SuppressWarnings("unchecked")
    public <T> T bindInterface(Class<T> proxyInterface){
        object = proxyInterface;
        return (T)Proxy.newProxyInstance(
                proxyInterface.getClassLoader(),
                new Class[]{proxyInterface},
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("log start");
        if(object instanceof Class<?>){
            Class<?> clazz = (Class<?>) object;
            for (Method clazzMethod : clazz.getDeclaredMethods()) {
                System.out.println(clazzMethod.getName());
            }
        }
        try{
            result = method.invoke(this.object,args);
            Class<?> returnType = method.getReturnType();
            System.out.println(returnType);
        }catch (Exception e){
            throw e;
        }
        System.out.println("log end");
        return result;
    }
    public static void main(String [] args) throws Exception {
        IService service = (IService)new DynaProxyServiceA()
                        .bind(new ServiceImplA());
        service.service("zhjl");
    }
}

输出结果
log start
ServiceImplA namezhjl
void
log end

运行完程序后,会在项目的根目录生成一个文件夹com.sun.proxy,里面会生成一个$Proxy0.class的代理类文件。

打开文件可以看到以下生成的源代码。

public final class $Proxy0 extends Proxy implements IService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 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 void service(String var1) throws Exception {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (Exception | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("org.example.aop.IService").getMethod("service", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

很容易可以发现,生成的源代码是有规律可寻的。

  • 固定会重写java.lang.Object中的equalstoStringhashCode三个方法
  • 对比静态变量Method的声明对应方法的位置与static代码块这三块地方,可以发现,声明顺序与静态代码块中反射获取的顺序一致,并且在各方法中执行反射出来的Method也是相等的。这里做到了提前加载被代理类的方法,然后使用到该代理类的方法时将被代理类的方法当参数传到h.invoke中执行。

进入到被继承的Proxy中,发现在生成的类中使用的h就是InvocationHandler这个类。即我们在使用动态代理时所要实现才那个类!

image
image.png

所以这个地方是生成一个回调代理类的invoke方法的类,来调用invoke时决定什么时候执行被代理类的service方法就能达到切面增强这个方法的效果

执行代理类的流程图

理解完了生成代码的意义和处理流程,剩下的就是怎么构造这些代码并将他编译成.class文件和被类加载器加载并被创建实例被我们所使用了。

如何构造和加载

参考Proxy.newProxyInstance的代码,看到getProxyClass0,前面的代码忽略,看注释就知道这里是生成指定代理类的方法。直接点进去就好了。

生成代码的方法
image
image
image

记住这两个变量分别是KeyFactoryProxyClassFactory。然后回到proxyClassCache.get(loader, interfaces)

image
image

根据实现的接口数量来返回Key

往下走,最后指向的类都是Factory,并在最后执行get方法。

image

最后回到ProxyClassFactory这个类的apply方法。

image

最后在方法的底部发现ProxyGenerator.generateProxyClass对应的作用就是构造相当于构造.java源文件。defineClass0相当于javac编译成.class文件并loadClass返回对应的类

image

这个生成方法较复杂,经过简单的查看源码,已经知道步骤如下:

  • 构造.java源文件
  • 编译成.class后加载类

笔者的方法比较简单,直接使用freemaker来构造源文件,需要传入以下四个参数。

  • package->生成类所在的包
  • className->生成的代理类名称
  • interface->实现的接口类全类名
  • methodList->需要重写的方法列表

freemaker模板如下

package ${package};
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
/**
* @Author: Jdragon
* @email: 1061917196@qq.com
* @Description: jdk动态代理实质
*/
public class ${className} extends Proxy implements ${interface} {
    public ${className}(InvocationHandler h) {
        super(h);
    }
<#list 0..(methodList!?size-1) as i>
    @Override
    public final ${methodList[i].retType} ${methodList[i].methodName}(
        <#list methodList[i].paramList as param>
                ${param} var${param_index}<#if param_has_next>,</#if>
        </#list>) {
        try {
            <#if (methodList[i].retType!="void")>return (${methodList[i].retType})</#if>
            <#if (methodList[i].paramList?size==0)>
            super.h.invoke(this, m${i}, (Object[])null);
            <#else>
            super.h.invoke(this, m${i}, new Object[]{
                <#list 0..(methodList[i].paramList!?size-1) as k>var${k}
                    <#if k_has_next>,</#if>
                </#list>});
            </#if>
        } catch (RuntimeException | Error e) {
            throw e;
        }catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
</#list>
<#list 0..(methodList!?size-1) as i>
    private static Method m${i};
</#list>
    static{
        try{
            <#list 0..(methodList!?size-1) as i>
                m${i} = Class.forName("${methodList[i].className}").getMethod("${methodList[i].methodName}"
                <#list methodList[i].paramList as param>
                    ,Class.forName("${param}")
                </#list>);
            </#list>
        }  catch (NoSuchMethodException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

主要代码。以下代码不完整,可到gitee获取源码

public class JdkProxyFactory {

    private final static String LN = System.lineSeparator();

    private final static AtomicInteger PROXY_INDEX = new AtomicInteger(0);

    private final static Boolean SAVE_GENERATED_FILES = Boolean.valueOf(System.getProperty("sun.misc.ProxyGenerator.saveGeneratedFiles"));

    private final static String USER_DIR = System.getProperty("user.dir") + "/com/jdragon/proxy/";

    private final static String PACKAGE_NAME = "com.jdragon.proxy";

    public static Object newProxyInstance(ClassLoader classLoader,
                                          @NotNull Class<?>[] interfaces,
                                          @NotNull InvocationHandler h) {
        try {
            if (interfaces.length == 0) {
                throw new Exception("至少要实现一个接口");
            }
            //使用被代理类的类名和自增数定义代理类的名字
            String proxyClass = interfaces[0].getSimpleName() + "$Proxy" + PROXY_INDEX.incrementAndGet();
            //加载代理类
            Class<?> loadClass = loadClass(interfaces[0], proxyClass);
            Constructor<?> constructor = loadClass.getDeclaredConstructor(InvocationHandler.class);
            return constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @Description: 加载类
    **/
    private static Class<?> loadClass(Class<?> interfaces, String proxyClassName) throws Exception {
        String classPath = PACKAGE_NAME + "." + proxyClassName;
        //构建源代码
        String sourceCode = generateSourceCode(interfaces, proxyClassName);
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        try (JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null))) {
            List<JavaFileObject> files = Collections.singletonList(new MemoryJavaFileObject(proxyClassName, sourceCode));
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, files);
            if (!task.call()) {
                throw new Exception("任务调用异常");
            }
            ClassLoader classLoader = manager.getClassLoader(null);
            Class<?> aClass = manager.getClassLoader(null).loadClass(classPath);
            if (SAVE_GENERATED_FILES) {
                save(proxyClassName, classLoader);
            }
            return aClass;
        }
    }

    /**
     * @Description: 构造源代码
    **/
    private static String generateSourceCode(Class<?> interfaces, String proxyClassName) {
        String interfaceName = interfaces.getName();
        List<MethodEntity> methodEntities = new ArrayList<>();
        methodEntities.add(new MethodEntity(Object.class, "toString", null,
                String.class));
        methodEntities.add(new MethodEntity(Object.class, "hashCode", null,
                int.class));
        methodEntities.add(new MethodEntity(Object.class, "equals", Collections.singletonList(Object.class.getName()),
                boolean.class));

        for (Method declaredMethod : interfaces.getDeclaredMethods()) {
            MethodEntity methodEntity = new MethodEntity();
            methodEntity.setClassName(interfaces);
            methodEntity.setMethodName(declaredMethod.getName());
            List<String> params = new ArrayList<>();
            for (Parameter parameter : declaredMethod.getParameters()) {
                String paramTypeName = parameter.getType().getName();
                params.add(paramTypeName);
            }
            methodEntity.setParamList(params);
            methodEntity.setRetType(declaredMethod.getReturnType());
            methodEntity.setTransferType(declaredMethod.getReturnType());
            methodEntities.add(methodEntity);
        }

        //利用定义好的模板传入参数到freemaker进行遍历填充,最后获得源代码
        Map<String, Object> map = new HashMap<>(8);
        map.put("package", PACKAGE_NAME);
        map.put("className", proxyClassName);
        map.put("interface", interfaceName);
        map.put("methodList", methodEntities);
        FreeMakerUtil freeMakerUtil = new FreeMakerUtil("/template/freemaker/", "ftl");
        return freeMakerUtil.printString("proxy", map);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值