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