技术框架

Eclipse|Maven: 查看3rd jar源码

写这篇文章的背景是:最近在开发一个虚拟账户的金融产品,部署到生产环境之后,如果应用在一段时间之内不活跃(即在这段时间内没有访问虚拟账户的请求),再次访问该应用时,发现一直在等待中,不响应。

查看后台db的日志发现程序卡死了,最后一行日志显示Opening JDBC Connection……,恩,看来在检查跟Oracle数据库服务器的连接上出现了什么幺蛾子。

接下来的思路是去查看druid(额,就是哪个著名的数据库连接池开源项目)的源码,关于最终怎么解决这个问题的故事到此结束,也不是本文的重点。重点在于提到了开源项目的源码,所以还是有必要去熟悉下Maven工具,现在项目构建工具很多,孰优孰劣,各有千秋吧。

如果对Maven真的是小白,似乎可以参考以下顺序来熟悉(其实是我自己的一个熟悉和了解过程),把这当中可能遇到的几个问题罗列在此,也算是一种总结。

[1]熟悉概念阶段
这个就自行Google和百度吧,或者直接读官网上的介绍,或者读下面这两个链接中的内容也是一个不错的选择:
Apache Maven 入门篇(上)
Apache Maven 入门篇(下)
好了,读完这两篇应该对Maven的POM,插件,生命周期,依赖管理,包和项目坐标有了一个基本 (更多…)

深入理解JVM—类加载的时机

最近在读《深入理解Java虚拟机:JVM高级特性与最佳实践》这本书,对于深入理解JVM的基础技术概念大有帮助,现对「类加载时机」这块逻辑处理过程进行一个简单的回顾和总结。

众所周知,JVM在字节码上运行,字节码从何而来,从高级源码编译过来,当年在制定Java虚拟机规范时并没有强制规定JVM只能运行由Java源代码编译过来的字节码,不管何种高级开发语言,只要编译后的字节码符合JVM的规范,理论上都能在JVM上运行,好了,扯远了。Java程序从加载到虚拟机内存中开始,到从内存中移出大致需要经历以下过程,如下图所示:
虚拟机类加载机制 (1)

 

我们从逻辑上把Java程序执行的完整链条切割成各个阶段,后一阶段的顺利执行取决于上一阶段的执行结果,前后依赖。下面对各个阶段所执行的操作作一简单介绍:

加载:我们在这里说的加载指的是类的加载,大致需要完成以下几件事情:
1)根据类的全路径名称获取描述此类的二进制字节流。这个动作一般通过「类加载器」来实现,「类加载器」是一块在Java虚拟机外部实现的动作代码,从Java虚拟机的角度出发,存在两种不同的类加载器:启动类加载器(由C++实现,是JVM的一部分)和其他类加载器,启动类加载器负责把<JAVA_HOME>\lib目录中的类库加载到虚拟机内存中;其他类加载器中的扩展类加载器负责加载<JAVA_HOME>\lib\ext目录中的类库;应用程序类加载器则负载加载用户类路径上指定的类库。
2)将这个字节流所代表的静态存储结构(比如类变量)转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的Java对象,通过该对象来访问该类的各种数据。

验证:确保上一步加载进虚拟机的字节流不包含危害虚拟机自身安全的信息,主要进行以下几个验证操作:
1)文件格式验证:验证字节流是否符合Class文件格式的规范;文件格式验证基于二进制字节流进行,通过该验证之后,字节流才会进入内存的方法区中进行存储,所以后面的验证操作都基于方法区中的存储结构进行,不会再直接操作字节流;
2)元数据验证:对类的元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息;
3)字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的;
4)符号引用验证:对类自身以外的信息进行匹配性校验,比如通过字符串描述的全路径名称是否能找到对应的类。

准备:为类变量在方法区中分配内存空间,并设置初始值,需要注意几个关键的地方:
1)仅仅只为类变量(类中以static修饰的变量)分配内存空间,并设置初始值;
2)准备阶段为变量设置初始值一般指的是变量对应类型的零值,比如public static int xyz = 123,准备阶段过后变量xyz的初始值为零,123赋值给xyz的操作需要等到初始化阶段才真正执行,但是也有一个例外情况:如果变量xyz具有ConstantValue常量属性,public static final int xyz = 123,那么xyz在准备阶段就会被初始化成值123。各变量对应的零值如下图所示:
QQ20151111-1@2x
解析:主要完成把常量池内的符号引用替换为直接引用

初始化:真正开始执行类中定义的Java程序代码(其实是字节码)
初始化阶段主要执行类构造器<clinit>()方法,<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,另外需要注意以下几个比较重要的地方:
1)<clinit>()方法与类的构造函数不同,前者不需要显式的调用父类的<clinit>()方法,Java虚拟机保证会在调用子类的<clinit>()方法之前先执行父类的<clinit>()方法,也就意味着父类中定义的静态语句块先于子类的变量赋值操作。

解析这一阶段所需要完成的工作相对来说是比较复杂的,可以另外再进行说明。

React’s diff algorithm(中文版)

Something go First

这是一篇介绍diff算法的翻译文章,原文来自FB工程师 Christopher Chedeau所写的《React’s diff algorithm》,特此说明,Ok,Here we go.

Background

React是由Facebook所发起的一个用于构建UI的Js库,创始初衷在于提高Js性能。在这篇文章中,我将使用图文的方式来呈现Diff算法和渲染是如何在React中工作的,并基于此原理,读者可以根据实际情况优化你的应用。

Diff Algorithm

首先,在正式开始了解React的实现细节之前,让我们从大体上来看下它是如何工作的:

在任何时候,你都能通过代码来描述你想要的UI效果。但是请记住,如果你使用了React库来描述你的UI,那么你并不是真正得到了真实页面上的DOM,得到的只是保存在内存中的一些轻量的Js对象,我们把这些对象称之为虚拟DOM。

React之所以能够大大提高网站性能,是因为React仅仅执行前一页面渲染成后一页面时所需要执行的最少差异动作,省去很多DOM属性值中间变换的渲染过程。

逐层比较(Level By Level)

在一般情况下,计算任意两棵二叉树之间的最少差异是一个O(n^3)的问题,你可以想象,在React实现中采用这种算法是不可取的,因此React设计了一种时间性能接近O(n)的启发式算法。
算法原理是React对任意两棵二叉树进行逐层比较,以此来确定这两棵二叉树之间的最少差异数。利用该方法大大降低了比较复杂度,按照通常来理解,这种比较方法是缺乏准确性的,但是在实际应用中观察发现:在DOM树中移动上下级节点的情况是非常少见的,通常只是在同一层的兄弟节点之间移动,如下图所示:

level

列表(List)

假设我们现在有一个组件,在一次迭代循环中,渲染出五个组件,接下去在该组件列表中插入一个新的组件,在信息如此匮乏的情况下,实现两个组件列表之间的映射是一件非常困难的事情。
在默认情况下,React会把前一个列表中的第一个组件与后一个列表中的第一个组件进行配对,以此类推,当然,你可以为每个组件提供一个Key属性来解决映射问题。在实际应用中,我们能够很容易的在孩子节点中找出唯一的Key,如下图所示:

list

组件(Components)

一个React应用通常是由许多用户自定义的组件组成,这些组件最终转化成一棵主要由div(s)组成的DOM树。这一辅助信息被diff算法所很好的利用,因为React通常只比对那些具有相同类的组件
举例,假设现在Header被一个ExampleBlock所代替,React将会简单粗暴的把Header标签移除,然后创建ExampleBlock,而不会浪费时间去比对这两个毫无相似之处的组件,如下图所示:

 

companet

事件代理(Event Delegation)

为DOM节点设置事件监听器是一件非常耗时和消耗内存的痛苦事。不过,React实现了一项更为流行的技术,叫做”事件代理”,更进一步的是,React还重新实现了一套遵循W3C标准的事件系统,这意味着IE8中原有的事件处理的Bug已成往事,事件名称在各个浏览器之间得以保持一致。
让我来解释一下这是如何实现的:单事件监听器与文档根节点进行绑定,当触发某一事件时,浏览器给了我们想要的DOM节点。因此,React为了在DOM层级间传播事件,并没有在虚拟DOM层级间迭代循环。
取而代之的是我们巧妙地利用了每个React组件都有一个编码层级的唯一标示符ID这样一个事实。通过简单的字符串操作我们能很容易的获取到所有父节点的ID。同时把事件监听器放在一个Hashmap中,我们发现这样做的性能远高于把事件监听器与虚拟DOM节点进行绑定。下面的代码展示了事件是如何在虚拟DOM间进行分发的:

浏览器为每个事件和每个监听器都创建一个新的事件对象,这样看起来很好,因为我们能追踪到每个事件对象甚至修改它,然而这也意味着需要许多次的内存分配操作。而React会在应用启动时在内存中分配一个对象池,无论何时需要事件对象,都可以从这个对象池中获取一个可用的事件对象进行复用,这一举措大大降低了垃圾回收的复杂度。

渲染(Rendering)

批处理(Batching)

无论何时在组件上触发setState方法,React都会把这个组件标记为dirty,等到结束事件循环时,React会对所有标记为dirty的组件进行批处理,重新渲染它们。
该批处理意味着在一个事件循环期间,无论某个DOM元素的属性值被更新过几次,最终在循环结束之后,该属性值只会被渲染一次,这对于构建一款高性能的应用至关重要,而在原生的JS中要实现这个功能却是一项非常困难的工作,而在React应用中,该功能与生俱来,如下图所示:

batching
子树渲染(Sub-tree Rendering)

当触发某个组件的setState时,该组件将会为其孩子节点重新构建虚拟DOM。如果你不幸调用了根元素的setState方法,那么整个React应用都将重新渲染,所有组件的render方法都将会被调用,即使这些节点根本就没有发生改变,这听起来很吓人,然而在实际应用中,并不如此,因为我们仅仅只操作了保存在内存中的虚拟DOM,而并没有碰真实的DOM。
首先,我们正在讨论的是展示用户界面。因为屏幕大小是有限制的,而你通常需要一次性的有顺序的展示上百甚至上千个元素。
另一个关键点是当你在写React代码时,你并不会常常调用根节点的setState方法。你只对那些受到事件触发的组件进行setState的调用,这意味setState方法的调用常常局限在与用户交互的组件上,很少会调用根节点的setState方法。

sub-tree

选择性的子树渲染(Selective Sub-tree Rendering)

最后,你也可以通过在组件上实现下面的方法来防止其子树进行渲染。

基于组件的前后状态,你可以告诉React该组件并没有发生变化,所以无需重新渲染。如果你能够正确使用这项功能,那么这对你应用的性能是一个极大的提升。

为了能够使用该功能,你必须能够比较JS对象,但是这会带来很多问题,比如我们应该进行何种程度的比较,深还是浅,如果进行深层次的比较,那我们也许应该使用不可变的数据结构或者进行深度拷贝。
并且你还要去考量,如果使用此项了功能,那么我们要确保比较JS对象所带来的计算时间要少于未使用此项功能而需要进行渲染的时间。

结论(Conclusion)

未翻译,全文完。转载请联系作者

triffic.tang@gmail.com

Reference

  1. http://calendar.perfplanet.com/2013/diff/
  2. http://blog.vjeux.com/

 

Logback日志输出规则

Background

LOGBACK作为一款优秀的开源日志组件,相信每位都多少接触过,这里记录几个比较容易忽略却重要的Points(主要指日志输出规则),下面是Logback中关于根节点和子节点之间的继承关系图。

logback1
关于Appender部分

这部分内容就不再说明,关于如何布局日志的输出,网上相关内容已经很多,可以查阅参考文献中给出的链接。
Logger和Root在输出日志时的规则:
Logger用来指定项目源码中某一个Package或者某一个class所对应的日志打印级别,所选用的appender,同时还有一个可选项为addtivity,共三个属性,具体解释如下:

Name:指定项目源码包或者类的路径,为该包或类定义独特的日志格式,一般为com.xxx;
Level:指定输出日志的最低级别,一般为下列中的某一项(缺省情况下使用上级的Level):

Trace
Debug
Info
Warn
Error
ALL
OFF
INHERITED or NULL 表示强制使用上级的Level配置

Addtivity:是否向上级Logger传递日志输出信息,缺省情况下为True。

Root从本质上说其也是一个Logger元素,只不过Root只有一个Level属性
日志输出规则如下图所示:

logback2

Reference

  1. http://my.oschina.net/looly/blog/298675

SpringMVC 入门笔记

SpringMCV笔记分成两部分,第一部分描述应用启动时针对SpringMVC框架所进行的准备工作;第二部分描述SpringMVC框架完成一次用户请求到响应请求的逻辑处理过程。

第一部分

SpringMVC的本质原理是通过Servlet拦截所有url的请求来达到控制的目的,我们不妨从一个web应用的描述符文件(web.xml)入手一探究竟(这里补充说一句,Servlet技术对于web开发者来说多多少少都有接触过,结合SpringMVC的本质原理,我们是否可以推测出Spring框架的核心:通过层层封装底层相关技术,如Servlet,从而达到高效开发WEB应用的目的,当然这中间包含了作者呕心沥血的构思和设计)。在描述符文件中包含了应用的资源名称,路径,启动项,监听器和拦截器等等,常常看起来像下面这样:

1444898662_full.png

在web.xml中分别标注出了各部分,对重要项进行解释:

1、在Part II中我们配置了一个SpringMVC核心的处理请求类DispatcherServlet,它继承自HttpServlet,继承关系如下所示:

1.1、Servlet框架技术由两个Java包组成:javax.servlet 和 javax.servlet.http。前者定义了所有servelt类都必须实现或扩展的通用接口和类;后者定义了基于HTTP协议的HttpServlet类。在HttpServlet类中包含了

方法,该方法在应用程序启动时执行初始化操作,SpringMVC框架重载了这个方法,用于在启动时针对特定应用需求做初始化工作,比如读取描述符文件中的配置信息。

1.2、当DispatcherServlet载入后,将从XML文件中加载配置信息,在默认情况下,该XML文件的文件名取决于,但也可以支持自定义,在该例子中SpringMVC框架将从/WEB-INF/config/springmvc-config.xml处加载配置项。

1.3、通过配置/拦截所有的用户请求,转向DispatcherServlet作进一步的处理,该请求处理过程可以认为是SpringMVC的核心处理逻辑,留在下一部分进行说明。

1.4、关于<load-on-startup>1</load-on-startup>该配置项表明应用程序启动时,则加载Servlet类,执行其init方法,在正式接受用户请求之前,完成初始化工作,当然也可以不加这个配置项,在第一次接受用户请求时进行资源初始化的操作,但在通常情况下,预先加载资源进行初始化工作会带来节省用户请求时间的好处,数值代表加载的优先级,数值越小,优先级越高。

2、在Part III配置了ContextLoaderListener上下文环境监听器,现在我们就大致来了解下这个ContextLoaderListener具备哪些功能?

2.1、当硬编码的时候,我们可以在代码中直接把Spring的配置信息读入Spring容器,比如通过以下方式:

但在通常情况下,在开发web应用时,考虑到配置的灵活性以及程序的正交性,通常将Spring配置文件的路径以的形式进行注册,并用ContextLoaderListener进行监听,好,交待完小小的背景,现在我们来看看ContextLoaderListener到底给我带来了什么样的功能,先查看其继承关系,如下:

而查看ServletContextListener接口发现其包含了两个需要子类实现的方法:

而web容器在启动时,会自动执行contextInitialized方法,所以我们便可以了解ContextLoaderListener的功能:在web容器启动时,就会默认执行其实现的方法contextInitialized,自动加载ApplicationContext的配置信息,开发者能够在为客户端提供服务之前向ServletContext中添加任意的对象。

2.2、每一个Web应用都有一个ServletContext与之相关联,它类似与一个全局变量,在应用启动时被创建,在应用关闭时被销毁。

Reference

1、http://www.cnblogs.com/xing901022/p/4178963.html