前言:应用的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)、掘金,转载请注明出处。