qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

Web开发测试中的18个关键性错误

 前几年,我有机会能参与一些有趣的项目,并且独立完成开发、升级、重构以及新功能的开发等工作
  本文总结了一些PHP程序员在Web开发中经常 忽略的关键错误,尤其是在处理中大型的项目上问题更为突出。典型的错误表现在不能很好区分各种开发环境和没有使用缓存和备份等。
  下面以PHP为例,但是其核心思想对每一个Web程序员都是适用的。
  应用程序级别的错误
  1、在开发阶段关闭了错误报告
  我唯一想问的是:为什么?为什么在开发的时候要关闭错误报告?
  PHP有很多级别的错误报告,在开发阶段我们必须将它们全部开启。
  如果你觉得错误不会发生,那么你把程序太理想化了,在现实世界中,错误是必然的。error_reporting和display_error是两个完全不同的方法,error_reporting()设置了错误的级别,而display_errors则是设置错误信息是否要被输出。
  在开发阶段,错误报告的级别应该设置成最高的,比如以下设置: error_reporting(E_ALL);以及ini_set(‘display_errors’, true);
  2、淹没错误
  和上一点相反,很多程序员喜欢将错误淹没了,你明知道错误会发生,但是你选择将错误隐藏掉,然后可以早早回家睡大觉,殊不知将来会发生更严重的错误。
  3、代码中任何地方都没有使用日志
  软件开发的一开始你就要牢记使用日志,不能到项目结束了才去弥补日志功能。很多程序员都会用这样或那样的手段进行日志记录,但是很少有人能真正用日志来记录异常信息,试问一个没有人查看的日志系统有什么用?
  4、没有使用缓存
  在的应用系统中,我们可以在多个系统层次上使用缓存,比如在服务端、应用端和数据库端等。和日志一样,缓存也应该在一开始就应用到系统中去,你可以在开发阶段禁用缓存,等到了产品发布后再将缓存开启。
  5、丢弃了最佳实践和设计模式
  你看到过多少人使用自己的密码加密算法?很遗憾的告诉你,有很多,因为他们认为将更了解它。
  最好的实践方式和设计模式已经由前辈创建了,这往往比你自己再造一个轮子要来的简单奏效,我们开发者只需要熟练掌握这些设计模式并且合理地应用在项目中即可,比如一些加密算法。
  6、没有使用自动化测试
  在每一个Web项目中都会使用到测试,就像日志一样,如果没有人管理和使用,那么测试也是一无是处的。
  运行测试工程是一项枯燥乏味的工作,幸好有一系列工具帮助我们实现自动化测试。在PHP开发中,有一款很好的测试工具叫Jenkins,使用起来非常方便。
  7、没有做代码审查
  在团队中工作是一项非常大的挑战,因为每一个成员都有自己不同的工作习惯和方式,如果没有良好的规范,那么项目开发就会走很多弯路。
  团队中的每一个成员都应该互相审查代码,就像单元测试,它可以帮助项目变得更加干净和一致性。
  8、编程只考虑理想情况
  你是否遇到过自己或者别人的代码在交到客户手中后经常出问题,甚至是乱套了?我当然没有。
  出现这种情况往往是因为开发者懒惰了,只考虑了理想情况,这会导致数据库崩溃了、PHP发生致命错误、甚至是服务器被黑。程序员在写代码时不仅要考虑最理想的情况,更要考虑最坏的情况,思考全面,才能让代码覆盖所有的情况。
  9、没有正确运用面向对象编程的思想
  大部分PHP初学者都不会再其代码中运用面向对象的思想,因为这个概念在刚开始的时候很难理解。
  当然面向对象的概念并不是简单地将一些类组织在一起。
  对象、属性、方法、继承和封装等都是OOP中最基本的概念,开发者正确使用了面向对象设计模式后,就有能力写出更干净、更有扩展性的代码了。
10、“飞行模式”(On-the-fly)编程
  大部分开发者都会遇到这样的情况:“快,客户需要一项新功能,要能运行ASAP”,于是你就在源代码上新增一些功能,然后直接上传到正在运行的服务器上,这种编程方式我们称其为“飞行模式”(On-the-fly)编程。
  我们在开发软件时,尤其是中大型的项目,都必须按照工作流程来进行分析、编程和发布,这将大大减少未来软件的bug。这种“飞行模式”并不可取。
  数据库级别的错误
  11、没有将数据库读写分离
  为了能长时间运行复杂的系统,每一个程序员都应该考虑到系统的可扩展性,系统99%的时间都不需要考虑扩展,因为并没有如此大的流量。
  为什么要数据库读写分离?
  在每一个系统中,数据库将会是第一个出现的瓶颈,在大流量的冲击下,数据库很可能将会是第一个阵亡的。所以大部分情况下我们会用多个数据库来分散流量,开发者经常会使用Master – Slave模式或者Master – Master 模式。Master – Slave是最受欢迎的一种数据库分压模式,它会将指定的select语句路由到每一个Slave服务器,这样Master服务器的压力会减轻不少。
  12、代码只能连接到一个数据库
  这和上一个错误非常像,但是开发者有时候因为某些原因需要连接到多个数据库,比如你会将用户日志、活动信息流、实时数据分析等高负载的数据放到不同的数据库中来缓解对主数据库的压力。
  13、没有检测数据库漏洞
  如果你不对数据库进行漏洞检测,就相当于给大部分黑客敞开了服务器的大门。
  在众多漏洞中,数据库漏洞是最脆弱的,最常见的就是SQL注入。因此定期做数据库漏洞检测还是很有必要的。
  14、数据表不建索引
  索引在数据表中有着非常重要的作用,合适的索引可以提高每张表的性能,这里有一篇文章就讲述了如何创建索引以及何时创建索引。
  15、没有使用事务机制
  数据完整性对Web系统非常重要,如果数据一致性发生错误,那么整个系统都会崩溃并且难以修复。合理地运用数据库的事务机制将有效地解决这个问题。比如你要保存用户数据,在table1中有e-mail, username和password,table2中有first name, last name,和gender age。我们可以利用事务对两张表更新时保证数据同时被更新或者同时不被更新。
  16、没有加密敏感数据
  对于数据库中的敏感信息,如果你不对它们进行加密,或者用简单的算法进行加密,那么在2014年你肯定会遇到一些麻烦的问题,黑客们一旦入侵你的数据库,用户的密码或者其他重要信息就会一览无余。
  PHP5.5中提供了一个哈希加密方法,使用如下:
  $hash = password_hash( $password, PASSWORD_BCRYPT );
  17、没有备份
  看到下面这张图片没,如果遇到这样的情况,你又没有备份,那么一切都over了。
  18、没有监控
  没有监控,你将不知道接下来会发生什么事情,对于监控,要注意以下几个问题:
  有多少人可以直接访问这个应用服务?
  服务器是否在高负载下运行?
  我们需要用另一台数据库服务器来扩展系统吗?
  应用系统的失败点在哪里?
  系统目前正处于离线状态吗?

posted @ 2014-09-26 10:08 顺其自然EVO 阅读(167) | 评论 (0)编辑 收藏

性能测试中如何选取被测对象的业务逻辑

很多搞性能测试的人员,只会跟着网上、前辈教导的方法进行测试:挑选业务逻辑中并发量、访问量最高的业务逻辑、结合读写等业务进行测试,然后取整条业务逻辑(模拟用户全流程动作)的逻辑进行测试;结果就是:准备大堆的测试数据,复杂的准备工作;其实那些数据只是用来满足业务流中的条件,而不是真的能产生压力的部分;
  笔者采用的方法:
  1、B/S结构中,用户操作功能流程其实是前端js依次调用不同的CGI接口,后台实现上面其实并没有强依赖关系(只要满足对应条件进行发包都能执行)。
  所以,首先挑选业务逻辑中用户访问最高的流程,然后从流程中挑选调用次数、压力最大的CGI接口;这样聚焦于对应的测试对象,可以避免很多无用的测试数据;
  2、根据业务逻辑,分析被测对象券流程中,所调用的接口,对于安全旁路、分支判断等,根据情况进行取舍(有些业务只测试某个CGI,有些是测试全平台,测试中根据情况进行聚焦)。
  3、根据分析情况直接修改被测对象代码:通常接口调用形式都会使用iret方式来判断,例如:
/*原有代码----begin*/
iret=xxx.call(args1,args2,args3);
if(iret != 0){
print("xxxxx");
break;
}
/*原有代码----end*/
iret=0 ---------- 添加iret=0,让程序继续走。
  这样不会影响外部接口调用次数,不会影响网络发包次数,但可能会影响单个网络包大小进而影响网络流量;同时稍微增加cpu负担(赋值造成内存读写)。但其实我们要测的是业务主流程,而不是外部接口(外部接口如果有需要可单独进行压测),所以笔者认为也是可以采取此种方案,而不需要准备一大堆无用的数据,只需有针对性的进行业务逻辑选取即可;

posted @ 2014-09-26 10:07 顺其自然EVO 阅读(213) | 评论 (0)编辑 收藏

测试用例设计的价值与误区

测试用例是一系列特定的软件行为,用于验证软件的某特定功能、检查软件能否正确处理某种出错行为、或者检查其他一些软件质量衡量的属性 (如性能、安全、可靠性等)。 一个测试用例是一个正式的文件或记录,描述了测试活动是怎样具体执行的。测试用例设计的目的就是发现缺陷,但是测试用例的用处远远超出发现缺陷。
  测试用例文档的一些好处如下:
  1、历史借鉴:测试用例的存在要远远超过产品发布。持续工程(Sustained engineering)以及产品未来版本的负责人往往需要借用测试用例来了解测试过什么,以及是如何测试的。测试用例文档和一个有组织的储存系统对长期支持或修订产品的一部分是至关重要的策略。----注----为了便于后来者对于测试用例的使用和借鉴,测试用例设计要尽量描述准确、步骤清晰。
  2、测试进展跟踪:通过测试用例文档,可以跟踪一些额外的属性,如测试用例的执行数目,测试用例的通过或失败数目,以及每个功能领域的测试用例总数。 ----注----在测试用例管理系统中要准确且实际地描述用例的执行结果,包括其他必填的属性,便于分期人员从各个属性和维度进行测试过程分析。
  3、可重复性:好的测试用例文档可以由任何人在任何时候执行。这同样适用于自动和手动的测试用例。重复准确地执行同样的测试对重现步骤或检测回归是至关重要的。 ----注----测试用例的描述要准确、全面,要能保证除自己以外的测试人员能正确理解和执行该用例。
  测试用例文档也有缺点:
  1、建立文档的时间:如果建立测试用例文档的时间比运行测试用例所需的时间还长,建立测试用例文档也许就没有意义了。经常有这样的情况,即测试用例只需要在一个单一的环境下执行寥寥几次。 ----注----但是从测试用例价值的角度来考虑,建立测试用例文档却是个不可裁剪的过程。但是,这个缺点会随着用例设计的熟练程度的提高以及用例设计平均时间成本的减小而逐渐减弱。
  2、功能变化引起测试用例过期:建立测试用例所需的时间很可能因功能经常变化而增加,以至于失去控制。如果测试用例的功能领域变化频繁,建立测试用例文档就不一定是明智的。这种场景之一是尝试写测试用例以验证用户界面组件。 ----注----功能需求或者设计和实现的变化常常导致测试用例需要调整和修改,甚至有时候修改用例的时间会超过新建用例的时间。因此,在用例设计过程中,保持与设计、开发人员的密切沟通,及时了解功能变化的情况十分必要。否则后期再修改用例的时间成本很大。当然,在软件开发后期,软件需求和设计尽量保持稳定是最合适的。
  3、很难设想读者的知识:写测试用例的人往往极为熟悉被测试的功能。这些人常犯的错误是在测试用例中使用术语或缩写,而将来运行测试用例的人很可能看不懂这些测试用例。出现这种情况出现时,测试用例已不再能准确地重复,测试用例也失去了这关键属性之一。 ----注----为了用例便于后来者正常使用该用例,用例设计应尽量避免使用只有自己熟悉的专业词汇和缩写词,或者存在用例描述太简洁但自己能理解的情况。
  测试用例设计的误区
  创建好的测试用例是一个困难的过程。即使一个错误就可以毁掉测试用例的意图。一些易出问题的领域如下:
  1、步骤缺乏:  匆忙建立的测试用例或假设测试用例的一些步骤会被执行而未将它们包括在测试用例里是非常常见的错误, 它造成不能准确地重复。----注----必要的用例步骤不能省,避免在执行用例的时候出现描述不清导致模棱两可的情况发生,从而影响案例执行进度。
  2、太多细节: 虽然提供具体和足够的信息很重要,不必要的字词或冗长的解释,会使测试用例难以遵循。仅需包含足够的信息以便精确地运行测试用例。  ----注----太多的细节描述会增加案例设计的成本,适可而止即可。
  3、行话太多: 不要以为运行测试用例的人(包括产品技术支持和持续工程)都知道所有你写的缩略语,代号和缩写。阐明任何对整个产品生命周期有价值和必要的信息。  ----注----同上缺点3的注释。
  4、不明确的通过/失败标准: 如果运行测试后,不清楚测试是否通过或失败,那测试用例是毫无用处的。----注----测试用例的预期结果一定要准确和清晰。对于测试后存在不符合预期结果的情况,即可判断为失败,全部符合预期结果则为成功。

posted @ 2014-09-26 10:06 顺其自然EVO 阅读(155) | 评论 (0)编辑 收藏

JDBC连接MySQL数据库及演示样例

 JDBC是Sun公司制定的一个能够用Java语言连接数据库的技术。
  一、JDBC基础知识
  JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,能够为多种关系数据库提供统一訪问,它由一组用Java语言编写的类和接口组成。JDBC为数据库开发者提供了一个标准的API,据此能够构建更高级的工具和接口,使数据库开发者能够用纯 Java API 编写数据库应用程序,而且可跨平台执行,而且不受数据库供应商的限制。
  1、跨平台执行:这是继承了Java语言的“一次编译,到处执行”的特点;
  2、不受数据库供应商的限制:巧妙在于JDBC设有两种接口,一个是面向应用程序层,其作用是使得开发者通过SQL调用数据库和处理结果,而不须要考虑数据库的提供商;还有一个是驱动程序层,处理与详细驱动程序的交互,JDBC驱动程序能够利用JDBC API创建Java程序和数据源之间的桥梁。应用程序仅仅须要编写一次,便能够移到各种驱动程序上执行。Sun提供了一个驱动管理器,数据库供应商——如MySQLOracle,提供的驱动程序满足驱动管理器的要求就能够被识别,就能够正常工作。所以JDBC不受数据库供应商的限制。
  JDBC API能够作为连接Java应用程序与各种关系数据库的纽带,在带来方便的同一时候也有负面影响,下面是JDBC的优、缺点。长处例如以下:
  操作便捷:JDBC使得开发者不须要再使用复杂的驱动器调用命令和函数;
  可移植性强:JDBC支持不同的关系数据库,所以能够使同一个应用程序支持多个数据库的訪问,仅仅要载入对应的驱动程序就可以;
  通用性好:JDBC-ODBC桥接驱动器将JDBC函数换成ODBC;
  面向对象:能够将经常使用的JDBC数据库连接封装成一个类,在使用的时候直接调用就可以。
  缺点例如以下:
  訪问数据记录的速度受到一定程度的影响;
  更改数据源困难:JDBC可支持多种数据库,各种数据库之间的操作必有不同,这就给更改数据源带来了非常大的麻烦
  二、JDBC连接数据库的流程及其原理
  1、在开发环境中载入指定数据库的驱动程序。比如,接下来的实验中,使用的数据库是MySQL,所以须要去下载MySQL支持JDBC的驱动程序(最新的是:mysql-connector-java-5.1.18-bin.jar);而开发环境是MyEclipse,将下载得到的驱动程序载入进开发环境中(详细演示样例的时候会解说怎样载入)。
  2、在Java程序中载入驱动程序。在Java程序中,能够通过 “Class.forName(“指定数据库的驱动程序”)” 方式来载入加入到开发环境中的驱动程序,比如载入MySQL的数据驱动程序的代码为:  Class.forName(“com.mysql.jdbc.Driver”)
  3、创建数据连接对象:通过DriverManager类创建数据库连接对象Connection。DriverManager类作用于Java程序和JDBC驱动程序之间,用于检查所载入的驱动程序能否够建立连接,然后通过它的getConnection方法,依据数据库的URL、username和password,创建一个JDBC Connection 对象。如:Connection connection =  DriverManager.geiConnection(“连接数据库的URL", "username", "password”)。当中,URL=协议名+IP地址(域名)+port+数据库名称;username和password是指登录数据库时所使用的username和password。详细演示样例创建MySQL的数据库连接代码例如以下:
  Connection connectMySQL  =  DriverManager.geiConnection(“jdbc:mysql://localhost:3306/myuser","root" ,"root" );
  4、创建Statement对象:Statement 类的主要是用于运行静态 SQL 语句并返回它所生成结果的对象。通过Connection 对象的 createStatement()方法能够创建一个Statement对象。比如:Statement statament = connection.createStatement(); 详细演示样例创建Statement对象代码例如以下:
  Statement statamentMySQL =connectMySQL.createStatement();
  5、调用Statement对象的相关方法运行相相应的 SQL 语句:通过execuUpdate()方法用来数据的更新,包含插入和删除等操作,比如向staff表中插入一条数据的代码:
  statement.excuteUpdate( "INSERT INTO staff(name, age, sex,address, depart, worklen,wage)" + " VALUES ('Tom1', 321, 'M', 'china','Personnel','3','3000' ) ") ;
  通过调用Statement对象的executeQuery()方法进行数据的查询,而查询结果会得到 ResulSet对象,ResulSet表示运行查询数据库后返回的数据的集合,ResulSet对象具有能够指向当前数据行的指针。通过该对象的next()方法,使得指针指向下一行,然后将数据以列号或者字段名取出。假设当next()方法返回null,则表示下一行中没有数据存在。使用演示样例代码例如以下:
  ResultSet resultSel = statement.executeQuery( "select * from staff" );
  6、关闭数据库连接:使用完数据库或者不须要訪问数据库时,通过Connection的close() 方法及时关闭数据连接。
  三、JDBC应用演示样例实验
  实验内容:使用phpMyAdmin在MySQL中创建数据库(myuser),并加入实验所需的数据(新建staff表,加入一些记录);编写Java程序,利用JDBC连接在MySQL中创建好的数据库(myuser),对staff表格进行插入、更新、删除和查询等操作。
  实验环境及开发工具:Win7操作系统;jdk1.6.0_26;XAMPP1.7.7(MySQL 5.1,  phpMyAdmin);MyEclipse 8.5
  实验环境的搭建:可參考我的博客
  Java环境搭配:http://blog.csdn.net/cxwen78/article/details/6400798;
  windows系统XAMPP安装配置使用:http://blog.csdn.net/cxwen78/article/details/6847927
  实验过程及步骤:
  1、下载MySQL支持JDBC的驱动程序:假设已经有了,可跳过这一步。前往MySQL官网(http://www.mysql.com/products/connector/ )下载驱动程序,,MySQL针对不同的平台提供了不同的连接器,我们须要的是DBC Driver for MySQL (Connector/J),例如以下图所看到的,点击 Download 跟着站点的引导进行下载。打开下载得到的压缩包(mysql-connector-java-5.1.18.zip),将当中的Java包(mysql-connector-java-5.1.18-bin.jar),拷贝到MySQL文件夹下(仅是为了方便才放到这里),以备载入驱动程序时使用。
  2、创建数据库:使用phpMyAdmin,登录MySQL,创建数据库myuser,并在当中插入一个名为staff的表格。并加入一些数据,操作步骤如图,登录进去MySQL数据库后:
  1)创建数据库,名称为myuser,编码为utf8_general_ci(支持中文);
  2)新建表格,名称为staff,表格有8个字段;
  3)8个字段的设置,包含名称、类型、值的长度、初始值、编码等等(点击查看大图);
  4)加入成功后,查看的staff表格情况:
  5)往表格中插入一些实验所需数据,须要插入两条,一个是员工lucy的,还有lili的:
  3、在MyEclips中创建项目并在项目中加入MySQL驱动程序:创建的项目类型能够是Java项目或者是Java Web项目都能够。这里创建的是Web项目,项目名称能够随便取,我命名为“JavaWebChp07”。创建成功后将步骤1里下载得到的MySQL驱动程序包(mysql-connector-java-5.1.18-bin.jar)加入到project的Build path中,加入过程如图所看到的:
 4、编写JDBC连接MySQL数据库的实例详细代码,JDBC_Test.java:
  详细代码:
  <pre name="code" class="java">package chp07;
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.sql.Statement;
  public class JDBC_Test {
  // 创建静态全局变量
  static Connection conn;
  static Statement st;
  public static void main(String[] args) {
  insert(); //插入加入记录
  update(); //更新记录数据
  delete(); //删除记录
  query(); //查询记录并显示
  }
  /* 插入数据记录,并输出插入的数据记录数*/
  public static void insert() {
  conn = getConnection(); // 首先要获取连接,即连接到数据库
  try {
  String sql = "INSERT INTO staff(name, age, sex,address, depart, worklen,wage)"
  + " VALUES ('Tom1', 32, 'M', 'china','Personnel','3','3000')"; // 插入数据的sql语句
  st = (Statement) conn.createStatement(); // 创建用于运行静态sql语句的Statement对象
  int count = st.executeUpdate(sql); // 运行插入操作的sql语句,并返回插入数据的个数
  System.out.println("向staff表中插入 " + count + " 条数据"); //输出插入操作的处理结果
  conn.close(); //关闭数据库连接
  } catch (SQLException e) {
  System.out.println("插入数据失败" + e.getMessage());
  }
  }
  /* 更新符合要求的记录,并返回更新的记录数目*/
  public static void update() {
  conn = getConnection(); //相同先要获取连接,即连接到数据库
  try {
  String sql = "update staff set wage='2200' where name = 'lucy'";// 更新数据的sql语句
  st = (Statement) conn.createStatement(); //创建用于运行静态sql语句的Statement对象,st属局部变量
  int count = st.executeUpdate(sql);// 运行更新操作的sql语句,返回更新数据的个数
  System.out.println("staff表中更新 " + count + " 条数据"); //输出更新操作的处理结果
  conn.close(); //关闭数据库连接
  } catch (SQLException e) {
  System.out.println("更新数据失败");
  }
  }
  /* 查询数据库,输出符合要求的记录的情况*/
  public static void query() {
  conn = getConnection(); //相同先要获取连接,即连接到数据库
  try {
  String sql = "select * from staff"; // 查询数据的sql语句
  st = (Statement) conn.createStatement(); //创建用于运行静态sql语句的Statement对象,st属局部变量
  ResultSet rs = st.executeQuery(sql); //运行sql查询语句,返回查询数据的结果集
  System.out.println("最后的查询结果为:");
  while (rs.next()) { // 推断是否还有下一个数据
  // 依据字段名获取对应的值
  String name = rs.getString("name");
  int age = rs.getInt("age");
  String sex = rs.getString("sex");
  String address = rs.getString("address");
  String depart = rs.getString("depart");
  String worklen = rs.getString("worklen");
  String wage = rs.getString("wage");
  //输出查到的记录的各个字段的值
  System.out.println(name + " " + age + " " + sex + " " + address
  + " " + depart + " " + worklen + " " + wage);
  }
  conn.close(); //关闭数据库连接
  } catch (SQLException e) {
  System.out.println("查询数据失败");
  }
  }
  /* 删除符合要求的记录,输出情况*/
  public static void delete() {
  conn = getConnection(); //相同先要获取连接,即连接到数据库
  try {
  String sql = "delete from staff  where name = 'lili'";// 删除数据的sql语句
  st = (Statement) conn.createStatement(); //创建用于运行静态sql语句的Statement对象,st属局部变量
  int count = st.executeUpdate(sql);// 运行sql删除语句,返回删除数据的数量
  System.out.println("staff表中删除 " + count + " 条数据\n"); //输出删除操作的处理结果
  conn.close(); //关闭数据库连接
  } catch (SQLException e) {
  System.out.println("删除数据失败");
  }
  }
  /* 获取数据库连接的函数*/
  public static Connection getConnection() {
  Connection con = null; //创建用于连接数据库的Connection对象
  try {
  Class.forName("com.mysql.jdbc.Driver");// 载入Mysql数据驱动
  con = DriverManager.getConnection(
  "jdbc:mysql://localhost:3306/myuser", "root", "root");// 创建数据连接
  } catch (Exception e) {
  System.out.println("数据库连接失败" + e.getMessage());
  }
  return con; //返回所建立的数据库连接
  }
  }
  项目部署到server,然后执行结果:

posted @ 2014-09-24 15:39 顺其自然EVO 阅读(820) | 评论 (0)编辑 收藏

Unix/Linux中Cron的用法

cron是unix或者linux下用来定时任务的命令,大致的用法如下:
  1、服务的启动和关闭
  /sbin/service crond start   //启动服务
  /sbin/service crond stop    //关闭服务
  /sbin/service crond restart //重启服务
  /sbin/service crond reload  //重新载入配置
  也可以让该服务在开机时自启动:在/etc/rc.d/rc.local这个脚本的末尾加上如下脚本:
  /sbin/service crond start
  2、编辑cron服务
  crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数crontab -l //列出某个用户cron服务的详细内容
  crontab -r //删除没个用户的cron服务
  crontab -e //编辑某个用户的cron服务
  用crontab -u user -e 进入vi编辑模式,编辑的内容一定要符合下面的格式:
  * * * * * command
  这个格式的前一部分是对时间的设定,后面一部分是要执行的命令,当然,这个命令也可以是一个脚本。五个 * 的作用如下:
  分钟 (0-59)
  小時 (0-23)
  日期 (1-31)
  月份 (1-12)
  星期 (0-6)//0代表星期天
  每 次编辑完某个用户的cron设置后,cron自动在/var/spool/cron下生成一个与此用户同名的文件,此用户的cron信息都记录在这个文件 中,这个文件是不可以直接编辑的,只可以用crontab -e 来编辑。cron启动后每过一份钟读一次这个文件,检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron服务。
  3、定时方法说明
  除了数字之外,还有几个特殊的符号("*"、"/"和"-"、",")可以用来编辑启动时间,*代表所有的取值范围内的数字,"/"代表每的意思,"*/5"表示每5个单位,"-"代表从某个数字到某个数字,","分开几个离散的数字。以下举几个例子说明问题:
  每天早上6点:0 6 * * * command
  每两个小时:0 */2 * * * command
  晚上11点到早上8点之间每两个小时,早上八点:0 23-7/2,8 * * * command
  每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点:0 11 4 * 1-3 command
  1月1日早上4点:0 4 1 1 * command
  4、配置文件/etc/crontab的编辑
  cron 服务每分钟不仅读一次/var/spool/cron内的文件,还要读一次/etc/crontab,因此我们配置这个文件也能运用cron服务做一些事 情。用crontab配置是针对某个用户的,而编辑/etc/crontab是针对系统的任务。此文件的文件格式是:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root //如果出现错误,或者有数据输出,数据作为邮件发给这个帐号
HOME=/ //使用者运行的路径,这里是根目录
# run-parts
01 * * * * root run-parts /etc/cron.hourly //每小时执行/etc/cron.hourly内的脚本
02 4 * * * root run-parts /etc/cron.daily //每天执行/etc/cron.daily内的脚本
22 4 * * 0 root run-parts /etc/cron.weekly //每星期执行/etc/cron.weekly内的脚本
42 4 1 * * root run-parts /etc/cron.monthly //每月去执行/etc/cron.monthly内的脚本
  大家注意"run-parts"这个参数了,如果去掉这个参数的话,后面就可以写要运行的某个脚本名,而不是文件夹名了。
  5、权限设置
  默认情况下,所有用户都能访问cron工具,要对cron进行访问控制,则可以生成/etc/cron.allow与/etc/cron.deny文件。
  ①、这两个文件都不存在时,每个用户都可以访问cron工具。
  ②、默认情况下,应该有cron.deny(空文件),cron.allow需要自己创建。
  ③、存在/etc/cron.allow文件时,则只有cron.allow文件中允许的用户才能访问cron工具,如果也有/etc/cron.deny文件,则忽略cron.deny文件中的内容。

posted @ 2014-09-24 15:38 顺其自然EVO 阅读(227) | 评论 (0)编辑 收藏

深入理解Java:类加载机制及反射

 一、Java类加载机制
  1.概述
  Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能。
  虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
  2.工作机制
  类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示的对象组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:
  (1) 装载:查找和导入Class文件;
  (2) 链接:把类的二进制数据合并到JRE中;
  (a)校验:检查载入Class文件数据的正确性;
  (b)准备:给类的静态变量分配存储空间;
  (c)解析:将符号引用转成直接引用;
  (3) 初始化:对类的静态变量,静态代码块执行初始化操作
  Java程序可以动态扩展是由运行期动态加载和动态链接实现的;比如:如果编写一个使用接口的应用程序,可以等到运行时再指定其实际的实现(多态),解析过程有时候还可以在初始化之后执行;比如:动态绑定(多态);
  【类初始化】
  (1) 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
  (2) 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
  (3) 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
  (4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
  只有上述四种情况会触发初始化,也称为对一个类进行主动引用,除此以外,所有其他方式都不会触发初始化,称为被动引用
  代码清单1
  上述代码运行后,只会输出【---SuperClass init】, 而不会输出【SubClass init】,对于静态字段,只有直接定义这个字段的类才会被初始化,因此,通过子类来调用父类的静态字段,只会触发父类的初始化,但是这是要看不同的虚拟机的不同实现。
  代码清单2
  此处不会引起SuperClass的初始化,但是却触发了【[Ltest.SuperClass】的初始化,通过arr.toString()可以看出,对于用户代码来说,这不是一个合法的类名称,它是由虚拟机自动生成的,直接继承于Object的子类,创建动作由字节码指令newarray触发,此时数组越界检查也会伴随数组对象的所有调用过程,越界检查并不是封装在数组元素访问的类中,而是封装在数组访问的xaload,xastore字节码指令中.
代码清单3
  对常量ConstClass.value 的引用实际都被转化为NotInitialization类对自身常量池的引用,这两个类被编译成class后不存在任何联系。
  【装载】
  在装载阶段,虚拟机需要完成以下3件事情
  (1) 通过一个类的全限定名来获取定义此类的二进制字节流
  (2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  (3) 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
  虚拟机规范中并没有准确说明二进制字节流应该从哪里获取以及怎样获取,这里可以通过定义自己的类加载器去控制字节流的获取方式。
  【验证】
  虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有害的字节流而导致系统奔溃。
  【准备】
  准备阶段是正式为类变量分配并设置类变量初始值的阶段,这些内存都将在方法区中进行分配,需要说明的是:
  这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中;这里所说的初始值“通常情况”是数据类型的零值,假如:
  public static int value = 123;
  value在准备阶段过后的初始值为0而不是123,而把value赋值的putstatic指令将在初始化阶段才会被执行
  二、类加载器与双亲委派模型
  类加载器
  (1) Bootstrap ClassLoader : 将存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar 名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用
  (2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库加载。开发者可以直接使用扩展类加载器。
  (3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。
  双亲委派模型
  工作过程:如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
  好处:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。
  java.lang.ClassLoader中几个最重要的方法:
  //加载指定名称(包括包名)的二进制类型,供用户调用的接口
  public Class<?> loadClass(String name);
  //加载指定名称(包括包名)的二进制类型,同时指定是否解析(但是,这里的resolve参数不一定真正能达到解析的效果),供继承用
  protected synchronized Class<?> loadClass(String name, boolean resolve);
  protected Class<?> findClass(String name)
  //定义类型,一般在findClass方法中读取到对应字节码后调用,可以看出不可继承(说明:JVM已经实现了对应的具体功能,解析对应的字节码,产生对应的内部数据结构放置到方法区,所以无需覆写,直接调用就可以了)
  protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError{}
  如下是实现双亲委派模型的主要代码:
  三、反射
  Reflection机制允许程序在正在执行的过程中,利用Reflection APIs取得任何已知名称的类的内部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,并可以在执行的过程中,动态生成instances、变更fields内容或唤起methods。
  1、获取构造方法
  Class类提供了四个public方法,用于获取某个类的构造方法。
  Constructor getConstructor(Class[] params)
  根据构造函数的参数,返回一个具体的具有public属性的构造函数
  Constructor getConstructors()
  返回所有具有public属性的构造函数数组
  Constructor getDeclaredConstructor(Class[] params)
  根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
  Constructor getDeclaredConstructors()
  返回该类中所有的构造函数数组(不分public和非public属性)
  2、获取类的成员方法
  与获取构造方法的方式相同,存在四种获取成员方法的方式。
  Method getMethod(String name, Class[] params)
  根据方法名和参数,返回一个具体的具有public属性的方法
  Method[] getMethods()
  返回所有具有public属性的方法数组
  Method getDeclaredMethod(String name, Class[] params)
  根据方法名和参数,返回一个具体的方法(不分public和非public属性)
  Method[] getDeclaredMethods()
  返回该类中的所有的方法数组(不分public和非public属性)
  3、获取类的成员变量(成员属性)
  存在四种获取成员属性的方法
  Field getField(String name)
  根据变量名,返回一个具体的具有public属性的成员变量
  Field[] getFields()
  返回具有public属性的成员变量的数组
  Field getDeclaredField(String name)
  根据变量名,返回一个成员变量(不分public和非public属性)
  Field[] getDelcaredFields()
  返回所有成员变量组成的数组(不分public和非public属性)

posted @ 2014-09-24 15:36 顺其自然EVO 阅读(167) | 评论 (0)编辑 收藏

EnterpriseFrameWork框架基础功能之字典数据配置管理

框架中的“通用字典数据配置管理”主要解决的问题是,所有的行业软件给客户实施第一步一般都是基础数据的维护,一个系统的字典是少不了的,涉及业务范围越广字典就越多,如果每一个字典数据都做一个界面来进行维护数据的话,那开发工作量还是比较大的,所以得考虑设计一个通用的模块来管理这些字典数据;
  1)通用字典管理功能清单
  2)通用字典管理界面展示,包括Winform版和Web版
 3)通用字典管理核心业务流程图与数据库表关系图
  4)通用字典管理关键点技术实现
  1.字典保存数据实现
//保存数据
public Object SaveResultDataTable(int titleId, string IdName, object IdValue, Dictionary<string, object> fieldAndValue)
{
if (IdValue.Equals(System.DBNull.Value) == true)//插入数据
{
string fields = "";
string values = "";
string strsql = "insert into {0} ({1}) values({2})";
foreach (KeyValuePair<string, object> val in fieldAndValue)
{
fields += (fields == "" ? "" : ",") + val.Key;
values += (values == "" ? "" : ",") + ConvertDBValue(val.Value);
}
BaseGeneralTitle title = NewObject<BaseGeneralTitle>().getmodel(titleId) as BaseGeneralTitle;
IdValue = oleDb.InsertRecord(string.Format(strsql, title.TableName, fields, values));
}
else//更新数据
{
string field_values = "";
string strsql = "update  {0} set {1} where {2}";
foreach (KeyValuePair<string, object> val in fieldAndValue)
{
field_values += (field_values == "" ? "" : ",") + val.Key + "=" + ConvertDBValue(val.Value);
}
BaseGeneralTitle title = NewObject<BaseGeneralTitle>().getmodel(titleId) as BaseGeneralTitle;
oleDb.DoCommand(string.Format(strsql, title.TableName, field_values, IdName + "=" + ConvertDBValue(IdValue)));
}
return IdValue;
}
  2.Web版JqueryEasyUI的Gird控件动态列
<div id="resulttool" class="toolbar">
<a href="#" class="easyui-linkbutton" plain="true" iconCls="icon-search" onclick="btnresult_search();">查询</a>
<a href="#" class="easyui-linkbutton" plain="true" iconCls="icon-add" onclick="btnresult_addData();">增加</a>
<a href="#" class="easyui-linkbutton" plain="true" iconCls="icon-edit" onclick="btnresult_editData();">编辑</a>
<a href="#" class="easyui-linkbutton" plain="true" iconCls="icon-cancel" onclick="btnresult_delData();">删除</a>
</div>
<table id="resultGird"  class="easyui-datagrid" fit="true" border="false" toolbar="#resulttool" iconCls="icon-edit" pagination="true" idField="<%=Session["resulstDataKeyName"]%>">
<thead>
<tr>
<th field="ck" checkbox="true"></th>
<%=Session["resulstDatacolmodel"]%>
</tr>
</thead>
</table>

posted @ 2014-09-24 15:34 顺其自然EVO 阅读(196) | 评论 (0)编辑 收藏

AppScan Source V8.8 中弃用的功能

从 AppScan Source V8.8 开始,不再支持以下操作系统
  Microsoft Windows XP
  Microsoft Windows Server 2003,所有版本和修订版
  此外:
  Visual Studio 2005 项目文件不再受支持,而且 AppScan Source for Development(Visual Studio 插件) 不再能适用于 Visual Studio 2005。
  Eclipse V3.3、V3.4 和 V3.5 项目文件和工作空间不再受支持,而且 AppScan Source for Development(Eclipse 插件) 不再能适用于 Eclipse V3.3、V3.4 和 V3.5。
  Rational Application Developer for WebSphere? Software (RAD) V7.x 项目文件和工作空间不再受支持,而且 IBM Security AppScan Source for Development plug-in for IBM Rational Application Developer for WebSphere Software (RAD) 不再能适用于 RAD V7.x。
  Java 和 JSP 编译不再支持 Tomcat V3 (Jasper 1)。如果要升级 AppScan Source 并使用该版本的 Tomcat,您需要将 Tomcat 升级到 AppScan Source V8.8 支持的版本(请参阅 AppScan Source 系统需求以了解受支持的 Tomcat 版本)。
  IBM Rational ClearQuest? V7.0 和 IBM Rational Team Concert? V2.0.0.2 不再是受支持的缺陷跟踪系统。
  注意:如果之前装的8.7的AppScan Enterprise Server,现在把AppScan Source升级到8.8,运行会提示版本不兼容!

posted @ 2014-09-24 15:33 顺其自然EVO 阅读(503) | 评论 (0)编辑 收藏

解决Junit单元测试 找不到类

 做junit 单元测试时,发现怎么执行都是以前编译过得代码。
  最后找到原因了, src/test/java 编译完的.class路径是 Default output folder
  Default output folder:  zphVip/src/main/webapp/WEB-INF/classes
  解决
  1 勾选 Allow output floders for source folders  ------允许源文件夹编译过后的.class输入文件夹自己指定
  2 Edit 指定 output floder为 target/classes-----不适用默认,自己指定output floder

posted @ 2014-09-24 15:33 顺其自然EVO 阅读(638) | 评论 (0)编辑 收藏

在MS单元测试中引发期望异常

 首先准备一个引发异常的方法。
  1 public static void ThrowException()
  2 {
  3     throw new ArgumentException();
  4 }
  然后在单元测试项目中,写下测试方法。
  [TestMethod]
  [ExpectedException(typeof(ArgumentException))]// 构造函数中为期望引发的异常。
  public void ThrowExceptionTest()
  {
  Program.ThrowException();// 调用被测试的方法。
  }
  如果测试通过,则说明被测试的方法与预期正确,否则被测试的方法逻辑存在错误。

posted @ 2014-09-24 15:32 顺其自然EVO 阅读(140) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 41 42 43 44 45 46 47 48 49 下一页 Last 
<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜