关于java:掌握java动态代理及原理有多难

3次阅读

共计 8473 个字符,预计需要花费 22 分钟才能阅读完成。

前言:应用的 jdk 是 1.7,须要理解反射机制 泛型 字节码登概念!

一、代理模式

代理模式是罕用的 java 设计模式,他的特色是代理类与委托类有同样的接口,代理类次要负责为委托类预处理音讯、过滤音讯、把音讯转发给委托类,以及预先解决音讯等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象自身并不真正实现服务,而是通过调用委托类的对象的相干办法,来提供特定的服务。

二、编写一个 java 动静代理

1. 筹备两个接口

别离是 ProductService(生产接口)和 FactoryService(工厂接口)

代码如下:

package test.myproxy;

/**
 * <ul>
 * <li>Title: FactoryService</li>
 * </ul>
 *
 * @author ken
 * @date 2021/4/13 0013 上午 10:05
 */
public interface FactoryService {void addProduce(int num);

}
package test.myproxy;

/**
 * <ul>
 * <li>Title: ProductService</li>
 * </ul>
 *
 * @author ken
 * @date 2021/4/12 0012 下午 17:46
 */
public interface ProductService {

    /**
     * 增加产品
     * @param productName
     */
    void addProduct(String productName);

}

2. MyServiceImpl 类实现上述两个接口

package test.myproxy;

/**
 * <ul>
 * <li>Title: MyServiceImpl</li>
 * <li>Description: TODO </li>
 * </ul>
 *
 * @author ken
 * @date 2021/4/12 0012 下午 17:47
 */
public class MyServiceImpl implements ProductService,FactoryService {

    @Override
    public void addProduct(String productName) {System.out.println("正在增加"+productName);
    }


    @Override
    public void addProduce(int num) {System.out.println("筹备生成"+num+"件商品");
    }
}

3. 实现动静代理的性能

3.1 jdk 中须要实现 InvocationHandler 这个接口,重写 invoke 办法
3.2 很多文章中为了便于了解,长把上面代码中的 target 和 getInstance 的参数写成理论的接口,如下面的 ProductService,这边不想这么写,是为了让大家能了解泛型,getInstance 的接口外面我也明确指出参数是与代理对象是继承关系 / 实现接口的关系。

代码如下:


package test.myproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * <ul>
 * <li>Title: JdkInvocationHandler</li>
 * <li>Description: TODO </li>
 * </ul>
 *
 * @author ken
 * @date 2021/4/12 0012 下午 17:45
 */
public class JdkInvocationHandler<T> implements InvocationHandler {
    // 代理对象
    private T target;
        
    public <B extends T> T getInstance(B target){
        this.target = target;
        Class clazz = this.target.getClass();
        // 参数 1:被代理类的类加载器 参数 2: 被代理类的接口 参数 3
        return (T)Proxy.newProxyInstance(clazz.getClassLoader(),
                clazz.getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String currentDate  = simpleDateFormat.format(new Date());
        System.out.println("日期【"+currentDate + "】增加了一款产品");
        return method.invoke(target,args);

    }
}
3.3 测试

这里的示例,用了泛型和强转两种形式:

 public static void main(String[] args) throws Exception {ProductService proxy =  new JdkInvocationHandler<ProductService>().getInstance(new MyServiceImpl());
        proxy.addProduct("iphone");
                
        FactoryService proxyFactory = (FactoryService) new JdkInvocationHandler().getInstance(new MyServiceImpl());
        proxyFactory.addProduce(500);

        // 这里咱们将 jdk 生成的代理类输入了进去,不便前面剖析应用
       /* byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{productService.getClass()});
        FileOutputStream os = new FileOutputStream("Proxy0.class");
        os.write(bytes);
        os.close();*/}

4. 剖析如何生成动静代理

在下面的 JdkInvocationHandler 类 实现了 InvocationHandler 接口,在 getInstance 办法中最要害的一行代码是 Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(), this);

上面追随这个代码进一步分析内容:

上面给大家介绍是
地位: java.lang.reflect
类:Proxy

在这个类外面有一些成员属性,先理解一下前面有些会用到。

4.1 newProxyInstance 办法的内容

(为了看起来简洁 我把一些正文删掉了)


 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
        throws IllegalArgumentException
    {if (h == null) {throw new NullPointerException();
        }

        /*
         * 查找或生成指定的代理类。* Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            SecurityManager sm = System.getSecurityManager();
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {public Object run() {return newInstance(cons, ih);
                    }
                });
            } else {return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {throw new InternalError(e.toString());
        }
    }

这段代码是通过 c1 这个 Class 类信息,依据结构函数参数,动静生成反射类。这里很重要的在 getProxyClass0 这行代码,正文上的英文的意思是 运行在堆上,且这部分代码不要重构。由此也可晓得这部分很重要。上面我把外面的代码展现进去:

4.2 getProxyClass0 办法的内容

(这个是 getProxyClass0 办法上的正文)


private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller
            final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME);
            final ClassLoader ccl = caller.getClassLoader();
            checkProxyLoader(ccl, loader);
            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
        }

        if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");
        }

        Class<?> proxyClass = null;
        String[] interfaceNames = new String[interfaces.length];

        // for detecting duplicates
        Set<Class<?>> interfaceSet = new HashSet<>();

        for (int i = 0; i < interfaces.length; i++) {String interfaceName = interfaces[i].getName();
            Class<?> interfaceClass = null;
            try {interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) { }
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(interfaces[i] + "is not visible from class loader");
            }

            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(interfaceClass.getName() + "is not an interface");
            }

            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException("repeated interface:" + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        List<String> key = Arrays.asList(interfaceNames);

        /*
         * Find or create the proxy class cache for the class loader.
         */
        Map<List<String>, Object> cache;
        synchronized (loaderToCache) {cache = loaderToCache.get(loader);
            if (cache == null) {cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
        }

        synchronized (cache) {
            do {Object value = cache.get(key);
                if (value instanceof Reference) {proxyClass = (Class<?>) ((Reference) value).get();}
                if (proxyClass != null) {
                    // proxy class already generated: return it
                    return proxyClass;
                } else if (value == pendingGenerationMarker) {
                    // proxy class being generated: wait for it
                    try {cache.wait();
                    } catch (InterruptedException e) { }
                    continue;
                } else {cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
            String proxyPkg = null;     // package to define proxy class in
            for (int i = 0; i < interfaces.length; i++) {int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {String name = interfaces[i].getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException("non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            {

                long num;
                synchronized (nextUniqueNumberLock) {num = nextUniqueNumber++;}
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
                try {
                    proxyClass = defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {throw new IllegalArgumentException(e.toString());
                }
            }
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);

        } finally {synchronized (cache) {if (proxyClass != null) {cache.put(key, new WeakReference<Class<?>>(proxyClass));
                } else {cache.remove(key);
                }
                cache.notifyAll();}
        }
        return proxyClass;
    }

上面剖析这段代码:
这部代码的意义

4.2.1. 如果类实现的接口超过 65535,间接抛出“interface limit exceeded”的异样,这部分应该是为了性能,个别状况下 咱们的实现的接口 大多在 200 个以下,这部分能够疏忽
4.2.2 类的所有接口实例化,并放入汇合中 , 而后查找或创立类加载器的代理类缓存(loaderToCache 就是寄存类加载器的缓存,key 为类加载器,value 为 Map<List<String>,Object>, 该 map 的 key 则为 类实现的接口汇合),前面是设置代理类的包名 如果有 non-public 的接口[即接口包名应用这个类的包名,否则默认包名是 com.sun.proxy, 类名通常是类名前缀 +num,类名前缀为 $Proxy, 这部分前面会提到], 最终返回的是生成代理类[留神主动生成代理类名],前面凭借“这个类名”能够调用办法
4.2.3 类的所有接口实例化
  Class <?> interfaceClass = Class.forName(interfaceName, false, loader);
4.2.4 类所有实现的接口 放入汇合中,这个局部前面
        List<String> key = Arrays.asList(interfaceNames);
4.2.5 查找或创立类加载器的代理类缓存。
 Map<List<String>, Object> cache;
        synchronized (loaderToCache) {cache = loaderToCache.get(loader);
            if (cache == null) {cache = new HashMap<>();
                loaderToCache.put(loader, cache);
            }
            /*
             * This mapping will remain valid for the duration of this
             * method, without further synchronization, because the mapping
             * will only be removed if the class loader becomes unreachable.
             */
        }
4.2.6 这部分是记录 以后创立代理的个数,避免生成的代理类的”类名”反复
                long num;
                synchronized (nextUniqueNumberLock) {num = nextUniqueNumber++;}
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
4.2.7 其中这一块就是创立 代理类的 字节码,defineClass0 是通过类加载器、代理类名、字节码生成代理类。
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
        proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

【这块调用的 c 语言的本地办法】

ps: 在很多文章外面都用了 ProxyGenerator.generateProxyClass 的办法,而后用文件输入流把字节码内容写入文件外面,看到这里预计你应该晓得为什么会如此写了吧。

上面简略看下 输入的内容

该类所有的办法都被“反射”创立了,理论调用时就是调用这个针对的办法。



看到最初,咱们能够晓得一个反射 无非把 本身的“所有办法”赋予了另一个“凭空产生的”对象,让他应用本人的“权力”而已。

本文来源于:程序员 ken,专属平台有 csdn、思否(SegmentFault)、简书、开源中国(oschina)、掘金,转载请注明出处。

正文完
 0