extjs源码分析与开发实例宝典
‘壹’ log4j2源码分析
1.概述
1.1.组件概览
1.2.灵活的配置
1.2.1.插件发现机制
1.2.2.插件装配机制
1.2.3.配置文件基本元素与对象的映射关系
2.属性占位符
2.1.概述
2.2.Interpolator插值器
2.3.默认属性配置
3.Logger
3.1.配置示例
3.2.配置详解
3.3.Logger继承机制
4.Appender
4.1.概述
4.2.框架支持的Appender实现
4.3.常用Appender详解
4.3.1.ConsoleAppender
4.3.2.RollingFileAppender
5.Layout
5.1.概述
5.2.PatternLayout
5.2.1.模式字符串
6.Manager
7.Filter
在log4j2中,LogManager就是日志的门面,相当于slf4j-api中的LoggerFactory.
框架为每个类加载分配了一个单独的LoggerContext,用于管理所有创建出来的Logger实例.
ContextSelector则负责管理类加载器到对应的LoggerContext实例之间的映射关系.
log4j2中,有5个关键概念:
组件架构如下:
在log4j2中,一切皆插件,框架通过 PluginRegistry 扫描并发现插件配置.
PluginRegistry 支持两种扫描方式
插件配置以 PluginType 的形式保存在插件注册表中, PluginType 的作用类似于spring中 BeanDefinition ,定义了如何创建插件实例.
插件类通过 @PluginFactory 注解或者 @PluginBuilderFactory 注解配置孝冲插件实例的实例化和属性注入方式.
log4j2知道如何实例化插件后,我们就可以通过编写配置文件(如:log4j2.xml),进行插件的实例化和属性注入了.
Configuration 全局配置对象负责保存所有解析到的配置.
通过 ConfigurationFactory.getConfiguration() 可以使用不同的工厂生产不同的配御或置对象,不同的 Configuration 实现可以解析不同格式的配置,如:xml,yaml,json等.
以xml文件为例,文件中每个元素都会最终对应一个插件实例,元素名称实际就是PluginType中的name,实例的属性可以从子元素对应的实例获取,也可以从自身元素的属性配置获取.
因此,xml中dom树的元素嵌套关系,也就是log4j组件实例的引用嵌套关系.
xml,yaml,json格式文件都可以描述这种嵌套关系,因此log4j2中定义了与文件格式无关的数据结构,Node来抽象配置.
AbstractConfiguration.setup() 负责提取配置,形成Node树.
AbstractConfiguration.doConfigure() 负责根据Node树,进行插件实例化和属性注入.
在log4j2中,环境变量信息(键值对)被封装为StrLookup对象,该对象作用类似于spring框架中的PropertySource.
在配置文件中,基本上所有的值的配置都可以通过参数占位符引用环境变量信息,格式为:${prefix:key}.
Interpolator内部以Map<String,StrLookup>的方式,封装了很多StrLookuo对象,key则对应巧拆歼参数占位符${prefix:key}中的prefix.
同时,Interpolator内部还保存着一个没有prefix的StrLookup实例,被称作默认查找器,它的键值对数据来自于log4j2.xml配置文件中的<Properties>元素的配置.
当参数占位符${prefix:key}带有prefix前缀时,Interpolator会从指定prefix对应的StrLookup实例中进行key查询,
当参数占位符${key}没有prefix时,Interpolator则会从默认查找器中进行查询.
Interpolator中默认支持的StrLookup查找方式如下(StrLookup查找器实现类均在org.apache.logging.log4j.core.lookup包下):
注意:Properties元素一定要配置在最前面,否则不生效.
log4j2框架会根据LoggerConfig的name建立对象之间的继承关系.这种继承机制与java的package很像,name以点进行名称空间分割,子名称空间继承父名称空间.
名称空间可以是全限定类名,也可以是报名.整个配置树的根节点就是RootLogger.
举例:假如我们的配置的Logger如下:
当通过LogManager.getLogger(name)获取Logger实例时,会根据name逐级递归直到找到匹配的LoggerConfig,或者递归到Root根节点为止.
追加器,负责控制Layout进行LogEvent的序列化,以及控制Manager对序列化后的字节序列进行输出.
在log4j2.xml配置文件中,配置方式如下:
控制台追加器,用于把日志输出到控制台,一般本地调试时使用.
配置示例如下:
文件滚动追加器,用于向本地磁盘文件中追加日志,同时可以通过触发策略(TriggeringPolicy)和滚动策略(RolloverStrategy)控制日志文件的分片,避免日志文件过大.
线上环境常用.
常用的触发策略包含两种:
滚动策略的实现包含两种:
配置示例如下:
布局对象,职责是把指定的LogEvent转换成可序列化对象(如:String),或者直接序列化成字节数组.
log4j2支持很多的序列化格式,如:普通模式字符串,JSON字符串,yaml字符串,XML格式字符串,HTML字符串等等.
类体系如下:
模式布局是我们最常使用的,它通过PatternProcessor模式解析器,对模式字符串进行解析,得到一个List<PatternConverter>转换器列表和List<FormattingInfo>格式信息列表.
在PatternLayout序列化时,会遍历每个PatternConverter,从LogEvent中取不同的值进行序列化输出.
模式字符串由3部分组成,格式为:%(格式信息)(转换器名称){选项1}{选项2}...
模式字符串的格式为:
%-(minLength).-(maxLength)(转换器名称){选项字符串}
minLength代表字段的最小长度限制,当字段内容长度小于最小限制时,会进行空格填充.
minLength前面的-负责控制对齐方式,默认为右对齐(左边空格填充),如果加上-,则会切换为左对齐方式(右边空格填充)
maxLength代表字段的最大长度限制,当字段内容长度大于最大限制时,会进行内容阶段
maxLength前面的-负责控制阶段方向,默认为左侧阶段,如果加上-,则会切换为右侧阶段
minLength和maxLength之间用点分隔.
格式信息中所有属性都是可选的,不配置,则使用默认值
log4j2会通过 PluginManager 收集所有类别为Converter的插件,同时分析插件类上的 @ConverterKeys 注解,获取转换器名称,并建立名称到插件实例的映射关系.
PatternParser识别到转换器名称的时候,会查找映射.
框架支持的所有转换器如下:
有时我们需要对特定的转换器进行特殊的配置,如:给DatePatternConverter配置时间格式,这个时候需要通过选项字符串配置.
PatternParser会提取模式字符串中的所有选项,保存在一个List<String>中,每个{}包裹的内容作为一个选项.
当创建转换器时,框架会自动扫描转换器类中声明的静态工厂方法newInstance,同时支持两种可选的形参,一种是Configuration,另一种String[]则会注入选项列表.
选项列表的识别由不同的转换器各自定义.
最后,以一个实际的例子解释配置:
日志会输出时间,类名,方法名,消息以及一个换行符.
同时,我们给DatePatternConverter指定了了时间格式,并且限制全限定类名最小长度为5,右截断,最大为10,左对齐.
管理器的职责主要是控制目标输出流,以及把保存在ByteBuffer字节缓冲区中的日志序列化结果,输出到目标流中.
如:RollingFileManager需要在每次追加日志之前,进行滚动检查,如果触发滚动还会创建新的文件输出流.
manager继承体系如下:
过滤器的核心职责就是对 LogEvent 日志事件进行匹配,匹配结果分为匹配和不匹配,结果值有3种:接受,拒绝,中立.可由用户自定义匹配和不匹配的行为结果.
所有实现了 Filterable 接口的组件都可以引用一个过滤器进行事件过滤,包含 LoggerConfig 和 AppenderControl 等.
框架实现的过滤器如下:
‘贰’ ExtJS是什么,一位学长说的,对这个从未听说
1.extja是什么?
答: ExtJS是一种主要用于创建前端用户界面,是一个与后台技术无关的前端ajax框架。
功能丰富,无人能出其右
无论是界面之美,还是功能之强,ext的表格控件都高居榜首。
单选行,多选行,高亮显示选中的行,推拽改变列宽度,按列排序,这些基本功能咱们就不提了。
自动生成行号,支持checkbox全选,动态选择显示哪些列,支持本地以及远程分页,可以对单元格按照自己的想法进行渲染,这些也算可以想到的功能。
再加上可编辑grid,添加新行,删除一或多行,提示脏数据,推拽改变grid大小,grid之间推拽一或多行,甚至可以在tree和grid之间进行拖拽,啊,这些功能实在太神奇了。更令人惊叹的是,这些功能竟然都在ext表格控件里实现了。
呵呵~不过ext也不是万能的,与fins的ecside比较,ext不能锁定列(薯仔说1.x里支持锁定列,但是2.0里没有了,因为影响效率。),也没有默认的统计功能,也不支持excel,pdf等导出数据。另外fins说,通过测试ecside的效率明显优于ext呢。:)
[编辑本段]Ext发展史
1、第一只“出海”的YUI-Ext只是作者Jack打算对基于BSD协议的Yahoo!UI库进行自定义的扩展,但后来一度风头盖过其父辈YUI,足以说明 大家对它的热情,很多人把它投入项目人并不十分了解它。分析人士打了一比喻:就好比尚未谋面, 并不了解一个人的家庭、教育、品行等背景,只因为他有一副精致漂亮的外观,就对其陷入了疯狂的倾慕之中。因此分析人士建议,在投入项目前,要认真仔细地了 解EXT的内在原理和与其他Ajax库不同地方。
2、在2006年初 ,Jack Slocum(杰克斯·洛克姆 ) 就一套公用设施扩建为Yahoo! User Interface (YUI) 库而工作。这些扩展很快组织成一个独立的库代码并以” yui-ext” 的名义下发布。
3、在2006年秋天,Jack发行了版本为0.33的yui-ext,而最终被证明为最后版本的代码,根据这名字(下开放源代码DSB许可)。在年底之前,这个库已大受欢迎, 名字被简化为Ext,反映了它作为一个框架的成熟和独立。
该公司成立于2007年初,Ext现在为双执照,使用LGPL和一个商业执照。
4、在2007年4月1日,发布1.0正式版。
5、直至今日(2008年4月1日)ExtJS已发展涵盖美国、日本、中国、法国、德国等全球范围的用户,现在的版本为Ext-3.0
6、官方在2009年4月14-16日的首次Ext Conference中发布了Ext的3.0 RC版本。
7、2009年5月4日,Ext的3.0 版本发布。
[编辑本段]什么是EXT
1、ExtJS可以用来开发RIA也即富客户端的AJAX应用,是一个用javascript写的,主要用于创建前端用户界面,是一个与后台技术无关的前端ajax框架。因此,可以把ExtJS用在.Net、Java、php等各种开发语言开发的应用中。ExtJs最开始基于YUI技术,由开发人员JackSlocum开发,通过参考JavaSwing等机制来组织可视化组件,无论从UI界面上CSS样式的应用,到数据解析上的异常处理,都可算是一款不可多得的JavaScript客户端技术的精品。
2、Ext的UI组件模型和开发理念脱胎、成型于Yahoo组件库YUI和Java平台上Swing两者,并为开发者屏蔽了大量跨浏览器方面的处理。相对来说,EXT要比开发者直接针对DOM、W3C对象模型开发UI组件轻松。
[编辑本段]ExtJs UI Engine简介
ExtJs初期仅是对Yahoo! UI的对话框扩展,后来逐渐有了自己的特色,深受网友的喜爱。 发展至今, Ext除YUI外还支持Jquery、Prototype等的多种JS底层库,让大家自由地选择。该框架完全基于纯Html/CSS+JS技术,提供丰富的跨浏览器UI组件,灵活采用JSON/XML数据源开发,使得服务端表示层的负荷真正减轻,从而达到客户端的MVC应用!
ExtJs支持多平台下的主流浏览器 Internet Explorer 6+ FireFox 1.5+ (PC, Mac) Safari2+、Opera9+。在使用的厂家包括IBM、Adobe、Cisco和更多。ExtJs官方网站www.extjs.com
[编辑本段]Ajax主流框架与ExtJS
JQuery、 Prototype和YUI都属于非常核心的JS库。虽然YUI,还有最近的JQuery,都给自己构建了一系列的UI器件(Widget),不过却没有一个真正的整合好的和完整的程序开发平台。哪怕是这些低层的核心库已经非常不错了,但当投入到真正的开发环境中,依然需要开发者做大量的工作去完善很多缺失之处。而Ext就是要填补这些缺口。主流开源框架中只有Dojo像Ext一样,尝试着提供整合的开发平台。相比Dojo这个出色的工具包,我们认为 Ext能提供一个粘合度更高的应用程序框架。Ext的各个组件在设计之时就要求和其它Ext组件组合一起工作是无缝合作的。这种流畅的互通性,离不开一个紧密合作的团队,还必须时刻强调设计和开发这两方面目标上的统一,而这点是很多开源项目未能做到的。从构建每一个组件开始,我们始终都强调组件的外观、性能、互通性和可扩展性,而我们认为组件已经达到了这几点的要求。
Ext绝对可以单独使用。实际上,除了有特定的要求,推荐单独使用Ext,这样的话文件占位更小,支持和整合也更紧密。我们也支持与jQuery、 YUI或Prototype整合使用,作为低层库的角色出现,以提供处理各种核心的服务,如DOM和事件处理,Ajax连接和动画特效。使用整合方式的一个原因是它们已具备了一些特定的器件而Ext并没有原生支持——像YUI的History控件便是一个典型的例子。这时,Ext需要依赖YUI这个库的底层来实现History控件,这样一来的话也可免去Ext自身底层库,从而减少了整个程序的内存占用。另一个使用整合方式的原因是,对于许多已在使用其他底层库的程序,你可能希望逐步加入Ext。总之,如果已经有了其他库,Ext可已利用它们。我们的宗旨是为用户提供各种可能性和性能上的优化。而事实是,只要实现了相对应的底层库接口,为任意一个框架添加上适配器是没有问题的——人们可以轻松地将Dojo、Moo、Ajax.NET,或其它JS库转变为 Ext的底层。
Ext从2.x开始收费,这给他的应用前景带来一些问题。不过对国内开发人员的影响不大,毕竟是客户付款。
[编辑本段]Ext学习及应用经验小结
一、理解Html DOM、Ext Element及Component
要学习及应用好Ext框架,需要理解Html DOM、Ext Element及Component三者的区别。
Ext是基于Web的富客户端框架,其完全是基于标准W3C技术构建设的,使用到的都是HTML、CSS、DIV等相关技术。Ext最杰出之处,是开发了一系列非常简单易用的控件及组件,我们只需要使用这些组件就能实现各种丰富多彩的UI的开发。
无论组件有多少配置属性、还是事件、方法等等,其最终都会转化为HTML在浏览器上显示出来,而每一个HTML页面都有一个层次分明的DOM树模型,浏览器中的所有内容都有相应的DOM对象,动态改变页面的内容,正是通过使用脚本语言来操作DOM对象实现。
仅仅有DOM是不够的,比如要把页面中的某一个节点移到其它位置,要给某一个节点添加阴影效果,要隐藏或显示某一个节点等,我们都需要通过几句 javascript才能完成。因此,Ext在DOM的基础上,创建了Ext Element,可以使用Element来包装任何DOM,Element对象中添加了一系列快捷、简便的实用方法。
对于终端用户来说,仅仅有Element是不够的,比如用户要显示一个表格、要显示一棵树、要显示一个弹出窗口等。因此,除了Element以外,Ext 还建立了一系列的客户端界面组件Component,我们在编程时,只要使用这些组件Componet即可实现相关数据展示及交互等,而 Component是较高层次的抽象,每一个组件在渲染render的时候,都会依次通过Element、DOM来生成最终的页面效果。
在使用Ext开发的应用程序中,组件Component是最高层次的抽象,是直接给用户使用的,Ext Element是Ext的底层API,主要是由Ext或自定义组件调用的,而DOM是W3C标准定义的原始API,Ext的Element通过操作DOM 来实现页面的效果显示。
在Ext中,组件渲染以后可以通过访问组件的el属性来得到组件对应的Element,通过访问Element的dom属性可以得到其下面的DOM对象。另外,我们可以通过通过Ext类的快捷方法getCmp、get、getDom等方法来得组件Component、Ext元素Element及DOM节点。比如:
var view=new Ext.Viewport();//创建了一个组件Component
view.el.setOpacity(.5);//调用Element的setOpacity方法
view.el.dom.innerHTML="Hello Ext";//通过Element的dom属性操作DOM对象
再看下面的代码:
var win=new Ext.Window({id:"win1",title:"我的窗口",width:200,height:200});
win.show();
var c=Ext.getCmp("win1");//得到组件win
var e=Ext.get("win1");//根据id得到组件win相应的Element
var dom=Ext.getDom("win1");//得到id为win1的DOM节点
二、熟悉ext组件体系
Ext2.0对整个框架进行了重构,最为杰出的是推出了一个以Component类为基础的组件体系,在Component类基础上,使用面向对象的方法,设计了一系列的组件及控件。因此,要能游刃有余地使用Ext,熟悉Ext组件体系是最基本的。
在《ExtJS实用开发指南》中,有如下面一幅组件图:
通过组件结构图我们可以一目了然的看出整个Ext组件继承及组成体系,当使用一个组件的时间,了解他的继承体系,这样可以便于我们掌握组件的各种特性。
三、掌握核心控件
控件其实也是组件,比如用于显示树信息的TreePanel、用于显示表格的GridPanel及EditorGridPanel,还有代表应用程序窗口的Ext.Window等都属于Ext控件。在使用Ext的时候,一定要掌握一些核心控件,特别是处于基类的控件。比如上面提到的几个控件,他们都是继承于面板Panel,所以我们要重点掌握面板这个核心控件的特性。比如面板由以下几个部分组成:一个顶部工具栏(tbar)、一个底部工具栏(bbar)、面板头部(header)、面板尾部(bottom)、面板主区域(body)几个部分组成。面板类中还内置了面板展开、关闭等功能,并提供一系列可重用的工具按钮使得我们可以轻松实现自定义的行为,面板可以放入其它任何容器中,面板本身是一个容器,他里面又可以包含各种其它组件。只要掌握了Panel的应用,那么学习TreePanel、Window等就会变得简单得多。
同样的道理,对于Ext的表单字段来说,不管是ComboBox,NumberField、还是DateField,他们其它都是 Ext.form.Field类的子类,在他上面定义了表单字段的各种基本操作及特性。在学习使用表单字段组件时,一定要重点研究Field这个类,掌握他的主要方法、事件等,就能有助于更好的学习使用其它的字段。
四、学习及研究示例
由于javascript语言非常灵活,不像静态强类型语言(比如Java)那样有固定的代码设计模式,而往往是不同的人就有不同的编程风格。在实际应用开发中,只有见多识广,才能在自己的在脑中建立一个开发库。
学习别人的示例对于我们开发帮助会非常大,示例包括基本组件的应用、综合应用等多个方面。在此,简单推荐几个。
1、Ext官方示例,在ext项目下载包的examples目录中,包括各个控件的基本应用演示,同时还有一些比较复杂的组合示例,有简有繁,非常适合初学者认真研究。
2、Vifir推出的示例,Vifir推出的一些示例主要包括两类,一种是开源的示例应用,另外一种是针对VIP用户的实用示例。开源的示例主要是指 wlr单用户blog系统,这个一个集合了前后台技术的ext综合示例,而针对VIP用户的实用示例则是可以作为开发骨架或扩展组件的示例。
3、其它示例,在ext社区中还有很多比较优秀的ext应用示例,有些只是一个应用演示,虽然没有提供源码下载,但我们可以直接下载引用的js文件来得到这些示例的ext应用代码,同样能取起到非常好的学习效果。
五、多运用
Ext看起来是非常简单的东西,稍有点编程知识的人,按照《ExtJS实用开发指南》中的入门指南,半小时就能学会使用Ext。然而,当准备使用Ext开发一个项目时,却不知道从何处入手,或者是在使用Ext的时候,出了一点小问题自己不知道该如何解决。编程是一门实践性的科学,仅仅靠看书、看别人写的代码是远远不够的,因此,必须多做实践才行,只有通过不断的练习,大量的使用,才能对Ext的组件特性、事件、事件处理机制以及与服务器端交互接口等深入的掌握,只有多做运用,深入了解ext的组件的工作原理及机制,才能编写出高级的Ext的应用。
六、熟读Ext项目的源代码
如果要想深入应用Ext,那么阅读Ext项目的源代码这是必不可少的环节,Ext的代码质量非常高,通过阅读他的代码我们可以更加深刻的了解 javascript面向对象编程,Ext代码中包含了很多高级的js技巧以及设计模式。在使用Ext的过程中,我们经常根据项目的需要对Ext组件进行扩展,设计自己的组件或控件,而如何实现一个自定义的Ext组件,我们可以从Ext的各个组件源代码中找到答案。
Ext的源代码在Ext项目的source目录。读Ext源码,并不一定非要从某一个地方开始,而组件核心代码Component.js、容器组件代码 Container.js、面板Panel.js等这些是必看的; core目录中的Element.js、Ext.js等也是必看的。当需要从一个控件进行扩展的时候,最好能简单看一看这个控件的源代码。
2.extjs应该很少应用吧 我做jsp的,从来就没有用过这些东西·
‘叁’ Eclipse怎么看ExtJS源码
Eclipse查看ExtJS源码的方法
1、 首先把extjs/src/debug.js文件引入所要调试的页面
2、 在需要调试的地方输入以下语句即可
Ext.log(‘This is a ExtJs debugger’);
3、 运行页面时,凡是在添加Ext.log信息的页面下方EXT都会自动生成调试信息,并且显示在CONSOLE控制台中,类似eclipse的CONSOLE。另外在控制台右侧窗口中输入要执行的代码,点击【run】可以查看运行结果,比如Ext.getCmp(‘comId’).getValue();
4、 ExtJs调试控制台 console默认是渲染到body下,如果页面本身渲染的区域覆盖住了调试控制台(用该组件Ext.Viewport时可能会覆盖),则可以动态的修改console渲染的位置,可以修改源码debug.js。在代码
cp.render(Ext.getBody());处可以修改为以下
cp.render(Ext.get('debug'));//debug为页面中dom元素
‘肆’ ARouter源码分析
Annotation Processing Tool,自定义注解处理器。
搞Android的基本上都知道这个吧。许多第三方库都使用了APT去实现自己的功能,比如butterknife,比如X2C,比如我们要讲的ARouter。
其基本做法是:
一般是自定义gradle Transform + ASM,实现AOP,可以在编译期修改project和第三方依赖库中的class文件(比如ARouter源码中的arouter-gradle-plugin模块),与APT主要是生成.java文件不同,ASM操作的是.class文件。
自定义gradle Transform功能很强大,可以与ASM结合,修改.class,也可以操作资源文件(比如统一压缩图片,转png大图为webp等)。
至于ASM,基于修改.class文件,我们即可以用ASM来插桩统计方法耗时,也可以用来实现自动化埋点,甚至是修改第三方lib中的crash...
使用方法可以看 ARouter 。
带着问题看源码,这里主要的问题是:
ARouter的核心方法。
这个方法算是核心中的核心了。
其实也只做了两件事情
总的来说,只干了一件事情,扫描所有的dex文件,找到com.alibaba.android.arouter.routes包下的所有文件并返回。这里的操作都是耗时操作。
但是com.alibaba.android.arouter.routes包下都是什么文件呢?
比如:
比如:
比如:
比如:
这些都是APT生成的辅助类。
①项目编译期,通过APT,生成辅助类(所有的辅助类包名都是com.alibaba.android.arouter.routes)
包括
②ARouter#init初始化的时候,扫描dex文件,找到①生成的辅助类文件(也即是包com.alibaba.android.arouter.routes下的文件),放到routerMap中
注意,这里没有实例化IRouteGroup,IRouteGroup的信息都在IRouteRoot中,这样做的目的是为了实现分组route的加载,用到了哪个group的route的信息,才会加载这个group的信息,没用到就不加载。这里可以仔细想想IProviderGroup/IInterceptorGroup/IRouteGroup的区别。
该方法执行完了以后,
至此,就完成了初始化路由表的操作。
我们回过头来瞄一眼ARouter#init,里面初始化路由表以后,执行了_ARouter#afterInit
这一句看着很熟悉。
页面路由跳转/IProvider/拦截器都是ARouter.getInstance().build("/test/activity").navigation()这种形式的话,我们就先从拦截器interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();开始分析吧。
虽然这里我们是想看拦截器的实现,但是要明确一点:InterceptorServiceImpl是IProvider的实现类,获取InterceptorService也就是获取一个IProvider。有一点绕,简单来说,ARouter使用一个IProvider来实现拦截器的初始化。
后面的逻辑就变成了获取一个IProvider上了。
也即是ARouter.getInstance().build("/arouter/service/interceptor").navigation()方法中,ARouter.getInstance().build("/arouter/service/interceptor")做的事情就是创建一个Postcard,其path是"/arouter/service/interceptor",group是"arouter".
简单来说,这里做的事情有
注意addRouteGroupDynamic(postcard.getGroup(), null)这个方法,通过groupName去groupIndex中查找,那"arouter"对应的是谁呢?正是ARouter$$Group$$arouter.class。
反射创建ARouter$$Group$$arouter对象,并执行ARouter$$Group$$arouter#loadInto方法
现在我们回过头来继续看LogisticsCenter#completion
总结一下LogisticsCenter#completion方法做了啥:
以ARouter.getInstance().build("/arouter/service/interceptor").navigation()举例说明就是:
init方法做的事情很单一,就是一次性实例化全部的拦截器,存到 Warehouse.interceptors中。(想想为什么要这么做?)
这样,ARouter.getInstance().build("/arouter/service/interceptor").navigation()就分析完了,ARouter#init的时候,会创建所有的拦截器实例。ARouter.getInstance().build("/arouter/service/interceptor").navigation()方法返回的是InterceptorServiceImpl的实例。
另外,_ARouter#navigation(Context, Postcard, int, NavigationCallback)方法的最后,调用了_ARouter#_navigation
方法看着长,内容却很简单:
这样, ARouter#init 就分析完了, 总结 一下:
另外使用ARouter.getInstance().build("path").navigation()方法 获取IProvider的流程 如下:
Activity跳转的流程如下:
至此,我们回答了问题1/问题2和问题4.
下面我们来看下剩下的问题
问题3:拦截器是如何生效的?
我们可以看看_ARouter
执行_ARouter#navigation的时候,执行了interceptorService.doInterceptions方法,前面我们已经知道,执行了interceptorService实际上是InterceptorServiceImpl。
这里一个一个调用拦截器,如果有拦截器拦截,就中断调用,否则,调用下一个拦截器进行拦截。
所以, 拦截器 总结如下
最后,我们来看下最后一个问题。
问题5 ARouter的Gradle Plugin做了哪些优化?
该问题的关键是LogisticsCenter#loadRouterMap
上面的是gradle plugin修改之前的,下面的loadRouterMap是gradle plugin修改之后的。
瞄一眼register方法我们就能明白,这还是之前的那一套,跟不使用gradle plugin不同的地方在于,这里不需要扫描dex去找IRouteRoot/IInterceptorGroup/IProviderGroup,在编译期,gradle plugin就已经找到了这些,然后生成新的loadRouterMap方法。
‘伍’ Cesium 50个实例集合,讲解、分析(内附源码)
为方便快速测试斗桥 Cesium Demo使用,添加了一些演示demo并且提供了webpack打包的示例。目前整理了20个demo,后面会对demo进行讲解。
详情源码连接:
Cesium 50个实例集合,讲解、分析(内附源码)
示例概要说明:
1.Draw.js 动态绘制 点、线、面
2.Scanning.js 一个简单的扫描功能
3.Spline.js 样条插值
4.HeightProperty.js 一个类似淹没分析的效果
5.VectorTile.js 矢量切片加载
6.Ellipsoids.js 圆特效
7.Sensor.js 传感器,可设置角度,位置等空液猛
8.PolylineArrow.js 动态线效果
9.VolumesCZML.js 加载czml文件 支持传感器等动画特效
10.VolumesApi.js VolumesApi 调用效果
11.Fly.js 绕点飞行
12.Radar.js 气象雷达动态图埋态
13.VisibleAnalysis.js 通视分析
14.EagleEye.js 鹰眼地图
15.Measure.js 测量
16.Translucency.js 地球半透明,可做地下管线、矿山项目
17.Interior.js 地球内部,结合VectorTile.js会出很牛的效果
18.3dtiles加载
19.可视域分析_3DTiles
20.站心坐标转WGS84
操作说明:
1.npm初始化第三方库
npm install
2.在根目录创建文件夹,文件夹名称为“ThirdParty” 。
3.将根目录下“node_moles\cesium\Build\Cesium”拷贝到“ThirdParty” 下。
4.编译
npm run build
5.运行
npm run start
注:可以不要“2”、“3”步骤的,只是为了初学者能更快的找到源码位置。
‘陆’ HttpRunner2.x源码分析——生成报告
在 api.py 中的run_tests函数中,httprunner执行测试用例并生成报告
api.py的_aggregate函数:
得到测试结果汇总后,通过 report.py 的 stringify_summary 函数将测试汇总串化,以便转储json文件并生成html报告。
串化测试汇总后,通过 report.py 的 render_html_report 函数生成html报告
httprunner使用jinja2生成测试报告,下文简单介绍jinja2的用法,想要更深入了解请查看:
在jinja2中有3中语法:
过滤器相当于jinja2中的内置函数,可以对变量进行相应的处理,常用的过滤器有:
过滤器用法:在竖明变量后使用管道 | 调用,可链式调用
for循环由于迭代python中的列表和字典
宏相当于jinja2的自定义函数,定义宏的关键字是macro,后接宏的名称和参数
调用宏
jinja2中最强大的部分就是模板继承。模板继承允许我们创建一个基本(骨架)文件,其他文件从该骨架文件继承,然后针对自己需要的地方进行修或或改。
jinja2的骨架文件中,利用block关键字表示其包涵的内容可以进行修改。
以下面的骨架文件base.html为例:
jinja2模块中有一个名为Enviroment的类,这个类的实例用于存储配置和全局对象,然后从文件系统或其他位置中加载模板。
大多数应用都在初始化的时候撞见一个Environment对象,并用它加载模板。Environment支持两种加载方式:
使用包加载器来加余团告载文档的最简单的方式如下:
其中:
文件系统加载器,不需要模板文件存在某个Python包下,可以直接访问系统中的文件。
‘柒’ RefreshScope源码分析
一、 属性
eager 默认为true,会进行refresh范围内的全部bean进行提前实例化
二、 方法
refresh方法
发送RefreshScopeRefreshedEvent事件;
EventListener监听方法
重新创建Bean对象闹轮旁;
这个是核心的Bean处理类,主要定义了Bean的生命周期信息,Bean创建的工厂信息,以及Bean信息定义的缓存,桐穗除外还定义了Bean实例化前的动态修改bean信息的方法,实现当监听到配置信息更新事件后能够从缓存获取Bean的定义信息,对需要重新修改bean定义的进行动态销毁和重建;
BeanLifecycleWrapper
包装bean生命液橡周期的包装类,主要持有了ObjectFactory,利用其进行Bean对象的创建;
BeanLifecycleWrapperCache
BeanLifecycleWrapper的缓存,提高bean信息定义的获取效率;
*BeanFactoryPostProcessor *
BeanFactoryPostProcessor的主体是BeanFactory,并且该接口中只定义了一个方法,其将会在ApplicationContext内部的BeanFactory加载完bean的定义后,但是在对应的bean实例化之前进行回调。所以通常我们可以通过实现该接口来对实例化之前的bean定义进行修改。
BeanFactoryPostProcessorBean(工厂的后置处理器)
在bean真正实例化前,通过注入的BeanFactory对上下文动态修改
(Bean注册器的后置处理器)
更早于BeanFactoryPostProcessor动态处理上下文中的BeanDefinition信息
‘捌’ Reference 和 Reference queue 精髓源码分析
每个Reference状态列表是Active、Pending、Enqueued、Inactive (本文也将基于以下这四种状态做出源码解读)
状态间主要的标识:
用Reference的成员变量queue与next(类似于单链表中的next)来标识
有了这些约束, GC 只需要检测next字段就可以知道是否需要对该引用对象(reference)采取特殊处理
什么特殊处理?
GC在状态active发送变化时,会主动把 weakReference中的 reference置null,PhantomReference 中的 reference 无视 (PhantomReference的get方法重写返回null,可能就因为这原因就没有置null了),并给next赋值this,以及pending等。(别问我为啥,测出来的。。。。培训出来就这水平了)
描述:
active 状态下的reference会受到GC的特别对待,GC在发现对象的可达性变成合适的状态后将变更reference状态,具体是变更为pending还是Inactive,取决于是否注册了 queue
特点:
源码分析:
描述:
pending-Reference列表中的引用都是这个状态。
它们等着被内部线程ReferenceHandler处理(调用ReferenceQueue.enqueue方法)
没有注册的实例不会进入这个状态!
特点:
源码分析:
3.似乎明白了 ReferenceQueue.ENQUEUED 的含义
描述:
调用ReferenceQueue.enqueued方法后的引用处于这个状态中。
没有注册的实例不会进入这个状态。
特点:
源码分析:
可以看出 整体是一种先进后出的栈结构。head作为栈头。r.next指向成员变量 head,如果head为null,那么就指向自己(为了后面的压栈好找元素)
描述:
最终状态,处于这个状态的引用对象,状态不会在改变。
两种途径到这个状态:
1.referenceQueue 调用 reallyPoll ,
2.不注册 referenceQueue,reference state变化时。
特点:
源码分析:
引用:
https://liujiacai.net/blog/2015/09/27/java-weakhashmap/#%E5%BC%B1%E5%BC%95%E7%94%A8%EF%BC%88weak-reference%EF%BC%89
‘玖’ 源码分析->一个应用到底有几个Context
相信很多人都知道是这样计算的,那到底为什么是这样呢?
源码分析基于Android28源码
什么是Context呢?可以理解为上下文、运行环境,当需要获取资源、系统服务以及启动Activity、Service用到,也可以通过它跟系统交互。
通过以下继承关系可以看出,Activity是继承ContextWrapper
ContextWrapper内部有一个Context类型的成员变量mBase
mBase是通过attachBaseContext()方法赋值
是创建Activity的关键,
主要工作
(1)createBaseContextForActivity()内部实例化ContextImpl 对象;
(2)mInstrumentation.newActivity()内部通过反射实例化Activity对象;
(3)activity.attach()内部会调用attachBaseContext()方法给mBase对象赋值;
通过以下继承关系可以看出,Application是继承ContextWrappe
是创建Application的关键,
主要工作:
(1)ContextImpl.createAppContext()实例化ContextImpl ;
(2)mActivityThread.mInstrumentation.newApplication(),内部通过反射实例化Application,并把appContext传递过去,通过attach()方法给mBase赋值;
跟Activity类似就不再做分析。
经过分析发现:
1.每个Activity,Service,Application都有一个ContextImpl 类型的成员变量mBase,ContextImpl是Context的实现类。
2.细心的读者可能发现,Activity,Service,Application都是继承Context,其实他们本身是一个Context,也都实现了Context的抽象方法,
那么一个Activity是否就拥有两个Context呢?
是不是
这样计算比较合适呢?
下面看下Context中常用的三个方法,
ContextImpl继承Context,并实现了这三个方法,
Activity间接继承Context,主要是在ContextWrapper实现了以上三个方法,从源码中可以看出,最终还是调用了ContextImpl的实现。
下图可以看出这几个的关系,ContextWrapper顾名思义就是Context的包装类(有ContextImpl的成员变量),并且实现了Context,这是一种装饰者设计模式。当在Activity中调用getAsset()时,其实最终是调用mBase的getAsset()。
Activity间接继承了Context,是为了拥有跟ContextImpl一样的功能,但真正起作用的是mBase这个成员变量,所以一个Activity其实就只有一个Context起作用,那就是ContextImpl类型的mBase。
这种计算方法应该是没有问题呢。
或许有人有这样的疑问,一个应用不是只有一个Application吗,为什么计算公式是加上Application个数?单进程应用来说,一个应用确实只有一个Application,而多进程应用,那么一个应用就有多个Application,所以应该说一个应用有一个或多个Application,一个进程有一个Application。
另外其他关于Context的常见面试题
1.Activity的this跟getBaseContext区别。
前者是Activity对象本身,后者是通过attachBaseContext传入的ContextImpl对象mBase,两者功能是一样的,通过this最终还是会调到mBase对象中。
2.getApplication和geApplicationContext区别。
两者都是返回Application对象,前者是Activity和Service里面的方法,后者是Context中定义的方法。
3.应用组件的构造,onCreate、attachBaseContext的执行顺序。
先是组件构造化,接着attachBaseContext,传入ContextImpl对象,最后是onCreate方法。
4.谈谈你对Context的理解
先是Context的作用,然后是有几种Context,Application、Service、Activity的Context有什么区别以及继承关系,
最后是mBase变量是如何实例化的。
以上分析有不对的地方,请指出,互相学习,谢谢哦!
‘拾’ ExtJS开发实战的目录
《extjs开发实战》
第 1 章 入门 1
1.1 何谓extjs 2
1.2 cakephp 5
1.3 部署开发环境 5
1.3.1 部署服务器 6
1.3.2 部署cakephp 6
1.3.3 部署extjs 10
1.4 开发工具 10
1.5 调试与性能评估工具 10
1.5.1 firebug 插件 10
1.5.2 chrome的javascript控制台 12
1.5.3 性能评估与数据监控 12
1.6 小结 14
第Ⅰ部分 基础架构
第 2 章 extjs 101 17
2.1 javascript的面向对象 18
2.1.1 创建类 18
2.1.2 定义类方法与类属性 21
2.1.3 类的继承 22
.2.1.4 extjs在面向对象方面的支持 26
2.1.5 实战练习:预配置类 33
2.2 浏谈并庆览器对象模型:bom 35
2.2.1 命名空间 36
2.2.2 用户代理对象 38
2.2.3 历史记录对象 40
2.2.4 dom文件对象document 46
2.2.5 cookie 47
2.2.6 定时调用函数 48
extjs 开发实战
第 3 章 dom与event 53
3.1 extjs对dom的支持 54
3.1.1 创建element实例 55
3.1.2 通过element创建dom节点 64
3.1.3 读取/更新dom节点及属性 72
3.1.4 domhelper与template、xtemplate 85
3.2 extjs对事件的支持 99
3.2.1 一切的开始——ext.ready() 100
3.2.2 注册/移除事件处理程序 100
3.2.3 ext.eventmanager 102
3.2.4 阻止事件的进行与默认行为 103
3.2.5 ext.util.observable与自定义事件 104
3.3 实战练习:画布应用 110
第 4 章含握 好用的php框架——cakephp 119
4.1 cakephp的设计结构 120
4.2 自动化开发 122
4.3 cakephp基础 130
4.3.1 cakephp的配置文件core.php 130
4.3.2 cakephp的基础模板default.ctp 132
4.3.3 模型 139
4.3.4 控制器 141
4.3.5 视图与视图助手 146
4.4 实战练习:登录窗体 148
4.4.1 制作模型 149
4.4.2 编写控制器 155
4.4.3 编写页面视图 159
4.4.4 更改默认控制器 160
4.4.5 url前缀的应用 162
4.4.6 使用cake 2.*版本? 不,谢谢 163
4.4.7 下一步 164
第 5 章 ajax与数据驱动 165
5.1 ext.ajax与ext.data.connection 166
5.2 存储对象 179
5.2.1 ext.data.store 181
5.2.2 读取远程数据 187
5.3 实战练习:表格组件 195
第 6 章 component结构 199
6.1 component模型 200
6.2 常用组件 208
6.2.1 colorpalette 调色板 208
6.2.2 button按钮 209
6.2.3 splitbutton按钮 211
6.2.4 baseitem基础选项 212
6.2.5 datepicker日期选择面板 213
6.3 xtype神秘类型 214
6.4 实战练习:自定义组件 218
第 7 章 container结构 223
7.1 container容器 224
7.2 panel容器 227
7.2.1 添加子组件/容器 229
7.2.2 移动子组件/容器 231
7.2.3 查找子组件/容器 234
7.2.4 删除子组件/容器 238
7.2.5 顶层组件栏(tbar)与底层组件栏(bbar) 239
7.2.6 可折叠的panel 240
7.2.7 嵌套panel 241
7.3 viewport容器 244
7.4 window容器 246
7.5 buttongroup容器 250
7.6 button类似容器的用法 253
7.7 实战练习:更实用的window类 254
extjs 开发实战
第Ⅱ部分 视图组件蔽伍
第 8 章 页面布局 261
8.1 一切都从containerlayout开始 262
8.2 可让组件大小随容器改变的anchorlayout 263
8.3 可随意摆放组件的absolutelayout 265
8.4 填满容器的fitlayout 268
8.5 如手风琴般的accordionlayout 269
8.6 将组件以扑克牌方式摆放的cardlayout 271
8.7 表单布局formlayout 273
8.8 边界布局borderlayout 274
8.9 水平布局hboxlayout与垂直布局vboxlayout 276
8.10 列布局columnlayout 276
8.11 表格布局tablelayout 278
8.12 混合布局 279
8.13 实战练习:自定义布局 282
第 9 章 表单组件 287
9.1 表单容器formpanel与一般容器panel的差异 288
9.2 文本字段 291
9.2.1 密码字段 294
9.2.2 文件上传字段 294
9.2.3 文本块 295
9.2.4 数字字段 296
9.2.5 字段验证 297
9.2.6 隐藏字段 298
9.3 下拉菜单与自动完成 299
9.3.1 使用本地数据 299
9.3.2 远程数据与下拉菜单 303
9.3.3 远程数据与自动完成 307
9.3.4 联动下拉菜单 308
9.4 wysiwyg文本编辑器 310
9.5 日期字段 311
9.6 复选组件与单选组件 312
9.7 提交/读取数据 313
9.8 字段提示quicktips与quicktip317
9.9 美化表单 320
9.9.1 使用boxcomponent为表单添加自定义html内容 320
9.9.2 使用fieldset集合特定字段 321
9.10 实战练习:登录表单 322
第 10 章 表格组件 329
10.1 gridpanel是如何组成的 330
10.2 创建gridpanel与数据排序 331
10.3 使用pagingtoolbar进行分页 336
10.4 girdpanel与查询 339
10.5 自定义绘图器 342
10.6 通过selectionmodel与gridpanel交互 345
10.7 实战练习:dtable 348
第 11 章 可编辑表格组件 355
11.1 创建editorgridpanel 356
11.2 更换字段编辑器与数据交互 358
11.2.1 更换字段编辑器 358
11.2.2 添加与删除记录 360
11.3 使用roweditor 363
11.4 数据的提交与回滚 365
11.4.1 commitchanges()与rejectchanges() 365
11.4.2 同步服务器数据 367
11.5 使用datawriter 370
11.6 实战练习:etable 373
第 12 章 dataview与listview 381
12.1 dataview 382
12.1.1 创建数据显示样式 382
12.1.2 创建store与xtemplate 383
12.1.3 创建dataview 385
12.1.4 让dataview可分页 386
extjs 开发实战
12.2 listview 387
12.3 属性设置表propertygrid 389
12.4 实战练习:数据读取 392
第 13 章 图表组件 401
13.1 图表底层的秘密 402
13.2 折线图:linechart 405
13.2.1 绘制折线图 405
13.2.2 修改提示信息 406
13.2.3 添加水平/垂直轴的标题 408
13.2.4 修改图表风格 409
13.2.5 添加多个序列 411
13.3 柱形图:columnchart与stackedcolumnchart 413
13.4 条形图:barchart与stackedbarchart 415
13.5 圆饼图:piechart 417
13.6 实战练习:图表应用 419
第 14 章 树状组件 425
14.1 使用静态数据创建treepanel 426
14.2 读取服务器数据 428
14.3 操作节点 432
14.3.1 添加节点 432
14.3.2 删除节点 438
14.3.3 编辑节点 441
14.3.4 查询节点 442
14.3.5 排序节点 443
14.4 columntree 444
14.5 实战练习:treeeditor 447
第 15 章 菜单与工具栏 453
15.1 创建菜单 454
15.1.1 菜单101 454
15.1.2 菜单事件 456
15.1.3 加入separator与textitem457
15.1.4 colormenu与datamenu 459
15.1.5 添加复选框 460
15.1.6 添加单选按钮 462
15.2 使用上下文菜单 463
15.3 创建工具栏 465
15.4 实战练习:复杂的菜单与工具栏组合 467
第 16 章 拖放——drag与drop 473
16.1 拖放的基础知识 474
16.2 拖放操作的实现 476
16.2.1 注册dom元素为dd 477
16.2.2 使用ddtarget注册放置目标 478
16.2.3 使用ddproxy 484
16.3 实战练习:将拖放与组件结合 487
16.3.1 dataview与拖放 487
16.3.2 treepanel与拖放 493
第 17 章 插件 495
17.1 继承的限制 496
17.2 插件的实现 499
第 18 章 长路漫漫——extjs 4 503
18.1 类系统 504
18.1.1 定义类的新方式 504
18.1.2 动态读取类定义与依赖性管理 508
18.1.3 混合属性 513
18.1.4 静态方法 515
18.1.5 自动生成变量的getter/setter方法 516
18.2 data 封包 517
18.2.1 model 517
18.2.2 proxy与store 523
18.3 panel、formpanel、gridpanel与window 537
18.3.1 panel组件 537
18.3.2 formpanel组件 539
extjs 开发实战
18.3.3 gridpanel组件 541
18.3.4 window组件 543
18.4 图表组件 545
18.4.1 area图表 545
18.4.2 bar图表 548
18.4.3 radar图表 551
18.4.4 pie图表 552
extjs 4后记 554
第Ⅲ部分 实战案例
第 19 章 案例——记账系统 557
19.1 系统需求分析 558
19.2 系统规划 558
19.2.1 界面规划 558
19.2.2 后台服务器部署 560
19.3 系统实现 561
19.3.1 实现cakephp端 562
19.3.2 实现extjs界面 578
19.4 与cakephp结合 611
第 20 章 案例——人人网应用程序 615
20.1 系统功能分析 616
20.2 系统部署 618
20.3 封装google maps api的类gmap 621
20.4 组合界面组件 629
20.4.1 定义geomappanel 629
20.4.2 定义functionmenu 630
20.4.3 定义georecordgridpanel 632
20.4.4 定义mainpanel 636
附 录 学习与实现资源 645