用struts1也有不短的日子了,对于它的功能也有了一定的理解。基于此,抱着学习的态度,我计划在空闲时间来系统的看下struts1的源码。之所以说系统,是因为之前断断续续的也看过一些,但限于当时对struts1的了解,体会得可能还不深入,所以总是容易忘记看过的东西。但现在来读struts1的源码,应该会更加合适一些,有几个方面的因素,而这几个因素,我觉得也可以用在阅读其他项目的源码上:
1、首先,我对struts1的功能有了一定的了解,虽然还不够精通,但对其整体的框架和体系是有一定认知的;
2、其次,能够区分struts1的各功能模块,这样在阅读源码时就会有的放矢,各个击破了。
struts1工作流程先让我们来看看struts1的工作流程,从宏观上对struts1的功能有个认知。
从网上找了两张图:
struts1的体系结构图:
struts1的工作流程图:
1、初始化:struts框架的总控制器ActionServlet是一个Servlet,它在web.xml中配置成自动启动的
Servlet,在启动时总控制器会读取配置文件(struts-config.xml)的配置信息,为struts
中不同的模块初始化相应的对象。
2、发送请求:用户提交表单或通过URL向WEB服务器提交请求,请求的数据用HTTP协议传给web服务器。
3、form填充:struts的总控制器ActionServlet在用户提交请求时将数据放到对应的form对象中的成员
变量中。
4、派发请求:控制器根据配置信息对象ActionConfig将请求派发到具体的Action,对应的formBean一并
传给这个Action中的excute()方法。
5、处理业务:Action一般只包含一个excute()方法,它负责执行相应的业务逻辑(调用其它的业务模块)
完毕后返回一个ActionForward对象。服务器通过ActionForward对象进行转发工作。
6、返回响应:Action将业务处理的不同结果返回一个目标响应对象给总控制器。
7、查找响应:总控制器根据Action处理业务返回的目标响应对象,找到对应的资源对象,一般情况下
为jsp页面。
8、响应用户:目标响应对象将结果传递给资源对象,将结果展现给用户。
struts1初始化下面开始我们的源码阅读之旅,首先,对struts1的初始化源码进行阅读。
web容器在初始化或者初始化完成后,会在适当的时候初始化servlet。struts1中的ActionServlet类就是一个servlet,其会被web容器进行调用。
首先会调用init()方法,该方法会执行如下动作:
1、initInternal()
初始化struts内部使用的消息资源。
2、initOther()
2.1 读取初始化参数config,如果在web.xml中对该参数进行了设置,则将ActionServlet类中的config字段设置上对应的值。该值记录的是默认情况下,struts配置文件的路径。
2.2 读取初始化参数convertNull,如果设置为true(可为true、yes、on、y、1),则将原始类型的封装类默认值设置为null。
3、initServlet()
利用Digester组件解析web.xml,将当前servlet对应的<servlet-mapping>内容加载到ActionServlet中。关键代码如下:
protected void initServlet() throws ServletException { // Remember our servlet name this.servletName = getServletConfig().getServletName(); // Prepare a Digester to scan the web application deployment descriptor Digester digester = new Digester(); digester.push(this); digester.setNamespaceAware(true); digester.setValidating(false); // Register our local copy of the DTDs that we can find for (int i = 0; i < registrations.length; i += 2) { URL url = this.getClass().getResource(registrations[i+1]); if (url != null) { digester.register(registrations[i], url.toString()); } } // Configure the processing rules that we need digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2); digester.addCallParam("web-app/servlet-mapping/servlet-name", 0); digester.addCallParam("web-app/servlet-mapping/url-pattern", 1); InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml"); try { digester.parse(input); } catch (IOException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } catch (SAXException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } finally { try { input.close(); } catch (IOException e) {
Top ten模式跟前面的有很大的不同,跟输入数据大小无关,最终得到的记录数量是确定的。而在通用filtering中,输出的规模取决于输入数据。
Intent根据数据集的排名,获取相对较小的前K条记录,不管数据量多大。
Motivation在数据分析中,找出离群值是很重要的工作,因为这些记录是典型的最引人关注的独特的数据片。这种模式的关键点是根据指定的规则找到最具代表性的记录,根据这些记录,可能就会找出导致这些记录特殊的原因。如果定义了能决定两条记录之间排名的排名方法或比较方法,就能用这种模式,用MapReduce找出整个数据集的排名最高的值。
这种模式特别引人关注的原因是,比较器是在MapReduce上下文之外实现的。在hql中,你可能更倾向于用排名值排序数据,然后取top k。在MapReduce中,正如下一章要讲的,全局排序是非常复杂的,会使用集群大量的资源。这种模式着眼于不通过排序而去取到有限的high-value 记录。
找出top ten 是一件有趣的事情。Stackoverflow上哪些发帖得分最高?谁是最老的会员?你的网站最大的订单是?哪个中帖子“meow”出现次数最多?
Applicability·这种模式需要一个能用于两条记录的比较方法。就是说,我们必须能够拿一条记录和另一条记录比较来决定那一条更大。
·输出记录的数量应该明显比输入少,其中一个重要原因是,这样应该比全局排序更有意义。
Structure这种模式mapprer reducer都有。Mapper找到本地top K,然后都发送到reducer来找出最终的top K。因为mapper输出的记录最多K条,相对较小,所以仅需要一个reducer。可以看图3-3.
class mapper:
setup():
initialize top ten sorted list
map(key, record):
insert record into top ten sorted list
if length of array is greater-than 10 then
truncate list to a length of 10
cleanup():
for record in top sorted ten list:
emit null,record
class reducer:
setup():
initialize top ten sorted list
reduce(key, records):
sort records
truncate records to top 10
for record in records:
emit record
Figure 3-3. The structure of the top ten pattern
Mapper读每条输入记录,用一个大小为K的数组对象收集值。在mapper的cleanup阶段,把存在数组里的K条记录作为值,key为null,发送到reducer。这是map任务最低需求的K。
我们会在reducer得到K*M条记录,M代表map任务数。在reduce方法,做跟mapper同样的事情。
我们在每个mapper中要选出top k是要考虑最极端的情况。
ConsequencesTop k条记录被返回。
Known usesOutlier analysis
离群值通常需要关注。可能是用户使用你的系统很困难造成的,或者网站的高级用户。用过滤和分组,也可能给你另一种数据集的视图。
Select interesting data
如果你能根据某种排序给记录评分,就能找出最有价值的数据。如果你打算跟踪后续处理过程,这就非常有用。例如BI工具或RDB,不能处理大规模的数据。评分规则可以用一些高级的算法设置的很复杂,例如给文本评分,根据语法和拼写的精确度,以此达到删除垃圾数据的目的。
Catchy dashboards
这不是一本心理学的书,所以你认为消费者感兴趣的top ten数据,他们就是。这种模式也可以用于网站的一些有趣的top ten的统计,并且可能让用户对数据有更多的思考,或者甚至带来竞争。
ResemblancesSql
在传统的小的RDB中,排序可能不算什么。这种情况下,可以根据排序的条件获得top ten。MapReduce中也可以做同样的事情,但你会在后面的模式中看到,排序是一种代价很高的操作。
SELECT * FROM table ORDER BY col4 DESC LIMIT 10;
Pig
Pig无论用任何最优的排序,在执行这种查询时会有一些问题。最简单的样式跟sql
查询一样,
1固件自检--------firmware
固件-------固化到主板芯片中的一段程序,通过CMOS/BIOS 用来加电自检
CMOS --- =存储器 给所有的芯片加电
BIOS------basic Input Output System 简单的输入输出系统-----连接软件和硬件之间的一座桥梁-----软件的命令和硬件的命令结合
2,加载操作系统的内核
Bootloader 引导着我们去找到这个操作系统的内核
Linux下有一个常用的自启动程序----grub---
Boot 目录下面存放的是引导找到操作系统内核的命令
通过grub.conf文件找
Title-------操作系统的名称
root (hd0,0) hd0-----ide 硬盘0 第几块硬盘---0 第几个分区()
Sd ------scisi
Kernel 操作系统内核的版本号
3,加载了内核
内核有哪些功能:驱动硬件,驱动---软件驱使硬件动起来
开启 init 命令
/etc/inittab
因为不同的运行级别加载的服务是不一样的,比如图形界面和单用户界面
Inittab------判断你默认的运行级别是什么
通过initdefault来判断我们默认的启动级别
初始化系统------通过:/etc/rc.d/rc.sysinit 这个脚本来实现加载系统的基本服务,不管你是什么运行级别都要执行这个脚本
默认的字体----系统的时间----环境变量
3,我们还要根据你不同的运行级别去开启不同的服务
不同的运行级别开启不同的服务是怎么来实现的呢?
/etc/rc.d/rc
Rc------他就是用来根据不同的启动级别开启不同的服务
原理:先判断你默认的启动级别,再去根据这个启动级别执行相应的 rcX.D这个脚本
进一步引导去找到具体的操作系统的内核
Etc 目录里面的文件其实大部分是软连接 ------用来找到具体的脚本的