反射真的慢么?動(dòng)態(tài)代理會(huì)創(chuàng)建很多臨時(shí)class?

問(wèn)題
1.反射真的慢么?
2.動(dòng)態(tài)代理會(huì)創(chuàng)建很多臨時(shí)class?
3.屬性通過(guò)反射讀取怎么實(shí)現(xiàn)的?
當(dāng)我們?cè)贗DE中編寫(xiě)代碼的時(shí)候,打一個(gè)點(diǎn)號(hào),IDE會(huì)自動(dòng)彈出對(duì)應(yīng)的屬性和方法名,當(dāng)我們?cè)赿ebug的時(shí)候,IDE會(huì)將方法運(yùn)行時(shí)方法內(nèi)局部變量和外部實(shí)例上屬性的值都展示出來(lái),spring中的IOC和AOP,以及一個(gè)RPC框架中,我們反序列化,consumer的代理,以及provider的調(diào)用都會(huì)用到j(luò)ava的反射功能,有人說(shuō)使用反射會(huì)慢,那么到底慢在哪里呢?

成都創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的城口網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

反射
反射使JAVA語(yǔ)言有了動(dòng)態(tài)編譯的功能,也就是在我們編碼的時(shí)候不需要知道對(duì)象的具體類(lèi)型,但是在運(yùn)行期可以通過(guò)Class.forName()獲取一個(gè)類(lèi)的class對(duì)象,在通過(guò)newInstance獲取實(shí)例。

先看下java.lang.reflect包下的幾個(gè)主要類(lèi)的關(guān)系圖,當(dāng)然動(dòng)態(tài)代理的工具類(lèi)也在該包下。
反射真的慢么?動(dòng)態(tài)代理會(huì)創(chuàng)建很多臨時(shí)class?
AnnotatedElement
作為頂級(jí)接口,這個(gè)接口提供了獲取注解相關(guān)的功能,我們?cè)诜椒ǎ?lèi),屬性,構(gòu)造方法上都可以加注解,所以下面的Field,Method,Constructor都有實(shí)現(xiàn)這個(gè)接口,以下是我們經(jīng)常用的兩個(gè)方法,jdk8以后,接口里面可以通過(guò)default修飾方法實(shí)現(xiàn)了

Annotation[] getAnnotations(); //獲取目標(biāo)對(duì)象(方法和屬性)上的所有注解
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
     Objects.requireNonNull(annotationClass);
     // Loop over all directly-present annotations looking for a matching one
     for (Annotation annotation : getDeclaredAnnotations()) {
         if (annotationClass.equals(annotation.annotationType())) {
             // More robust to do a dynamic cast at runtime instead
             // of compile-time only.
             return annotationClass.cast(annotation);
         }
     }
     return null;
 }

GenericDeclaration
提供了獲取泛型相關(guān)的功能,只有方法和構(gòu)造方法上支持泛型,所以只有Method,Constructor實(shí)現(xiàn)了該接口

Member
作為一個(gè)對(duì)象內(nèi)部方法和屬性的聲明的抽象,包含了名稱(chēng),修飾符,所在的類(lèi),其中修飾符包含了 static final public private volatile 等,通過(guò)一個(gè)整數(shù)表示,每一個(gè)類(lèi)型在二進(jìn)制中占一個(gè)位.

public Class<?> getDeclaringClass();
public String getName();
public int getModifiers();

以下為Modifier類(lèi)部分代碼

public static final int PUBLIC           = 0x00000001;
public static final int PRIVATE          = 0x00000002;
public static final int PROTECTED        = 0x00000004;
public static final int STATIC           = 0x00000008;
public static final int FINAL            = 0x00000010;
public static final int SYNCHRONIZED     = 0x00000020;
public static final int VOLATILE         = 0x00000040;
public static final int TRANSIENT        = 0x00000080;
public static final int NATIVE           = 0x00000100;
public static final int INTERFACE        = 0x00000200;
public static final int ABSTRACT         = 0x00000400;
public static final int STRICT           = 0x00000800;
public static boolean isPublic(int mod) {
    return (mod & PUBLIC) != 0;
}

AccessibleObject

這是一個(gè)類(lèi),提供了權(quán)限管理的功能,例如是否允許在反射中在外部調(diào)用一個(gè)private方法,獲取一個(gè)private屬性的值,所以method,constructor,field都繼承該類(lèi),下面這段代碼展示了如何在反射中訪問(wèn)一個(gè)私有的成員變量,class對(duì)象的構(gòu)造方法不允許對(duì)外.

private static void setAccessible0(AccessibleObject obj, boolean flag)
    throws SecurityException
{
    if (obj instanceof Constructor && flag == true) {
        Constructor<?> c = (Constructor<?>)obj;
        if (c.getDeclaringClass() == Class.class) {
            throw new SecurityException("Cannot make a java.lang.Class" +
                                        " constructor accessible");
        }
    }
    obj.override = flag;
}

boolean override;

public boolean isAccessible() {
    return override;
}

以下為 Field里面通過(guò)field.get(原始對(duì)象)獲取屬性值得實(shí)現(xiàn),先通過(guò)override做校驗(yàn),如果沒(méi)有重載該權(quán)限,則需要校驗(yàn)訪問(wèn)權(quán)限

public Object get(Object obj)
    throws IllegalArgumentException, IllegalAccessException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    return getFieldAccessor(obj).get(obj);
}

下面我們看看如何通過(guò)反射修改Field里面屬性的值

通過(guò)上面的代碼,我們可以看出jdk將Field屬性的讀取和寫(xiě)入委托給FieldAccessor,那么如何獲取FieldAccessor呢

class UnsafeFieldAccessorFactory {
    UnsafeFieldAccessorFactory() {
    }

    static FieldAccessor newFieldAccessor(Field var0, boolean var1) {
        Class var2 = var0.getType();
        boolean var3 = Modifier.isStatic(var0.getModifiers());
        boolean var4 = Modifier.isFinal(var0.getModifiers());
        boolean var5 = Modifier.isVolatile(var0.getModifiers());
        boolean var6 = var4 || var5;
        boolean var7 = var4 && (var3 || !var1);

        if (var3) {
            UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());

            return (FieldAccessor) ((!var6)
            ? ((var2 == Boolean.TYPE)
            ? new UnsafeStaticBooleanFieldAccessorImpl(var0)
            : ((var2 == Byte.TYPE)
            ? new UnsafeStaticByteFieldAccessorImpl(var0)
            : ((var2 == Short.TYPE)
            ? new UnsafeStaticShortFieldAccessorImpl(var0)
            : ((var2 == Character.TYPE)
            ? new UnsafeStaticCharacterFieldAccessorImpl(var0)
            : ((var2 == Integer.TYPE)
            ? new UnsafeStaticIntegerFieldAccessorImpl(var0)
            : ((var2 == Long.TYPE)
            ? new UnsafeStaticLongFieldAccessorImpl(var0)
            : ((var2 == Float.TYPE)
            ? new UnsafeStaticFloatFieldAccessorImpl(var0)
            : ((var2 == Double.TYPE)
            ? new UnsafeStaticDoubleFieldAccessorImpl(var0)
            : new UnsafeStaticObjectFieldAccessorImpl(var0)))))))))
            : ((var2 == Boolean.TYPE)
            ? new UnsafeQualifiedStaticBooleanFieldAccessorImpl(var0, var7)
            : ((var2 == Byte.TYPE)
            ? new UnsafeQualifiedStaticByteFieldAccessorImpl(var0, var7)
            : ((var2 == Short.TYPE)
            ? new UnsafeQualifiedStaticShortFieldAccessorImpl(var0, var7)
            : ((var2 == Character.TYPE)
            ? new UnsafeQualifiedStaticCharacterFieldAccessorImpl(var0, var7)
            : ((var2 == Integer.TYPE)
            ? new UnsafeQualifiedStaticIntegerFieldAccessorImpl(var0, var7)
            : ((var2 == Long.TYPE)
            ? new UnsafeQualifiedStaticLongFieldAccessorImpl(var0, var7)
            : ((var2 == Float.TYPE)
            ? new UnsafeQualifiedStaticFloatFieldAccessorImpl(var0, var7)
            : ((var2 == Double.TYPE)
            ? new UnsafeQualifiedStaticDoubleFieldAccessorImpl(var0, var7)
            : new UnsafeQualifiedStaticObjectFieldAccessorImpl(var0, var7))))))))));
        } else {
            return (FieldAccessor) ((!var6)
            ? ((var2 == Boolean.TYPE)
            ? new UnsafeBooleanFieldAccessorImpl(var0)
            : ((var2 == Byte.TYPE) ? new UnsafeByteFieldAccessorImpl(var0)
                                   : ((var2 == Short.TYPE)
            ? new UnsafeShortFieldAccessorImpl(var0)
            : ((var2 == Character.TYPE)
            ? new UnsafeCharacterFieldAccessorImpl(var0)
            : ((var2 == Integer.TYPE)
            ? new UnsafeIntegerFieldAccessorImpl(var0)
            : ((var2 == Long.TYPE) ? new UnsafeLongFieldAccessorImpl(var0)
                                   : ((var2 == Float.TYPE)
            ? new UnsafeFloatFieldAccessorImpl(var0)
            : ((var2 == Double.TYPE) ? new UnsafeDoubleFieldAccessorImpl(var0)
                                     : new UnsafeObjectFieldAccessorImpl(var0)))))))))
            : ((var2 == Boolean.TYPE)
            ? new UnsafeQualifiedBooleanFieldAccessorImpl(var0, var7)
            : ((var2 == Byte.TYPE)
            ? new UnsafeQualifiedByteFieldAccessorImpl(var0, var7)
            : ((var2 == Short.TYPE)
            ? new UnsafeQualifiedShortFieldAccessorImpl(var0, var7)
            : ((var2 == Character.TYPE)
            ? new UnsafeQualifiedCharacterFieldAccessorImpl(var0, var7)
            : ((var2 == Integer.TYPE)
            ? new UnsafeQualifiedIntegerFieldAccessorImpl(var0, var7)
            : ((var2 == Long.TYPE)
            ? new UnsafeQualifiedLongFieldAccessorImpl(var0, var7)
            : ((var2 == Float.TYPE)
            ? new UnsafeQualifiedFloatFieldAccessorImpl(var0, var7)
            : ((var2 == Double.TYPE)
            ? new UnsafeQualifiedDoubleFieldAccessorImpl(var0, var7)
            : new UnsafeQualifiedObjectFieldAccessorImpl(var0, var7))))))))));
        }
    }
}

以上代碼可以發(fā)現(xiàn),通過(guò)工廠模式根據(jù)field屬性類(lèi)型以及是否靜態(tài)來(lái)獲取,為什么會(huì)有這樣的劃分呢

首先,jdk是通過(guò)UNSAFE類(lèi)對(duì)堆內(nèi)存中對(duì)象的屬性進(jìn)行直接的讀取和寫(xiě)入,要讀取和寫(xiě)入首先需要確定屬性所在的位置,也就是相對(duì)對(duì)象起始位置的偏移量,而靜態(tài)屬性是針對(duì)類(lèi)的不是每個(gè)對(duì)象實(shí)例一份,所以靜態(tài)屬性的偏移量需要單獨(dú)獲取

其實(shí)通過(guò)該偏移量我們可以大致推斷出一個(gè)實(shí)例內(nèi)每個(gè)屬性在堆內(nèi)存的相對(duì)位置,以及分別占用多大的空間,有了位置信息,我們還需要這個(gè)字段的類(lèi)型,以方便執(zhí)行器知道讀幾個(gè)字節(jié)的數(shù)據(jù),并且如何進(jìn)行解析,目前提供了8大基礎(chǔ)類(lèi)型(char vs Charector)和數(shù)組和普通引用類(lèi)型。 java虛擬機(jī)為了保證每個(gè)對(duì)象所占的空間都是8個(gè)字節(jié)倍數(shù),有時(shí)候?yàn)榱吮苊鈨蓚€(gè)volatile字段存放在同一個(gè)緩存行,所以有時(shí)候會(huì)再某些字段上做空位填充

以下為UnSafe類(lèi)的部分代碼

public final class Unsafe {
    private static final Unsafe theUnsafe;
    private Unsafe() {
    }

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

    public native int getInt(Object var1, long var2);
    public native void putInt(Object var1, long var2, int var4);
    public native Object getObject(Object var1, long var2);
    public native void putObject(Object var1, long var2, Object var4);
    public native boolean getBoolean(Object var1, long var2);
    public native void putBoolean(Object var1, long var2, boolean var4);
    public native byte getByte(Object var1, long var2);

    public native long objectFieldOffset(Field var1);
@Deprecated
public int fieldOffset(Field var1) {
    return Modifier.isStatic(var1.getModifiers())?(int)this.staticFieldOffset(var1):(int)this.objectFieldOffset(var1);
}

然后我們?cè)趤?lái)看看通過(guò)反射來(lái)調(diào)用方法

同樣,jdk通過(guò)MethodAccessor來(lái)進(jìn)行method的調(diào)用,java虛擬機(jī)提供了兩種模式來(lái)支持method的調(diào)用 一個(gè)是NativeMethodAccessorImpl 一個(gè)是通過(guò)ASM字節(jié)碼直接動(dòng)態(tài)生成一個(gè)類(lèi)在invoke方法內(nèi)部調(diào)用目標(biāo)方法,由于是動(dòng)態(tài)生成所以jdk中沒(méi)有其源碼,但jdk提供了DelegatingMethodAccessorImpl委派模式以方便在運(yùn)行過(guò)程中可以動(dòng)態(tài)切換字節(jié)碼模式和native模式,我們可以看下生成MethodAccessor的代碼

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private final Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;

    NativeMethodAccessorImpl(Method var1) {
        this.method = var1;
    }

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if(++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

        return invoke0(this.method, var1, var2);
    }

    void setParent(DelegatingMethodAccessorImpl var1) {
        this.parent = var1;
    }

    private static native Object invoke0(Method var0, Object var1, Object[] var2);
}

可以看到JDK內(nèi)部通過(guò)numInvocations判斷如果該反射調(diào)用次數(shù)超過(guò)ReflectionFactory.inflationThreshold()則用字節(jié)碼實(shí)現(xiàn),如果小于該值則采用native實(shí)現(xiàn),native的調(diào)用比字節(jié)碼方式慢很多, 動(dòng)態(tài)實(shí)現(xiàn)和本地實(shí)現(xiàn)相比執(zhí)行效率要快20倍,因?yàn)閯?dòng)態(tài)實(shí)現(xiàn)無(wú)需經(jīng)過(guò)JAVA,C++再到JAVA的轉(zhuǎn)換,之前在jdk6以前有個(gè)工具ReflectAsm就是采用這種方式提升執(zhí)行效率,不過(guò)在jdk8以后,也提供了字節(jié)碼方式,由于許多反射只需要執(zhí)行一次,然而動(dòng)態(tài)方式生成字節(jié)碼十分耗時(shí),所以jdk提供了一個(gè)閾值默認(rèn)15,當(dāng)某個(gè)反射的調(diào)用次數(shù)小于15的話就走本地實(shí)現(xiàn),大于15則走動(dòng)態(tài)模式,而這個(gè)閾值可以在jdk啟動(dòng)參數(shù)里面做配置

反射為什么慢
經(jīng)過(guò)以上優(yōu)化,其實(shí)反射的效率并不慢,在某些情況下可能達(dá)到和直接調(diào)用基本相同的效率,但是在首次執(zhí)行或者沒(méi)有緩存的情況下還是會(huì)有性能上的開(kāi)銷(xiāo),主要在以下方面

Class.forName();會(huì)調(diào)用本地方法,我們用到的method和field都會(huì)在此時(shí)加載進(jìn)來(lái),雖然會(huì)進(jìn)行緩存,但是本地方法免不了有JAVA到C+=在到JAVA得轉(zhuǎn)換開(kāi)銷(xiāo)
class.getMethod(),會(huì)遍歷該class所有的公用方法,如果沒(méi)匹配到還會(huì)遍歷父類(lèi)的所有方法,并且getMethods()方法會(huì)返回結(jié)果的一份拷貝,所以該操作不僅消耗CPU還消耗堆內(nèi)存,在熱點(diǎn)代碼中應(yīng)該盡量避免,或者進(jìn)行緩存
invoke參數(shù)是一個(gè)object數(shù)組,而object數(shù)組不支持java基礎(chǔ)類(lèi)型,而自動(dòng)裝箱也是很耗時(shí)的
反射的運(yùn)用
spring ioc
spring加載bean的流程基本都用到了反射機(jī)制

獲取類(lèi)的實(shí)例 通過(guò)構(gòu)造方法getInstance(靜態(tài)變量初始化,屬性賦值,構(gòu)造方法)
如果實(shí)現(xiàn)了BeanNameAware接口,則用反射注入bean賦值給屬性
如果實(shí)現(xiàn)了BeanFactoryAware接口,則設(shè)置 beanFactory
如果實(shí)現(xiàn)了ApplicationContextAware,則設(shè)置ApplicationContext
調(diào)用BeanPostProcesser的預(yù)先初始化方法
如果實(shí)現(xiàn)了InitializingBean,調(diào)用AfterPropertySet方法
調(diào)用定制的 init-method()方法 對(duì)應(yīng)的直接 @PostConstruct
調(diào)用BeanPostProcesser的后置初始化完畢的方法
序列化
fastjson可以參考ObjectDeserializer的幾個(gè)實(shí)現(xiàn) JavaBeanDeserializer和ASMJavaBeanDeserializer

動(dòng)態(tài)代理
jdk提供了一個(gè)工具類(lèi)來(lái)動(dòng)態(tài)生成一個(gè)代理,允許在執(zhí)行某一個(gè)方法時(shí)進(jìn)行額外的處理

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

class HWInvocationHandler implements InvocationHandler{
        //目標(biāo)對(duì)象
        private Object target;
        public HWInvocationHandler(Object target){
            this.target = target;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------插入前置通知代碼-------------");
            //執(zhí)行相應(yīng)的目標(biāo)方法
            Object rs = method.invoke(target,args);
            System.out.println("------插入后置處理代碼-------------");
            return rs;
        }
    }

我們分析下這個(gè)方法的實(shí)現(xiàn),首先生成的代理對(duì)象,需要實(shí)現(xiàn)參數(shù)里面聲明的所有接口,接口的實(shí)現(xiàn)應(yīng)給委托給InvocationHandler進(jìn)行處理,invocationHandler里面可以根據(jù)method聲明判斷是否需要做增強(qiáng),所以所生成的代理類(lèi)里面必須能夠獲取到InvocationHandler,在我們無(wú)法知道代理類(lèi)的具體類(lèi)型的時(shí)候,我們可以通過(guò)反射從構(gòu)造方法里將InvocationHandler傳給代理類(lèi)的實(shí)例 所以 總的來(lái)說(shuō)生成代理對(duì)象需要兩步

獲取代理類(lèi)的class對(duì)象
通過(guò)class對(duì)象獲取構(gòu)造方法,通過(guò)反射生成代理類(lèi)的實(shí)例,并將InvocationHandler傳人

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();

    /*
     * Look up or generate the designated proxy class.
     * 生成代理類(lèi)
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {

        //獲取代理類(lèi)的構(gòu)造方法
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //獲取代理類(lèi)的實(shí)例,并且將invocationhandler傳人
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
       ...
    }
}

下面我們?cè)诳聪?getProxyClass0 如何獲取代理類(lèi)的class對(duì)象,這里idk通過(guò)WeakCache來(lái)緩存已經(jīng)生成的class對(duì)象,因?yàn)樯稍揷lass通過(guò)字節(jié)碼生成還是很耗時(shí),同時(shí)為了解決之前由于動(dòng)態(tài)代理生成太多class對(duì)象導(dǎo)致內(nèi)存不足,所以這里通過(guò)弱引用WeakReference來(lái)緩存所生成的代理對(duì)象class,當(dāng)發(fā)生GC的時(shí)候如果該class對(duì)象沒(méi)有其他的強(qiáng)引用將會(huì)被直接回收 生成代理類(lèi)的class在ProxyGenerator的generateProxyClass方法內(nèi)實(shí)現(xiàn),該方法返回一個(gè)byte[]數(shù)組,最后通過(guò)一個(gè)本地方法加載到虛擬機(jī),所以可以看出生成該對(duì)象還是非常耗時(shí)的

//生成字節(jié)碼數(shù)組
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
try {
//加載進(jìn)虛擬機(jī)
    return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
    /*
     * A ClassFormatError here means that (barring bugs in the
     * proxy class generation code) there was some other
     * invalid aspect of the arguments supplied to the proxy
     * class creation (such as virtual machine limitations
     * exceeded).
     */
    throw new IllegalArgumentException(e.toString());
}

private byte[] generateClassFile() {
    this.addProxyMethod(hashCodeMethod, Object.class);
    this.addProxyMethod(equalsMethod, Object.class);
    this.addProxyMethod(toStringMethod, Object.class);
    Class[] var1 = this.interfaces;
    int var2 = var1.length;

    int var3;
    Class var4;
    for(var3 = 0; var3 < var2; ++var3) {
        var4 = var1[var3];
        Method[] var5 = var4.getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method var8 = var5[var7];
            this.addProxyMethod(var8, var4);
        }
    }

    this.methods.add(this.generateConstructor());
...
 }
 //生成一個(gè)帶invocationhandler參數(shù)的構(gòu)造方法
private ProxyGenerator.MethodInfo generateConstructor() throws IOException {
    ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);
    DataOutputStream var2 = new DataOutputStream(var1.code);
    this.code_aload(0, var2);
    this.code_aload(1, var2);
    var2.writeByte(183);
    var2.writeShort(this.cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
    var2.writeByte(177);
    var1.maxStack = 10;
    var1.maxLocals = 2;
    var1.declaredExceptions = new short[0];
    return var1;
}

上面的流程可以簡(jiǎn)單歸納為

增加hashcode,equals,toString方法
增加所有接口中聲明的未實(shí)現(xiàn)方法
增加一個(gè)方法參數(shù)為java/lang/reflect/InvocationHandler的構(gòu)造方法
其他靜態(tài)初始化數(shù)據(jù)
動(dòng)態(tài)代理的應(yīng)用
spring-aop

spring aop默認(rèn)基于jdk動(dòng)態(tài)代理來(lái)實(shí)現(xiàn),我們來(lái)看下下面這個(gè)經(jīng)典的面試問(wèn)題

一個(gè)類(lèi)里面,兩個(gè)方法A和方法B,方法B上有加注解做事物增強(qiáng),那么A調(diào)用this.B為什么沒(méi)有事物效果?

因?yàn)閟pring-aop默認(rèn)基于jdk的動(dòng)態(tài)代理實(shí)現(xiàn),最終執(zhí)行是通過(guò)生成的代理對(duì)象的,而代理對(duì)象執(zhí)行A方法和B方法其實(shí)是調(diào)用的InvocationHandler里面的增強(qiáng)后的方法,其中B方法是經(jīng)過(guò)InvocationHandler做增強(qiáng)在方法前后增加了事物開(kāi)啟和提交的代碼,而真正執(zhí)行代碼是通過(guò)methodB.invoke(原始對(duì)象) 而A方法的實(shí)現(xiàn)內(nèi)部雖然包含了this.B方法 但其實(shí)是調(diào)用了methodA.invoke(原始對(duì)象),而這一句代碼相當(dāng)于調(diào)用的是原始對(duì)象的methodA方法,而這里面的this.B()方法其實(shí)是調(diào)用的原始對(duì)象的B方法,沒(méi)有進(jìn)行過(guò)事物增強(qiáng),而如果是通過(guò)cglib做字節(jié)碼增強(qiáng),生成這個(gè)類(lèi)的子類(lèi),這種調(diào)用this.B方法是有事物效果的
反射真的慢么?動(dòng)態(tài)代理會(huì)創(chuàng)建很多臨時(shí)class?
rpc consumer

有過(guò)RMI開(kāi)發(fā)經(jīng)驗(yàn)的人可能會(huì)很熟悉,為什么在對(duì)外export rmi服務(wù)的時(shí)候會(huì)分別在client和server生成兩個(gè)stub文件,其中client的文件其實(shí)就是用動(dòng)態(tài)代理生成了一個(gè)代理類(lèi) 這個(gè)代理類(lèi),實(shí)現(xiàn)了所要對(duì)外提供服務(wù)的所有接口,每個(gè)方法的實(shí)現(xiàn)其實(shí)就是將接口信息,方法聲明,參數(shù),返回值信息通過(guò)網(wǎng)絡(luò)發(fā)給服務(wù)端,而服務(wù)端收到請(qǐng)求后通過(guò)找到對(duì)應(yīng)的實(shí)現(xiàn)然后用反射method.invoke進(jìn)行調(diào)用,然后將結(jié)果返回給客戶端

其實(shí)其他的RPC框架的實(shí)現(xiàn)方式大致和這個(gè)類(lèi)似,只是客戶端的代理類(lèi),可能不僅要將方法聲明通過(guò)網(wǎng)絡(luò)傳輸給服務(wù)提供方,也可以做一下服務(wù)路由,負(fù)載均衡,以及傳輸一些額外的attachment數(shù)據(jù)給provider
反射真的慢么?動(dòng)態(tài)代理會(huì)創(chuàng)建很多臨時(shí)class?

分享標(biāo)題:反射真的慢么?動(dòng)態(tài)代理會(huì)創(chuàng)建很多臨時(shí)class?
文章起源:http://bm7419.com/article8/jdcgip.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、App設(shè)計(jì)網(wǎng)頁(yè)設(shè)計(jì)公司、服務(wù)器托管App開(kāi)發(fā)、網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁(yè)設(shè)計(jì)公司