visible相关知识
-
强大的CSS:focus-visible伪类真的太6了!一、快速了解CSS :focus-visible伪类 :focus-visible伪类是非常年轻的一个伪类,目前仅Chrome浏览器标准支持,但足够了。如果你是一个深入用户体验的开发者,这个伪类会非常有用。 :focus-visible伪类应用的场景是:元素聚焦,同时聚焦轮廓浏览器认为应该显示。 是不是很拗口?规范就是这么定义的。:focus-visible的规范并没有强行约束匹配逻辑,而是交给了UA(也就是浏览器)。我们通过真实的案例来解释下这个伪类是做什么用的。 在所有现代浏览器下,链接元素<a>鼠标点击的时候是不会有焦
-
jQuery tips and tricks1. How to judge element is visible ? if($(".nav").is(":visible")) { // This element is visible }This filter is very power, for example: <div > <div id="test">This element is hidden. </div> &n
-
html5中document新增了一个事件 visibilitychange在html5中document新增了一个事件 visibilitychange,这个事件在页面前台或后台切换时被触发,它也有个对应的属性visibilityState,用于检测当前页面的状态值为hidden还是visible。解法是,在hidden时记录当前时间,在visible时用当前时间减去之前记录的时间就为当前倒计时需要减去的时间,这也就不需要和后台沟通了,或者你直接在visible时刷新页面也行,下面是小样,测试一下 var b=getTime(); function getTime() { &n
-
导航栏效果<div class="header-bottom fix-hb"> <div class="container"> <div class="quick-access"> <div class="row"> <div class="col-md-2 visible-lg visible-md"> &nb
visible相关课程
visible相关教程
- 2.5 控制提示框渲染方式 与提示框渲染方式有关的配置项包括:配置名类型默认值说明renderModestringhtml指定渲染模式,支持 html、richTextextraCssTextstring附加在提示浮层的样式,仅当 renderMode = html时有效renderMode 用于指定提示框的渲染模式。当 renderMode = html时,提示框会以 DOM 形式追加到图表容器节点的后面,结果如:此时可以使用 extraCssText 为提示浮层增加更多样式,extraCssText 属性与 html 标签的 style 属性一样,接受 ;分割的 CSS 值,如:extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);'Tips:在官方文档中有提及另外一个属性:appendToBody,用于指定当 renderMode = html时,提示框的 DOM 是否追加到 <body>节点下,但实测无效,无论如何设置,渲染出的结果都只会追加到图表容器节点上。当 renderMode = richText时,内容将渲染在 canvas 上(SVG 模式目前还不支持),关于 richText的更多介绍,可参考 Echarts 富文本样式 一节。 html 与 richText 模式的主要区别有:当提示框超出图表范围时,html 模式下可以通过设置容器节点的 overflow: visible避免内容截断; richText 模式由于是在 canvas 内容渲染的,不受 CSS 影响,只能通过 confine 属性防止溢出;richText 的样式算法由 ECharts 实现,在各种环境下能稳定输出;html 模式则受上下文 CSS 环境影响,渲染效果可能会略有差异;html 模式下接受 HTML 语法,提示内容中的 HTML 字符串会被转换为 DOM 展示;richText 下,HTML 字符串则被当作普通字符串直接显示。例如,当指定 formatter: 'Data Item:<br /> {b0}: {c0}'渲染效果的差异:Tips:html 模式与普通的页面开发方法相似,更容易通过浏览器的 debugger 工具调试,所以个人更推荐使用 html 模式。canvas 则可应对一些没有 DOM 的环境,例如小程序中。
- 3.3 内存异常 内存不足可能会导致系统发生颠簸,这是因为虽然内存不足时系统会终止某些进程来释放内存,但又会继续启动其他进程。要查看内存不足的确凿证据,请检查二进制事件日志中 am_proc_died 和 am_proc_start 条目的密集程度。内存不足还可能会减慢任务切换速度,并且可能会阻止进行返回尝试(因为用户尝试返回到的任务已被终止)。如果启动器被终止,它会在用户触摸主屏幕按钮时重启,并且日志中会显示启动器重新加载其内容。查看历史指标二进制事件日志中的 am_low_memory 条目表示最后一个缓存的进程已终止。在此之后,系统开始终止各项服务。日志范例如下:grep "am_low_memory" bugreport-2015-10-01-18-13-48.txt10-01 18:11:02.219 4600 7513 I am_low_memory: 4110-01 18:12:18.526 4600 14112 I am_low_memory: 3910-01 18:12:18.874 4600 7514 I am_low_memory: 3810-01 18:12:22.570 4600 14112 I am_low_memory: 4010-01 18:12:34.811 4600 20319 I am_low_memory: 4310-01 18:12:37.945 4600 6521 I am_low_memory: 4310-01 18:12:47.804 4600 14110 I am_low_memory: 43查看系统颠簸指标关于系统颠簸(分页、直接回收等)的其他指标包括 kswapd、kworker 和 mmcqd 消耗的 CPU 周期。日志范例如下:CPU INFO (top -n 1 -d 1 -m 30 -t)User 15%, System 54%, IOW 28%, IRQ 0%User 82 + Nice 2 + Sys 287 + Idle 1 + IOW 152 + IRQ 0 + SIRQ 5 = 529 PID TID PR CPU% S VSS RSS PCY UID Thread Proc15229 15229 0 19% R 0K 0K fg root kworker/0:229512 29517 1 7% D 1173524K 101188K bg u0_a27 Signal Catcher com.google.android.talk24565 24570 3 6% D 2090920K 145168K fg u0_a22 Signal Catcher com.google.android.googlequicksearchbox:search19525 19525 2 6% R 3476K 1644K fg shell top top24957 24962 2 5% R 1706928K 125716K bg u0_a47 Signal Catcher com.google.android.GoogleCamera19519 19519 3 4% S 0K 0K fg root kworker/3:1 120 120 0 3% S 0K 0K fg root mmcqd/118233 18233 1 3% S 0K 0K fg root kworker/1:125589 25594 1 2% D 1270476K 75776K fg u0_a8 Signal Catcher com.google.android.gms19399 19399 2 1% S 0K 0K fg root kworker/2:2 1963 1978 1 0% S 1819100K 125136K fg system android.fg system_server 1963 1981 3 0% S 1819100K 125136K fg system android.display system_server获取内存快照内存快照是一种 dumpstate,其中会列出正在运行的 Java 进程和本机进程.日志范例如下:Total PSS by OOM adjustment: 86752 kB: Native 22645 kB: surfaceflinger (pid 197) 18597 kB: mediaserver (pid 204) 136959 kB: System 136959 kB: system (pid 785) 220218 kB: Persistent 138859 kB: com.android.systemui (pid 947 / activities) 39178 kB: com.android.nfc (pid 1636) 28313 kB: com.android.phone (pid 1659) 13868 kB: com.redbend.vdmc (pid 1646) 9534 kB: Persistent Service 9534 kB: com.android.bluetooth (pid 23807) 178604 kB: Foreground 168620 kB: com.google.android.googlequicksearchbox (pid 1675 / activities) 9984 kB: com.google.android.apps.maps (pid 13952) 188286 kB: Visible 85326 kB: com.google.android.wearable.app (pid 1535) 38978 kB: com.google.process.gapps (pid 1510) 31936 kB: com.google.android.gms.persistent (pid 2072) 27950 kB: com.google.android.gms.wearable (pid 1601) 4096 kB: com.google.android.googlequicksearchbox:interactor (pid 1550) 52948 kB: Perceptible 52948 kB: com.google.android.inputmethod.latin (pid 1566) 150851 kB: A Services 81121 kB: com.google.android.gms (pid 1814) 37586 kB: com.google.android.talk (pid 9584) 10949 kB: com.google.android.music:main (pid 4019) 10727 kB: com.motorola.targetnotif (pid 31071) 10468 kB: com.google.android.GoogleCamera (pid 9984) 33298 kB: Previous 33298 kB: com.android.settings (pid 9673 / activities) 165188 kB: B Services 49490 kB: com.facebook.katana (pid 15035) 22483 kB: com.whatsapp (pid 28694) 21308 kB: com.iPass.OpenMobile (pid 5325) 19788 kB: com.google.android.apps.googlevoice (pid 23934) 17399 kB: com.google.android.googlequicksearchbox:search (pid 30359) 9073 kB: com.google.android.apps.youtube.unplugged (pid 21194) 7660 kB: com.iPass.OpenMobile:remote (pid 23754) 7291 kB: com.pujie.wristwear.pujieblack (pid 24240) 7157 kB: com.instagram.android:mqtt (pid 9530) 3539 kB: com.qualcomm.qcrilmsgtunnel (pid 16186) 204324 kB: Cached 43424 kB: com.amazon.mShop.android (pid 13558) 22563 kB: com.google.android.apps.magazines (pid 13844) 4298 kB: com.google.android.apps.enterprise.dmagent (pid 13826)
- 8.2 源码分析论证 第一步我们先从 Proxy.newProxyInstance 方法探究,通过它在外部更为直观是可以获取代理类对象。class Client { public static void main(String[] args) { IPurchaseHouse houseOwner = new HouseOwner(); DynamicProxy dynamicProxy = new DynamicProxy(houseOwner); //第一步: 从Proxy.newProxyInstance方法入手 IPurchaseHouse agentA = (IPurchaseHouse) Proxy.newProxyInstance( houseOwner.getClass().getClassLoader(), new Class[]{IPurchaseHouse.class}, dynamicProxy ); agentA.inquiryPrice(); agentA.visitHouse(); agentA.payDeposit(); agentA.signAgreement(); agentA.payMoney(); agentA.getHouse(); }}第二步进入 Proxy.newProxyInstance 方法的定义,Proxy.newProxyInstance 有三个参数:loader(ClassLoader): 这个参数是实际被代理类的类加载器实例;interfaces(Class<?>[]): 代理类和被代理类共同实现的接口的 Class 数组;h(InvocationHandler): 代理拦截器接口,一般需要使用子类去实现该接口或匿名类去实现。 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone();//将interfaces的Class数组clone一份副本,赋值给intfs final SecurityManager sm = System.getSecurityManager(); if (sm != null) {//检查创建一个新的代理类需要权限 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ //注意点1: getProxyClass0方法拿到代理类的Class对象实例cl //注意传入的参数就是从外部传入的loader(被代理类的类加载器)、intfs(被代理类实现所接口的Class[]的副本) Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //注意点2: 拿到cl实例后,就通过反射机制创建代理类实例 final Constructor<?> cons = cl.getConstructor(constructorParams);//先拿到代理类的构造器Constructor实例cons final InvocationHandler ih = h; //检查代理类构造器是否是公有的public权限, 不是就会通过AccessController去修改访问权限以致于可以创建代理类实例 if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true);//将访问权限设置为可访问的 return null; } }); } //注意点3: 拿到构造器实例cons后,就到了最为关键的也就是最后一步,创建代理类实例。 //但是需要注意的是构造器反射传入的参数是h,也就是传入的InvocationHandler的实例,也可以进一步推论生成的代理类中存在以InvocationHandler为参数的构造器。 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }再一次来梳理下 newProxyInstance 源码流程:首先传入 loader、interfaces、h 三个参数,先将 interfacesclone 一份副本保存在 intfs 中,然后检查创建一个新的代理类所需要的权限,接着到了我们第一个注意点 1,就是通过 getProxyClass0 方法 (需要传入 loader 和 intfs 参数) 获得代理类的 Class 对象实例。拿到了代理类实例后,我们就通过反射的机制创建代理类实例;到了我们的 注意点二,通过代理类 Class 对象 cl 获得构造器对象 cons,并检查构造器对象是否是 public, 否则就强行修改访问权限;最后到了注意点三,通过 cons.newInstance 创建代理类对象,并且构造器反射中传入 h(InvocationHandler对象),说明我们可以推断一下生成的代理类中存在以 InvocationHandler 为参数的构造器。第三步进入 getProxyClass0 方法,传入的参数 loader 和 intfs,在该方法内部会委托给 proxyClassCache 的 get 方法,如果给定的类加载器中定义的代理类实现了给定的接口,直接返回缓存中的副本,否则它将通过 ProxyClassFactory 创建代理类. 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 //请注意上面那段英文注释: 如果给定的类加载器中定义的代理类实现了给定的接口, //那么就会直接返回缓存中的副本,否则它将通过ProxyClassFactory创建代理类 //注意点1: proxyClassCache;注意点2: ProxyClassFactory return proxyClassCache.get(loader, interfaces); }第四步 proxyClassCache 的介绍和定义,请注意创建 proxyClassCache 传入的构造器两个参数分别是: KeyFactory 和 ProxyClassFactory: /** * a cache of proxy classes */ private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());proxyClassCache 是一个 WeakCache<K,P,V> 对象,WeakCache<K,P,V> 中的 K 表示 key 值,P 代表参数,V 代表存储的值。此类用于缓存 (key, sub-key) -> value 键值对。内部具体实现是借助了 ConcurentMap<Object, ConcurrentMap<Object, Supplier<V>>>,Supplier 是一个接口,就一个 get 方法用于获得值,不过是泛型 V 的包装类,第一个 Object 就是 key(这里表达式不用泛型 K 是因为 key 值可以为 null),第二个就是 sub-key,那么它对应着什么呢?而且具体的缓存中也没有泛型 P 呢,这就需要引出另外一个函数接口 BiFunction<T, U, R>, 该接口内部存在 R apply(T t, U u) 方法,这个方法意思就是根据传入两个泛型 T 和 U 的值经过一定计算得到泛型 R 的值。在 WeakCache<K,P,V> 类中存在两个 BiFunction 对象:final class WeakCache<K, P, V> { private final ReferenceQueue<K> refQueue = new ReferenceQueue<>(); // the key type is Object for supporting null key private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>(); private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>(); private final BiFunction<K, P, ?> subKeyFactory; private final BiFunction<K, P, V> valueFactory; public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) { //根据K,P得到sub-key算法 this.subKeyFactory = Objects.requireNonNull(subKeyFactory); //根据K,P得到value算法 this.valueFactory = Objects.requireNonNull(valueFactory); } ...}在 WeakCahe 类中只有一个核心 get 方法,里面包含了整个缓存的逻辑,注意我们获取代理类 Class 对象,就是通过 proxyClassCache.get(loader, interfaces); 实际上就是调用 WeakCache 中的 get 方法。//K泛型是一级map的缓存key, P泛型传入的参数,分别对应外部传入的 loader和 interfacespublic V get(K key, P parameter) { ... //通过传入一级map的key,通过CacheKey拿到最终 Object cacheKey = CacheKey.valueOf(key, refQueue); // 懒初始化cacheKey对应的二级valuesMap, 如果valuesMap为空,就会新创建一个空的ConcurrentMap的valueMap,put到一级缓存map中 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) { //如果valuesMap为空,就会新创建一个空的ConcurrentMap的valueMap ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>()); //如果内部已经存在原来的oldValuesMap直接用它 if (oldValuesMap != null) { valuesMap = oldValuesMap; } } //------注意点1: subKeyFactory.apply(key, parameter)----- //根据传入的一级map的key和参数parameter,通过subKeyFactory中的apply方法获得sub-key, Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //然后通过我们的sub-key,从二级缓存的valuesMap中取的supplier对象 Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { //supplier这个对象可能是Factory或CacheValue<V>对象, //那么也就是supplier.get()方法可能是调用了Factory中的get方法或CacheValue<V>中的get方法//--------注意点2: supplier.get()----- V value = supplier.get(); //如果value不为空就返回value,结束整个get方法调用 if (value != null) { return value; } } //如果缓存中没有supplier对象 //或者supplier中get返回是null //或者Factory对象没有在CacheValue中被成功创建 //factory为null,就会创建一个新的Factory实例 if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } //supplier为null if (supplier == null) { //根据新创建的factory和subKey拿到supplier对象,如果valuesMap中存在subKey, factory键值对,就返回已经存在的值,没有直接返回null supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { //如果拿到supplier为null,supplier就变为了factory,这就是前面说supplier为一个factory supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { supplier = factory; } else { //通过valuesMap.get()拿到supplier supplier = valuesMap.get(subKey); } } } }我们来一起梳理下 WeakCache 的逻辑:首先 proxyClassCache 就是一个 WeakCache 实例对象,它有两个构造器参数 subKeyFactory 和 valueFactory, 创建 proxyClassCache 实例对应传入的是 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()) 中的 KeyFactory 和 ProxyClassFactory。然后在 WeakCache 内部存在二级 ConcurrentHashMap, 一级 map 的 key 就是 get 方法传入的 key, 通过这个 key 拿到 cacheKey, 从而拿到对应的 valuesMap二级map。然后又通过根据传入的一级 map 的 key 和参数 parameter,subKeyFactory 中的 apply 方法获得 sub-key, 通过 sub-key 拿到二级 map 中存储的 Supplier 对象,它可能是一个 CacheValue 也有可能是一个 Factory,最终通过 Factory 的 get 方法拿到实际的值。对于上述有两个核心注意点:注意点 1 -----> 获取 subKey 过程: 通过 subKeyFactory.apply(key,parameter) 拿到 sub-key://weakCache调用处: Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //KeyFactory的定义 private static final class KeyFactory implements BiFunction<ClassLoader, Class<?>[], Object> { @Override public Object apply(ClassLoader classLoader, Class<?>[] interfaces) { //可以看到是根据被代理类实现的接口的Class数组长度来决定选用哪一种subKey switch (interfaces.length) { //对于被代理类只实现了1个接口情况,也是最频繁一种 case 1: return new Key1(interfaces[0]); // the most frequent //对于被代理类只实现了2个接口情况 case 2: return new Key2(interfaces[0], interfaces[1]); //对于被代理类只实现了0个接口情况 case 0: return key0; //对于被代理类只实现了超过2个接口情况 default: return new KeyX(interfaces); } } }注意点 2----> supplier.get () 获取 value 的过程:我们都知道 supplier 对应的可以是 Factory 对象,也就是最后会调用 Factory 中的 get 方法。 @Override public synchronized V get() { // serialize access // 再一次检查supplier,如果传入从valuesMap拿到的不等于当前Factory对象,因为它可能已经变成CacheValue了,那就直接返回null Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { return null; } //创建一个新的value V value = null; try { //注意点出现,value最终会通过valueFactory.apply(key, parameter)拿到 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // 判断value是否为null assert value != null; // 将拿到的value包装成一个CacheValue CacheValue<V> cacheValue = new CacheValue<>(value); // 尝试把valuesMap中的对应subKey的Factory替换成cacheValue, //这就是为什么前面说过valuesMap中取出的Supplier可能是Factory可能是CacheValue,有没有种偷梁换柱的赶脚 if (valuesMap.replace(subKey, this, cacheValue)) { //替换成功后,并把cacheValue put到reverseMap中 reverseMap.put(cacheValue, Boolean.TRUE); } else { throw new AssertionError("Should not reach here"); } // 成功替换了新的CacheValue,并返回最终的值 return value; }通过上述代码分析,我们知道最终 value 获取是来自于 valueFactory 中 apply 方法,还记得 valueFactory 是啥吗?没错它就是 ProxyClassFactory 也就是最终定位到了 ProxyClassFactory 中的 apply 方法。这也就是为什么之前说如果缓存中有直接从缓存中返回缓存的副本,没有就在 ProxyClassFactory 中创建代理对象。第五步进入 ProxyClassFactory 中的 apply 方法进行探究,这是创建新的代理类 Class 对象唯一来源。 private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 所有生成的代理类名称统一前缀$Proxy private static final String proxyClassNamePrefix = "$Proxy"; private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { //验证类加载器是否将此接口的名称解析为同一个Class对象 Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } //验证interfaceClass的Class对象是否是一个接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //验证此接口不是重复的 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; //记录非公共代理接口的包,以便proxy类将在同一个包中定义。验证所有非公共代理接口是否在同一个包中 for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.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 = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成确定的代理类Class文件的byte数组 //------注意点ProxyGenerator.generateProxyClass----- byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { //通过defineClass0方法传入被代理类的类加载器、代理类唯一名称、生成的代理类文件反序列化成一个代理类的Class对象 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }再重新梳理一下 ProxyClassFactory 中的 apply 中的逻辑,首先做一些接口验证操作,然后通过 ProxyGenerator.generateProxyClass 生成确定的代理类 Class 文件的 byte 数组,最后通过 defineClass0 方法传入被代理类的类加载器、代理类唯一名称、生成的代理类文件反序列化成一个代理类的 Class 对象第六步进入 ProxyGenerator 中的 generateProxyClass 方法进行探究,主要通过它来生成代理类 Class 文件。generateProxyClass 方法传入的参数主要有: proxyName (唯一代理类名称), interfaces (需要代理的接口 Class 数组),accessFlags (访问权限标识): public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); //-----注意点----调用ProxyGenerator中的generateClassFile方法 final byte[] var4 = var3.generateClassFile(); //是否需要把生成Class文件保存在本地文件中,这个标识可以从外部进行配置 //boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles")) if (saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { try { int var1 = var0.lastIndexOf(46); Path var2; if (var1 > 0) { Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar)); Files.createDirectories(var3); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class"); } Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }第七步进入 generateClassFile() 方法,该方法主要生成 Class 文件: private byte[] generateClassFile() { //---注意点1----在生成的代理类中加入Object类几个默认方法比如常见的hashCode、equal、toString方法 this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); //取出代理类的接口的Class数组 Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; //----注意点2---遍历代理类的接口的Class数组,将代理类接口中的方法加入生成的代理类中 for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; //获得每个接口中定义的所有方法Method对象 Method[] var5 = var4.getMethods(); int var6 = var5.length; //然后再遍历所有的Method对象,并把它加入到生成的代理类中 for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } ... try { //----注意点3 生成的代理类中加入生成构造器方法generateConstructor---- this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); ... //----注意点4 生成的代理类中加入生成静态初始化块---- this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } ... //创建字节数组输出流 ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { ... var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); //往输出流写入生成代理类Filed字段相关信息 while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); //往输出流写入生成代理类Method方法相关信息 while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); //返回最终的Class文件的字节数组 return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }以上就是整个动态代理中代理类生成代码的过程,为了进一步弄明白动态的机制,比如 invoke 是怎么调用的呢。不妨我们把生成代码保存在本地文件中,然后一起来看下生成的代理类长啥样。public final class $Proxy1 extends Proxy implements IPurchaseHouse { private static Method m1; private static Method m7; private static Method m8; private static Method m2; private static Method m4; private static Method m3; private static Method m6; private static Method m0; private static Method m5; //----注意点1 生成代理类中的构造器中有个InvocationHandler参数---- public $Proxy1(InvocationHandler var1) throws { super(var1);//并把它传给它的父类Proxy中的h(InvocationHandler) } //生成equals方法 public final boolean equals(Object var1) throws { try { //---注意点出现super.h.invoke--- //委托父类`Proxy`中的`h`中的`invoke`方法来实现调用,并把当前生成的代理类实例this、当前方法对应的`Method`对象和参数数组`args`通过`invoke`回调出去,此时`InvocationHandler`子类中的`invoke`方法就会得以触发 return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } //生成Object类中默认的hashCode方法 public final int hashCode() throws { try { //---注意点出现super.h.invoke--- return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成Object类中默认的toString方法 public final String toString() throws { try { //---注意点出现super.h.invoke--- return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成代理接口中的payDeposit方法 public final void payDeposit() throws { try { //---注意点出现super.h.invoke 同理--- super.h.invoke(this, m7, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成代理接口中的signAgreement方法 public final void signAgreement() throws { try { //---注意点出现super.h.invoke 同理--- super.h.invoke(this, m8, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成代理接口中的payMoney方法 public final void payMoney() throws { try { //---注意点出现super.h.invoke 同理--- super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成代理接口中的getHouse方法 public final void getHouse() throws { try { //---注意点出现super.h.invoke 同理--- super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成代理接口中的visitHouse方法 public final void visitHouse() throws { try { //---注意点出现super.h.invoke 同理--- super.h.invoke(this, m6, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成代理接口中的inquiryPrice方法 public final void inquiryPrice() throws { try { //---注意点出现super.h.invoke 同理--- super.h.invoke(this, m5, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //生成的静态初始化块中,通过反射拿到对应的方法Method对象, //其中包括了Object中的方法和代理接口中的方法 static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m7 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("payDeposit"); m8 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("signAgreement"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("payMoney"); m3 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("getHouse"); m6 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("visitHouse"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m5 = Class.forName("com.mikyou.design_pattern.delegates.dynamic.IPurchaseHouse").getMethod("inquiryPrice"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }}其实当你看到了生成的代理类的代码后,你就会发现动态代理的机制就非常一目了然。你也就明白了 InvocationHandler 中的 invoke 方法什么时候调用了。那我们再来整体梳理下动态代理核心机制,其实最为核心的就是 InvocationHandler:首先,我们需要去实现一个 InvocationHandler 的子类,重写它的 invoke 方法,该方法中会回调三个参数: Object proxy, Method method, Object[] args, 然后在我们在 invoke 方法中只需要通过调用 method 的 invoke 方法,并传入 args 参数。然后我们去创建一个代理类实例是通过 Proxy.newProxyInstance, 会传入 InvocationHandler 子类实例,并把这个 InvocationHandler 子类实例作为生成新的代理类的构造器函数参数,并把这个参数传给新的代理类的父类 Proxy,在 Proxy 中会维护这个 InvocationHandler 子类实例 h。然后通过上述生成的代理类代码来看,会把所有方法都转成对应的 Method 对象,并在静态初始化块中通过反射进行初始化,然后每个方法内部调用实现,都会委托父类 Proxy 中的 h 中的 invoke 方法来实现调用,并把当前生成的代理类实例、当前方法对应的 Method 对象和参数数组 args 通过 invoke 回调出去,此时 InvocationHandler 子类中的 invoke 方法会得以触发,那么在其内部又转为 method 调用它的 invoke 方法,并传入 args 参数就相当于利用反射去调用这个方法。
- Node多进程开发进阶 一句话介绍
- 13-27 redis拓展 - 基于zset的数据 Spring Cloud分布式微服务实战
- 4. 从架构师角度看ARouter实现原理 移动端架构师电子书
visible相关搜索
-
vacuum
vagrant
val
validationgroup
validationsummary
vals
valueof
values
vant
variables
vb
vb if else
vb if语句
vb net
vb net 教程
vb net 数据库
vb net教程
vb net下载
vb 教程
vb 数组