动态代理----JDK动态代理

动态代理

         代理模式一般用于增强类的方法而不直接修改原始类的代码,动态代理可以用于动态生成目标类的代理类来执行相应操作,目前动态代理主要有JDK动态代理以及CGLIB,经常会提起,也会看一下解释,但是并没有真正实践过,现在学习一下。

                               

JDK动态代理

使用反射机制,一般用来代理接口类。

具体来说

1.要有目标接口

public interface UserService {
    int hello(String name);
}

目标接口实现类

public class UserServiceImpl implements UserService{

    @Override
    public int hello(String name) {
        System.out.println("hello " + name);
        return 100;
    }
}

 

UserServiceImpl类实现UserService接口hello方法,实现一定的业务逻辑,现在假定需要改变或者增强hello方法,一种方法是重新定义一个类实现UserService接口,如果hello方法需要频繁变动则该种方式不合适,可以考虑动态代理的方式,生成一个代理类,代理类中可以调用UserServiceImpl实现逻辑,并且可以增强hello方法。

2.实现InvocationHandler,进行目标方法的增强,调用Proxy.newProxyInstance, 产生动态代理类,执行方法

 

UserService us = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
        UserService.class.getClassLoader(),
        UserServiceImpl.class.getInterfaces(), (proxy1, method, args1) -> {
            if(method.getName().equals("hello")){
                System.out.println("before");
                method.invoke(us, args1);
                System.out.println("after");
            }
            return 200;
        });
//us.hello("lisi");
proxy.hello("lisi");

代理类由Proxy.newProxyInstance来产生,其方法主要说明如下

/**
 * 返回一个实现特定接口的代理类,在代理类中可以根据特定的处理器来转发方法调用
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 * 类加载器用来加载代理类,一般与目标接口的加载器相同即可
 * @param   loader the class loader to define the proxy class
 * 目标接口集合
 * @param   interfaces the list of interfaces for the proxy class
 *          to implement
 * 转发方法调用,再次实现代理方法的增强
 * @param   h the invocation handler to dispatch method invocations to
 * @return  a proxy instance with the specified invocation handler of a
 *          proxy class that is defined by the specified class loader
 *          and that implements the specified interfaces
 */

其实质是对于目标类来说,经过编译成class文件由类加载器进行加载执行,对于代理类,根据目标类和InvocationHandler来动态组装class文件,实现目标类的方法增强。

InvocationHandler方法的定义

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

其中invoke方法参数说明如下:

proxy代理类实例,method代理类调用的方法,args代理类调用方法传入的参数

/**
 * Processes a method invocation on a proxy instance and returns
 * the result.  This method will be invoked on an invocation handler
 * when a method is invoked on a proxy instance that it is
 * associated with.
 *
 * @param   proxy the proxy instance that the method was invoked on
 *
 * @param   method the {@code Method} instance corresponding to
 * the interface method invoked on the proxy instance.  The declaring
 * class of the {@code Method} object will be the interface that
 * the method was declared in, which may be a superinterface of the
 * proxy interface that the proxy class inherits the method through.
 *
 * @param   args an array of objects containing the values of the
 * arguments passed in the method invocation on the proxy instance,
 * or {@code null} if interface method takes no arguments.
 * Arguments of primitive types are wrapped in instances of the
 * appropriate primitive wrapper class, such as
 * {@code java.lang.Integer} or {@code java.lang.Boolean}.
 *
 * @return  the value to return from the method invocation on the
 * proxy instance.  If the declared return type of the interface
 * method is a primitive type, then the value returned by
 * this method must be an instance of the corresponding primitive
 * wrapper class; otherwise, it must be a type assignable to the
 * declared return type.  If the value returned by this method is
 * {@code null} and the interface method's return type is
 * primitive, then a {@code NullPointerException} will be
 * thrown by the method invocation on the proxy instance.  If the
 * value returned by this method is otherwise not compatible with
 * the interface method's declared return type as described above,
 * a {@code ClassCastException} will be thrown by the method
 * invocation on the proxy instance.
 */

JDK动态代理源码分析

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

newProxyInstance方法中核心逻辑

生成代理类class文件

/*
 * Look up or generate the designated proxy class.
 */
Class<?> cl = getProxyClass0(loader, intfs);

根据class文件获取构造函数,构造代理类实例并返回,这里使用反射原理

生成的class文件中含有InvocationHandler为参数的构造函数,调用该构造函数,产生代理类实例。

final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{h});

这里的核心是代理类class文件生成过程

代理类class生成

/*
 * Look up or generate the designated proxy class.
 */
Class<?> cl = getProxyClass0(loader, intfs);

该方法传入类加载器以及接口集合,产生class文件。可知代理类class数据会被缓存,初次代理会产生class数据,再次使用代理会根据类加载器和接口来从缓存中获取。

/**
 * Generate a proxy class.  Must call the checkProxyAccess method
 * to perform permission checks before calling this.
 */
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}

WeakCache用来缓存代理class数据

/**
 * a cache of proxy classes
 */
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
Cache mapping pairs of {@code (key, sub-key) -> value}. Keys and values are weakly but sub-keys are strongly referenced. 
Keys and values are weakly but sub-keys are strongly referenced.

可知WeakCache实现了双关键字和值得对应缓存。在此处key=类加载器,subKey=接口集合,判断两个类是同一个类需要满足两个条件,class相同并且类加载器相同,主关键词对于值是弱相关,即主关键字相同,值未必相同,副关键词对于值是强相关,即副关键词不相同则值必定不相同。

new KeyFactory()通过对于类加载器和接口集合来产生key;new ProxyClassFactory()产生代理class数据

KeyFactory实现BiFunction接口,BiFunction<ClassLoader, Class<?>[], Object>表示传入ClassLoader和Class<?>[]两个参数,经过apply方法处理而返回一个Object对象

/**
 * A function that maps an array of interfaces to an optimal key where
 * Class objects representing interfaces are weakly referenced.
 */
private static final class KeyFactory
    implements BiFunction<ClassLoader, Class<?>[], Object>
{
    @Override
    public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
        switch (interfaces.length) {
            case 1: return new Key1(interfaces[0]); // the most frequent
            case 2: return new Key2(interfaces[0], interfaces[1]);
            case 0: return key0;
            default: return new KeyX(interfaces);
        }
    }
}

这里主要根据接口数量来分别返回不同的Key对象。

ProxyClassFactory类实现BiFunction接口,其实现传入ClassLoader和Class<?>[],返回Class<?>

/**
 * A factory function that generates, defines and returns the proxy class given
 * the ClassLoader and array of interfaces.
 */
private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>

代理class名称生成规则

// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

产生代理类class数据

/*
 * Generate the specified proxy class.
 */
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
try {
    return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);

核心由ProxyGenerator.generateProxyClass实现

/**
 * Generate a proxy class given a name and a list of proxy interfaces.
 *
 * @param name        the class name of the proxy class
 * @param interfaces  proxy interfaces
 * @param accessFlags access flags of the proxy class
*/
public static byte[] generateProxyClass(final String name,
                                        Class<?>[] interfaces,
                                        int accessFlags)

ProxyGenerator相当于一个小型的编译器,负责生成代理类的class文件

ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();

class文件生成具体过程

1.收集代理类所有需要代理的方法

处理hashcode  equals  toString方法

addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);

处理所有的代理接口方法

for (Class<?> intf : interfaces) {
    for (Method m : intf.getMethods()) {
        addProxyMethod(m, intf);
    }
}

2.收集代理类所有FieldInfo和MethodInfo信息

构造函数  代理方法

/* ============================================================
 * Step 2: Assemble FieldInfo and MethodInfo structs for all of
 * fields and methods in the class we are generating.
 */
try {
    methods.add(generateConstructor());

    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        for (ProxyMethod pm : sigmethods) {

            // add static field for method's Method object
            fields.add(new FieldInfo(pm.methodFieldName,
                "Ljava/lang/reflect/Method;",
                 ACC_PRIVATE | ACC_STATIC));

            // generate code for proxy method and add it
            methods.add(pm.generateMethod());
        }
    }

    methods.add(generateStaticInitializer());

核心构造函数的产生,因为代理增强逻辑是通过InvocationHandler通过构造函数来加入到代理类class中的。

这其中涉及到反射以及class文件格式,以后慢慢研究。

/**
 * Generate the constructor method for the proxy class.
 */
private MethodInfo generateConstructor() throws IOException {
    MethodInfo minfo = new MethodInfo(
        "<init>", "(Ljava/lang/reflect/InvocationHandler;)V",
        ACC_PUBLIC);

    DataOutputStream out = new DataOutputStream(minfo.code);

    code_aload(0, out);

    code_aload(1, out);

    out.writeByte(opc_invokespecial);
    out.writeShort(cp.getMethodRef(
        superclassName,
        "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));

    out.writeByte(opc_return);

    minfo.maxStack = 10;
    minfo.maxLocals = 2;
    minfo.declaredExceptions = new short[0];

    return minfo;
}

3.产生最终class文件

/* ============================================================
 * Step 3: Write the final class file.
 */

/*
 * Make sure that constant pool indexes are reserved for the
 * following items before starting to write the final class file.
 */
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
    cp.getClass(dotToSlash(intf.getName()));
}

/*
 * Disallow new constant pool additions beyond this point, since
 * we are about to write the final constant pool table.
 */
cp.setReadOnly();

ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);

try {
    /*
     * Write all the items of the "ClassFile" structure.
     * See JVMS section 4.1.
     */
                                // u4 magic;
    dout.writeInt(0xCAFEBABE);
                                // u2 minor_version;
    dout.writeShort(CLASSFILE_MINOR_VERSION);
                                // u2 major_version;
    dout.writeShort(CLASSFILE_MAJOR_VERSION);

    cp.write(dout);             // (write constant pool)

                                // u2 access_flags;
    dout.writeShort(accessFlags);
                                // u2 this_class;
    dout.writeShort(cp.getClass(dotToSlash(className)));
                                // u2 super_class;
    dout.writeShort(cp.getClass(superclassName));

                                // u2 interfaces_count;
    dout.writeShort(interfaces.length);
                                // u2 interfaces[interfaces_count];
    for (Class<?> intf : interfaces) {
        dout.writeShort(cp.getClass(
            dotToSlash(intf.getName())));
    }

                                // u2 fields_count;
    dout.writeShort(fields.size());
                                // field_info fields[fields_count];
    for (FieldInfo f : fields) {
        f.write(dout);
    }

                                // u2 methods_count;
    dout.writeShort(methods.size());
                                // method_info methods[methods_count];
    for (MethodInfo m : methods) {
        m.write(dout);
    }

                                 // u2 attributes_count;
    dout.writeShort(0); // (no ClassFile attributes for proxy classes)

} catch (IOException e) {
    throw new InternalError("unexpected I/O Exception", e);
}

return bout.toByteArray();

因此便产生最终代理类的class文件数据。

目标类通过编译成class文件而执行,用户实现InvocationHandler接口在此实现对于原始方法的增强逻辑,代理类生成class文件,通过构造函数方式将用户自定义InvocationHandler加入到代理类class文件中,从而实现代理类的增强。

查看一下生成的代理类class文件

public static void main(String[] args) throws IOException {
    UserService us = new UserServiceImpl();

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

    UserService proxy = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),
            UserServiceImpl.class.getInterfaces(), (proxy1, method, args1) -> {
                if(method.getName().equals("hello")){
                    System.out.println("before");
                    method.invoke(us, args1);
                    System.out.println("after");
                }
                return 200;
            });
    //us.hello("lisi");
    proxy.hello("lisi");

    byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", UserServiceImpl.class.getInterfaces());
    FileOutputStream fo = new FileOutputStream("$Proxy0.class");
    fo.write(bytes);
    fo.close();
}

得到class文件反编译如下所示:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.java.proxy.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements UserService {
    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 int hello(String var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | 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("com.java.proxy.UserService").getMethod("hello", 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());
        }
    }
}

代理类由构造函数传入InvocationHandler ,当调用代理类hello方法时,其由InvocaitonHandler来进行转发

public final int hello(String var1) throws  {
    try {
        return (Integer)super.h.invoke(this, m3, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}

其中m3方法由反射方法获取目标类的hello方法。

m3 = Class.forName("com.java.proxy.UserService").getMethod("hello", Class.forName("java.lang.String"));

参考文献:

https://www.jianshu.com/p/3616c70cb37b

https://blog.csdn.net/zhangdefeng2008/article/details/79399898

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值