深入浅出RPC---3、动态代理-灵析社区

提笔写架构

动态代理流程

内部接口如何调用实现

RPC的调用内部核心技术采用的就是动态代理。

JDK动态代理的如何实现?

示例代码:

package com.rpc.sample.proxy;

import sun.misc.ClassLoaderUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy {

    /**
     * 定义用户的接口
     */
    public interface User {
        String job();
    }

    /**
     * 实际的调用对象
     */
    public static class Teacher  {

        public String invoke(){
            return "i'm a Teacher";
        }
    }

    /**
     * 创建JDK动态代理类
     */
    public static class JDKProxy implements InvocationHandler {
        private Object target;

        JDKProxy(Object target) {
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] paramValues) {
            return ((Teacher)target).invoke();
        }
    }

    public static void main(String[] args){
        // 构建代理器
        JDKProxy proxy = new JDKProxy(new Teacher());
        ClassLoader classLoader = JDKDynamicProxy.class.getClassLoader();
        // 生成代理类
        User user = (User) Proxy.newProxyInstance(classLoader, new Class[]{User.class}, proxy);
        // 接口调用
        System.out.println(user.job());
    }
}

JDK动态代理的实现原理:


JDK内部如何处理?

代理类 $Proxy里面会定义相同签名的接口,然后内部会定义一个变量绑定JDKProxy代理对象,当调用 User.job接口方法,实质上调用的是JDKProxy.invoke()方法。

为什么要加入动态代理?

第一, 缺点: 不便于管理,不利于扩展维护。

第二, 优点: 可以做到拦截,添加其他额外功能。

动态代理开源技术

1.Cglib 动态代理

Cglib是一个强大的、高性能的代码生成包。

  1. Javassist 动态代理 一个开源的分析、编辑和创建Java字节码的类库,dubbo内部动态代理采用Javassist 。
    引入maven库:
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.21.0-GA</version>
        </dependency>

示例代码:

public interface ProxyFactory {

    <T> T getProxy(Object target, InvocationHandler handler) throws Throwable;
}
import com.bytebeats.codelab.javassist.proxy.ProxyFactory;
import java.lang.reflect.InvocationHandler;

public class JavassistProxyFactory implements ProxyFactory {

    @Override
    public <T> T getProxy(Object target, InvocationHandler handler) throws Throwable {
        return (T) ProxyGenerator.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                target.getClass(), handler);
    }
}
package com.bytebeats.codelab.javassist.proxy.javassist;

import com.bytebeats.codelab.javassist.util.ClassUtils;
import javassist.*;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class ProxyGenerator {

    private static final AtomicInteger counter = new AtomicInteger(1);

    private static ConcurrentHashMap<Class<?>, Object> proxyInstanceCache = new ConcurrentHashMap<>();

    public static Object newProxyInstance(ClassLoader classLoader, Class<?> targetClass, InvocationHandler invocationHandler)
            throws Exception {

        if(proxyInstanceCache.containsKey(targetClass)){
            return proxyInstanceCache.get(targetClass);
        }

        ClassPool pool = ClassPool.getDefault();

        //生成代理类的全限定名
        String qualifiedName = generateClassName(targetClass);
        // 创建代理类
        CtClass proxy = pool.makeClass(qualifiedName);

        //接口方法列表
        CtField mf = CtField.make("public static java.lang.reflect.Method[] methods;", proxy);
        proxy.addField(mf);

        CtField hf = CtField.make("private " + InvocationHandler.class.getName() + " handler;", proxy);
        proxy.addField(hf);

        CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get(InvocationHandler.class.getName())}, proxy);
        constructor.setBody("this.handler=$1;");
        constructor.setModifiers(Modifier.PUBLIC);
        proxy.addConstructor(constructor);

        proxy.addConstructor(CtNewConstructor.defaultConstructor(proxy));

        // 获取被代理类的所有接口
        List<Class<?>> interfaces = ClassUtils.getAllInterfaces(targetClass);

        List<Method> methods = new ArrayList<>();
        for (Class cls : interfaces) {
            CtClass ctClass = pool.get(cls.getName());
            proxy.addInterface(ctClass);

            Method[] arr = cls.getDeclaredMethods();
            for (Method method : arr) {
                int ix = methods.size();
                Class<?> rt = method.getReturnType();
                Class<?>[] pts = method.getParameterTypes();

                StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
                for(int j=0;j<pts.length;j++) {
                    code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
                }
                code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
                if(!Void.TYPE.equals(rt) )
                    code.append(" return ").append(asArgument(rt, "ret")).append(";");

                StringBuilder sb = new StringBuilder(1024);
                sb.append(modifier(method.getModifiers())).append(' ').append(getParameterType(rt)).append(' ').append(method.getName());
                sb.append('(');
                for(int i=0;i<pts.length;i++)
                {
                    if( i > 0 )
                        sb.append(',');
                    sb.append(getParameterType(pts[i]));
                    sb.append(" arg").append(i);
                }
                sb.append(')');

                Class<?>[] ets = method.getExceptionTypes();    //方法抛出异常
                if( ets != null && ets.length > 0 )
                {
                    sb.append(" throws ");
                    for(int i=0;i<ets.length;i++)
                    {
                        if( i > 0 )
                            sb.append(',');
                        sb.append(getParameterType(ets[i]));
                    }
                }
                sb.append('{').append(code.toString()).append('}');

                CtMethod ctMethod = CtMethod.make(sb.toString(), proxy);
                proxy.addMethod(ctMethod);

                methods.add(method);
            }
        }

        proxy.setModifiers(Modifier.PUBLIC);

        Class<?> proxyClass = proxy.toClass(classLoader, null);
        proxyClass.getField("methods").set(null, methods.toArray(new Method[0]));

        Object instance = proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
        Object old = proxyInstanceCache.putIfAbsent(targetClass, instance);
        if(old!=null){
            instance = old;
        }
        return instance;
    }

    private static String modifier(int mod) {
        if( Modifier.isPublic(mod) ) return "public";
        if( Modifier.isProtected(mod) ) return "protected";
        if( Modifier.isPrivate(mod) ) return "private";
        return "";
    }

    /**
     * 数组类型返回 String[]
     * @param c
     * @return
     */
    public static String getParameterType(Class<?> c) {
        if(c.isArray()) {   //数组类型
            StringBuilder sb = new StringBuilder();
            do {
                sb.append("[]");
                c = c.getComponentType();
            } while( c.isArray() );

            return c.getName() + sb.toString();
        }
        return c.getName();
    }

    private static String asArgument(Class<?> cl, String name) {
        if( cl.isPrimitive() ) {
            if( Boolean.TYPE == cl )
                return name + "==null?false:((Boolean)" + name + ").booleanValue()";
            if( Byte.TYPE == cl )
                return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
            if( Character.TYPE == cl )
                return name + "==null?(char)0:((Character)" + name + ").charValue()";
            if( Double.TYPE == cl )
                return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
            if( Float.TYPE == cl )
                return name + "==null?(float)0:((Float)" + name + ").floatValue()";
            if( Integer.TYPE == cl )
                return name + "==null?(int)0:((Integer)" + name + ").intValue()";
            if( Long.TYPE == cl )
                return name + "==null?(long)0:((Long)" + name + ").longValue()";
            if( Short.TYPE == cl )
                return name + "==null?(short)0:((Short)" + name + ").shortValue()";
            throw new RuntimeException(name+" is unknown primitive type.");
        }
        return "(" + getParameterType(cl) + ")"+name;
    }

    private static String generateClassName(Class<?> type){

        return String.format("%s$Proxy%d", type.getName(), counter.getAndIncrement());
    }
}

  1. Byte Buddy 字节码增强库 Byte Buddy是致力于解决字节码操作和 简化操作复杂性的开源框架。

几种动态代理性能比较:
Byte Buddy > Javassist > CGLIB > JDK

阅读量:2016

点赞量:0

收藏量:0