??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲成a人片在线观看日本麻豆 ,亚洲无人区视频大全,国产精品亚洲成在人线http://m.tkk7.com/zeroone0/category/9397.htmlzh-cnFri, 02 Mar 2007 15:40:51 GMTFri, 02 Mar 2007 15:40:51 GMT60jxl的一些ȝhttp://m.tkk7.com/zeroone0/articles/42343.htmlzeroone0zeroone0Fri, 21 Apr 2006 08:12:00 GMThttp://m.tkk7.com/zeroone0/articles/42343.htmlhttp://m.tkk7.com/zeroone0/comments/42343.htmlhttp://m.tkk7.com/zeroone0/articles/42343.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/42343.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/42343.htmlOutputStream os=new FileOutputStream("c:\\excel2.xls");

再徏完这个文件的时候再建立工作文g
jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(os));

如果q个文g已经存在,那么我们可以在这个文仉面加入一个sheetZ和以前的数据q行分开;
jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
在createSheetҎ(gu)里前面的参数是sheet名,后面是要操作的sheet?

接下来就可以往q个文g里面写入数据?


写入数据的时候注意的格式


Q?Q添加的字体样式
jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
WritableFont()Ҏ(gu)里参数说明:(x)
q个Ҏ(gu)是一个容器,可以放进d多属?
W一? TIMES是字体大,他写的是18
W二? BOLD是判断是否ؓ(f)斜体,选择true时ؓ(f)斜体
W三? ARIAL
W四? UnderlineStyle.NO_UNDERLINE 下划U?
W五? jxl.format.Colour.RED 字体颜色是红色的

jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);

jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell"QwcfF);
ws.addCell(labelC);
在Label()Ҏ(gu)里面有三个参?
W一个是代表列数,
W二是代表行敎ͼ
W三个代表要写入的内?
W四个是可选项Q是输入q个label里面的样?
然后通过写sheet的方法addCellQ)把内容写qsheet里面?

Q?Q添加带有formatting的Number对象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");


Q?Q添加Number对象
Q?.1Q显Cnumber对象数据的格?

jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);

jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
ws.addCell(labelNF);
Number()Ҏ(gu)参数说明:
前两上表C入的位置
W三个表C入的内容


Q?Q添加Boolean对象
jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
ws.addCell(labelB);


Q?Q添加DateTime对象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
ws.addCell(labelDT);
DateTime()Ҏ(gu)的参数说?
前两个表C入的位置
W三个表C入的当前旉


Q?Q添加带有formatting的DateFormat对象
q个昄当前旉的所有信息,包括q月日小时分U?
jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
ws.addCell(labelDTF);

Q?Q添加带有字体颜色Formatting的对?
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);

import="jxl.format.*
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL,20,WritableFont.BOLD,false,UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.GREEN);

Q?Q设|单元格样式

jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
wcfFC.setBackGround(jxl.format.Colour.RED);//讄单元格的颜色为红?
wcfFC = new jxl.write.Label(6,0,"i love china",wcfFC);

FROM: http://forum.javaeye.com/viewtopic.php?t=4157&postdays=0&postorder=asc&start=0

zeroone0 2006-04-21 16:12 发表评论
]]>
JAVA生成EXCEL文ghttp://m.tkk7.com/zeroone0/articles/42340.htmlzeroone0zeroone0Fri, 21 Apr 2006 07:59:00 GMThttp://m.tkk7.com/zeroone0/articles/42340.htmlhttp://m.tkk7.com/zeroone0/comments/42340.htmlhttp://m.tkk7.com/zeroone0/articles/42340.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/42340.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/42340.html 1 从Excel文gd数据?/font>

Java Excel API既可以从本地文gpȝ的一个文?.xls)Q也可以从输入流中读取Excel数据表。读取Excel数据表的W一步是创徏Workbook(术语Q工作薄)Q下面的代码片段举例说明了应该如何操作:(x)(完整代码见ExcelReading.java)

												
														import java.io.*;
import jxl.*;
????
try
{
//构徏Workbook对象, 只读Workbook对象
	//直接从本地文件创建Workbook
//从输入流创徏Workbook
    InputStream is = new FileInputStream(sourcefile);
    jxl.Workbook rwb = Workbook.getWorkbook(is);
}
catch (Exception e)
{
	e.printStackTrace();
}

												
										

一旦创ZWorkbookQ我们就可以通过它来讉KExcel Sheet(术语Q工作表)。参考下面的代码片段Q?/p>
												
														//获取W一张Sheet?
Sheet rs = rwb.getSheet(0);

												
										

我们既可能通过Sheet的名U来讉K它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一Ҏ(gu)下标?开始,像数组一栗?/p>

一旦得CSheetQ我们就可以通过它来讉KExcel Cell(术语Q单元格)。参考下面的代码片段Q?/p>
												
														//获取W一行,W一列的?
Cell c00 = rs.getCell(0, 0);
String strc00 = c00.getContents();

//获取W一行,W二列的?
Cell c10 = rs.getCell(1, 0);
String strc10 = c10.getContents();

//获取W二行,W二列的?
Cell c11 = rs.getCell(1, 1);
String strc11 = c11.getContents();

System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());

												
										

如果仅仅是取得Cell的|我们可以方便地通过getContents()Ҏ(gu)Q它可以Q何类型的Cell值都作ؓ(f)一个字W串q回。示例代码中Cell(0, 0)是文本型QCell(1, 0)是数字型QCell(1,1)是日期型Q通过getContents()Q三U类型的q回值都是字W型?/p>

如果有需要知道Cell内容的确切类型,API也提供了一pd的方法。参考下面的代码片段Q?/p>
												
														String strc00 = null;
double strc10 = 0.00;
Date strc11 = null;

Cell c00 = rs.getCell(0, 0);
Cell c10 = rs.getCell(1, 0);
Cell c11 = rs.getCell(1, 1);

if(c00.getType() == CellType.LABEL)
{
LabelCell labelc00 = (LabelCell)c00;
strc00 = labelc00.getString();
}
if(c10.getType() == CellType.NUMBER)
{
	NmberCell numc10 = (NumberCell)c10;
strc10 = numc10.getValue();
}
if(c11.getType() == CellType.DATE)
{
DateCell datec11 = (DateCell)c11;
strc11 = datec11.getDate();
}

System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());

												
										

在得到Cell对象后,通过getType()Ҏ(gu)可以获得该单元格的类型,然后与API提供的基本类型相匚wQ强制{换成相应的类型,最后调用相应的取值方法getXXX()Q就可以得到定cd的倹{API提供了以下基本类型,与Excel的数据格式相对应Q如下图所C:(x)



每种cd的具体意义,请参见Java Excel API Document?/p>

当你完成对Excel?sh)子表格数据的处理后Q一定要使用close()Ҏ(gu)来关闭先前创建的对象Q以释放d数据表的q程中所占用的内存空_(d)在读取大量数据时昑־ؓ(f)重要。参考如下代码片D:(x)

												
														//操作完成Ӟ关闭对象Q释攑֍用的内存I间
rwb.close();

												
										

Java Excel API提供了许多访问Excel数据表的Ҏ(gu)Q在q里我只要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API Document?/p>

WorkbookcL供的Ҏ(gu)

1. int getNumberOfSheets()
获得工作薄(WorkbookQ中工作表(SheetQ的个数Q示例:(x)

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
int sheets = rwb.getNumberOfSheets();

												
										

2. Sheet[] getSheets()
q回工作薄(WorkbookQ中工作表(SheetQ对象数l,CZQ?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
Sheet[] sheets = rwb.getSheets();

												
										

3. String getVersion()
q回正在使用的API的版本号Q好像是没什么太大的作用?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
String apiVersion = rwb.getVersion();

												
										

Sheet接口提供的方?/b>

1) String getName()
获取Sheet的名UͼCZQ?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
String sheetName = rs.getName();

												
										

2) int getColumns()
获取Sheet表中所包含的d敎ͼCZQ?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsColumns = rs.getColumns();

												
										

3) Cell[] getColumn(int column)
获取某一列的所有单元格Q返回的是单元格对象数组Q示例:(x)

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getColumn(0);

												
										

4) int getRows()
获取Sheet表中所包含的总行敎ͼCZQ?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsRows = rs.getRows();

												
										

5) Cell[] getRow(int row)
获取某一行的所有单元格Q返回的是单元格对象数组Q示例子Q?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getRow(0);

												
										

6) Cell getCell(int column, int row)
获取指定单元格的对象引用Q需要注意的是它的两个参敎ͼW一个是列数Q第二个是行敎ͼq与通常的行、列l合有些不同?

												
														jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell cell = rs.getCell(0, 0);

												
										

2 生成新的Excel工作?/font>

下面的代码主要是向大家介l如何生成简单的Excel工作表,在这里单元格的内Ҏ(gu)不带M修饰?如:(x)字体Q颜色等{?Q所有的内容都作为字W串写入?完整代码见ExcelWriting.java)

与读取Excel工作表相|首先要用Workbookcȝ工厂Ҏ(gu)创徏一个可写入的工作薄(Workbook)对象Q这里要注意的是Q只能通过API提供的工厂方法来创徏WorkbookQ而不能用WritableWorkbook的构造函敎ͼ因ؓ(f)cWritableWorkbook的构造函Cؓ(f)protectedcd。示例代码片D如下:(x)

												
														import java.io.*;
import jxl.*;
import jxl.write.*;
????
try
{
//构徏Workbook对象, 只读Workbook对象
//Method 1Q创建可写入的Excel工作?
    jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));

//Method 2Q将WritableWorkbook直接写入到输出流
/*
    OutputStream os = new FileOutputStream(targetfile);
    jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
*/
}
catch (Exception e)
{
	e.printStackTrace();
}

												
										

API提供了两U方式来处理可写入的输出,一U是直接生成本地文gQ如果文件名不带全\径的话,~省的文件会(x)定位在当前目录,如果文g名带有全路径的话Q则生成的Excel文g则会(x)定位在相应的目录Q另外一U是Excel对象直接写入到输出流Q例如:(x)用户通过览器来讉KWeb服务器,如果HTTP头设|正的话,览器自动调用客L(fng)的Excel应用E序Q来昄动态生成的Excel?sh)子表格?/p>

接下来就是要创徏工作表,创徏工作表的Ҏ(gu)与创建工作薄的方法几乎一P同样是通过工厂模式Ҏ(gu)获得相应的对象,该方法需要两个参敎ͼ一个是工作表的名称Q另一个是工作表在工作薄中的位|,参考下面的代码片段Q?/p>
												
														//创徏Excel工作?
jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);

												
										

"q锅也支好了Q材料也准备齐全了,可以开始下锅了Q?Q现在要做的只是实例化API所提供的Excel基本数据cdQƈ它们添加到工作表中可以了Q参考下面的代码片段Q?/p>
												
														//1.dLabel对象
jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
ws.addCell(labelC);

//d带有字型Formatting的对?
jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
ws.addCell(labelCF);

//d带有字体颜色Formatting的对?
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
ws.addCell(labelCF);

//2.dNumber对象
jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
ws.addCell(labelN);

//d带有formatting的Number对象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
ws.addCell(labelNF);

//3.dBoolean对象
jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
ws.addCell(labelB);

//4.dDateTime对象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
ws.addCell(labelDT);

//d带有formatting的DateFormat对象
jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
ws.addCell(labelDTF);

												
										

q里有两点大家要引v大家的注意。第一点,在构造单元格Ӟ单元格在工作表中的位|就已经定了。一旦创建后Q单元格的位|是不能够变更的Q尽单元格的内Ҏ(gu)可以改变的。第二点Q单元格的定位是按照下面q样的规?column, row)Q而且下标都是?开始,例如QA1被存储在(0, 0)QB1被存储在(1, 0)?/p>

最后,不要忘记关闭打开的Excel工作薄对象,以释攑֍用的内存Q参见下面的代码片段Q?/p>
												
														//写入Exel工作?
wwb.write();

//关闭Excel工作薄对?
wwb.close();

												
										

q可能与dExcel文g的操作有少不同Q在关闭Excel对象之前Q你必须要先调用write()Ҏ(gu)Q因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文g中。如果你先关闭了Excel对象Q那么只能得C张空的工作薄了?/p>

3 拯、更新Excel工作?/font>

接下来简要介l一下如何更C个已l存在的工作薄,主要是下面二步操作,W一步是构造只ȝExcel工作薄,W二步是利用已经创徏的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段Q?完整代码见ExcelModifying.java)

												
														//创徏只读的Excel工作薄的对象
jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));

//创徏可写入的Excel工作薄对?
jxl.write.WritableWorkbook  wwb = Workbook.createWorkbook(new File(targetfile), rw);
            
//dW一张工作表
jxl.write.WritableSheet ws = wwb.getSheet(0);

//获得W一个单元格对象
jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
            
//判断单元格的cd, 做出相应的{?
if(wc.getType() == CellType.LABEL)
{
Label l = (Label)wc;
    l.setString("The value has been modified.");
}

//写入Excel对象
wwb.write();

//关闭可写入的Excel对象
wwb.close();

//关闭只读的Excel对象
rw.close();

												
										

之所以用这U方式构建Excel对象Q完全是因ؓ(f)效率的原因,因ؓ(f)上面的示例才是API的主要应用。ؓ(f)了提高性能Q在d工作表时Q与数据相关的一些输Z息,所有的格式信息Q如Q字体、颜色等{,是不被处理的Q因为我们的目的是获得行数据的|既没有了修饎ͼ也不?x)对行数据的g生什么媄响。唯一的不利之处就是,在内存中?x)同时保存两个同L(fng)工作表,q样当工作表体积比较大时Q会(x)占用相当大的内存Q但现在好像内存的大ƈ不是什么关键因素了?/p>

一旦获得了可写入的工作表对象,我们可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()Ҏ(gu)Q因为单元格已经于工作表当中Q所以我们只需要调用相应的setXXX()Ҏ(gu)Q就可以完成更新的操作了?/p>

单元格原有的格式化修饰是不能去掉的Q我们还是可以将新的单元g饰加上去Q以使单元格的内容以不同的Ş式表现?/p>

新生成的工作表对象是可写入的Q我们除了更新原有的单元格外Q还可以d新的单元格到工作表中Q这与示?的操作是完全一L(fng)?/p>

最后,不要忘记调用write()Ҏ(gu)Q将更新的内容写入到文g中,然后关闭工作薄对象,q里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的?br />
FROM: http://www-128.ibm.com/developerworks/cn/java/l-javaExcel/index.html



zeroone0 2006-04-21 15:59 发表评论
]]>
Java Annotationhttp://m.tkk7.com/zeroone0/articles/42171.htmlzeroone0zeroone0Thu, 20 Apr 2006 07:30:00 GMThttp://m.tkk7.com/zeroone0/articles/42171.htmlhttp://m.tkk7.com/zeroone0/comments/42171.htmlhttp://m.tkk7.com/zeroone0/articles/42171.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/42171.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/42171.html一、AnnotationI竟是什么?

Annotation提供了一条与E序元素兌M信息或者Q何元数据QmetadataQ的途径。从某些斚w看,annotation像修饰W一栯使用Qƈ应用于包、类型、构造方法、方法、成员变量、参数、本地变量的声明中。这些信息被存储在annotation的“name=value”结构对中。annotationcd是一U接口,能够通过java反射API的方式提供对其信息的讉K?br />
annotation能被用来为某个程序元素(cR方法、成员变量等Q关联Q何的信息。需要注意的是,q里存在着一个基本的潜规则:(x)annotaion不能影响E序代码的执行,无论增加、删除annotationQ代码都始终如一的执行。另外,管一些annotation通过java的反apiҎ(gu)在运行时被访问,而java语言解释器在工作时忽略了q些annotation。正是由于java虚拟机忽略了annotationQ导致了annotationcd在代码中是“不起作用”的Q只有通过某种配套的工h?x)对annotationcd中的信息q行讉K和处理。本文中涵盖标准的annotation和meta-annotationcdQ陪伴这些annotationcd的工hjava~译器(当然要以某种Ҏ(gu)的方式处理它们)?br />
׃上述原因Qannotation在用时十分ѝ一个本地变量可以被一个以NonNull命名的annotationcd所标注Q来作ؓ(f)对这个本地变量不能被赋予null值的断言。而我们可以编写与之配套的一个annotation代码分析工具Q用它来对h前面变量的代码进行解析,q且试验证q个断言。当然这些代码ƈ不必自己~写。在JDK安装后,在JDK/bin目录中可以找到名为“apt”的工具Q它提供了处理annotation的框Ӟ(x)它启动后扫描源代码中的annotationQƈ调用我们定义好的annotation处理器完成我们所要完成的工作Q比如验证前面例子中的断aQ。说到这里,annotation的强大功能似乎可以替代XDocletq类的工具了Q随着我们的深入,大家?x)更加坚信这一炏V?br />注:(x)详细描述请参看jsr250规范Q?br />http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/

二、Annotation的定义:(x)

q段文字开始介lannotation相关技术。在此大家将看到java5.0的标准annotationcdQ这U标准类型就是前文中所说的“内建”类型,它们可以直接被javac支持。可喜的是,在java6.0beta版中的javac已经加入了对自定义annotation的支持?br />
1。Annotation的概念和语法Q?/span>

首先Q关键的概念是理解annotation是与一个程序元素相兌信息或者元数据的标注。它从不影响javaE序的执行,但是对例如编译器警告或者像文档生成器等辅助工具产生影响?br />
下面是常用的annotation列表Q我们应该注意在annotation和annotationcd之间的不同:(x)

A.annotationQ?/span>
annotation使用了在java5.0所带来的新语法Q它的行为十分类似public、finalq样的修饰符。每个annotationh一个名字和成员个数>=0。每个annotation的成员具有被UCؓ(f)name=value对的名字和|像javabean一PQname=value装蝲了annotation的信息?br />
B.annotationcdQ?/span>
annotationcd定义了annotation的名字、类型、成员默认倹{一个annotationcd可以说是一个特D的java接口Q它的成员变量是受限制的Q而声明annotationcd旉要用新语法。当我们通过java反射api讉Ka(chn)nnotationӞq回值将是一个实C该annotationcd接口的对象,通过讉Kq个对象我们能方便的讉K到其annotation成员。后面的章节提到在java5.0的java.lang包里包含?个标准annotationcd?br />
C.annotation成员Q?/span>
annotation的成员在annotationcd中以无参数的Ҏ(gu)的Ş式被声明。其Ҏ(gu)名和q回值定义了该成员的名字和类型。在此有一个特定的默认语法Q允许声明Q何annotation成员的默认|(x)一个annotation可以name=value对作为没有定义默认值的annotation成员的|当然也可以用name=valueҎ(gu)覆盖其它成员默认倹{这一Ҏ(gu)些近似类的承特性,父类的构造函数可以作为子cȝ默认构造函敎ͼ但是也可以被子类覆盖?br />
D.marker annotationcdQ?/span>
一个没有成员定义的annotationcd被称为marker annotation。这Uannotationcd仅用自w的存在与否来ؓ(f)我们提供信息。如后面要说的Override?br />
E.meta-annotationQ?/span>
meta-annotation也称为元annotationQ它是被用来声明annotationcd的annotation。Java5.0提供了一些标准的?annotationcd。下面介l的target、retention是meta-annotation?br />
F.targetQ?/span>
annotation的target是一个被标注的程序元素。target说明了annotation所修饰的对象范_(d)(x)annotation可被用于packages、typesQ类、接口、枚举、annotationcdQ、类型成员(Ҏ(gu)、构造方法、成员变量、枚丑ր|、方法参数和本地变量Q如循环变量、catch参数Q。在annotationcd的声明中使用了target可更加明晰其修饰的目标?br />
G.retentionQ?/span>
annotation的retention定义了该annotation被保留的旉长短Q某些annotation仅出现在源代码中Q而被~译器丢弃;而另一些却被编译在class文g中;~译在class文g中的annotation可能?x)被虚拟机忽略,而另一些在class被装载时被dQ请注意q不影响class的执行,因ؓ(f)annotation与class在用上是被分离的)。用这个meta-annotation可以对annotation的“生命周期”限制?br />
H.metadataQ?/span>
׃metadata被广泛用于各种计算机开发过E中Q所以当我们在这里谈论的metadata卛_数据通常指被annotation装蝲的信息或者annotation本n?br />
2。用标准AnnotationQ?/span>
java5.0在java.lang包中定义?U标准的annotationcdQ?br />
A.OverrideQ?/span>
java.lang.Override是一个marker annotationcdQ它被用作标注方法。它说明了被标注的方法重载了父类的方法,起到了断a的作用。如果我们用了q种annotation在一个没有覆盖父cL法的Ҏ(gu)Ӟjava~译器将以一个编译错误来警示?br />q个annotaton常常在我们试图覆盖父cL法而确又写错了Ҏ(gu)名时发挥威力?br />
使用Ҏ(gu)极其单:(x)在用此annotation时只要在被修饰的Ҏ(gu)前面加上@Override?br />下面的代码是一个用@Override修饰一个企N载父cȝtoStringҎ(gu)Q而又存在拼写错误的sampleQ?br />

@Override
public String toSting() {   // 注意Ҏ(gu)名拼写错?br />    return "[" + super.toString() + "]";
}


B.DeprecatedQ?/span>
同样Deprecated也是一个marker annotation。当一个类型或者类型成员用@Deprecated修饰的话Q编译器不鼓励使用q个被标注的E序元素。而且q种修饰h一定的“gl性”:(x)如果我们在代码中通过l承或者覆盖的方式使用了这个过时的cd或者成员,虽然l承或者覆盖后的类型或者成员ƈ不是被声明ؓ(f)@DeprecatedQ但~译器仍然要报警?br />值得注意Q@Deprecatedq个annotationcd和javadoc中的@deprecatedq个tag是有区别的:(x)前者是java~译器识别的Q而后者是被javadoc工具所识别用来生成文档Q包含程序成员ؓ(f)什么已l过时、它应当如何被禁止或者替代的描述Q?br />在java5.0Qjava~译器仍然象其从前版本那样寻找@deprecatedq个javadoc tagQƈ使用它们产生警告信息。但是这U状况将在后l版本中改变Q我们应在现在就开始用@Deprecated来修饰过时的Ҏ(gu)而不是@deprecated javadoc tag?br />

下面是一D用@Deprecated的代码:(x)
/**
* q里是javadoc的@deprecated声明.
* @deprecated No one has players for this format any more.  Use VHS instead.
*/
@Deprecated public class Betamax { ... }


C.SuppressWarningsQ?/span>
@SuppressWarnings被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0Qsun提供的javac~译器ؓ(f)我们提供?Xlint选项来ɾ~译器对合法的程序代码提告,此种警告从某U程度上代表了程序错误。例如当我们使用一个generic collectionc而又没有提供它的cdӞ~译器将提示?unchecked warning"的警告?br />
通常当这U情况发生时Q我们就需要查扑ּ赯告的代码。如果它真的表示错误Q我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的caseQ那么我们就应增加一个默认的case来避免这U警告?br />总Q有时我们无法避免这U警告,例如Q我们用必d非generic的旧代码交互的generic collectioncLQ我们不能避免这个unchecked warning。此时@SuppressWarningpz上用场了,在调用的Ҏ(gu)前增加@SuppressWarnings修饰Q告诉编译器停止Ҏ(gu)Ҏ(gu)的警告?br />SuppressWarning不是一个marker annotation。它有一个类型ؓ(f)String[]的成员,q个成员的gؓ(f)被禁止的警告名。对于javac~译器来Ԍ?Xlint选项有效的警告名也同样对@SuppressWarings有效Q同时编译器忽略掉无法识别的警告名?br />
annotation语法允许在annotation名后跟括P括号中是使用逗号分割的name=value对用于ؓ(f)annotation的成员赋|(x)

@SuppressWarnings(value={"unchecked","fallthrough"})
public void lintTrap() { /* sloppy method body omitted */ }


在这个例子中SuppressWarnings annotationcd只定义了一个单一的成员,所以只有一个简单的value={...}作ؓ(f)name=value寏V又׃成员值是一个数l,故用大括号来声明数l倹{?br />
注意Q我们可以在下面的情况中~写annotationQ当annotation只有单一成员Qƈ成员命名?value="。这时可以省?value="。比如将上面的SuppressWarnings annotationq行~写Q?br />

@SuppressWarnings({"unchecked","fallthrough"})

如果SuppressWarnings所声明的被止警告个数Z个时Q可以省d括号Q?br />

@SuppressWarnings("unchecked")


3。Annotation语法Q?/span>

在上一个章节中Q我们看C写marker annotation和单一成员annotation的语法。下面本人来介绍一下完整的语法Q?br />
annotation由“@+annotationcd名称+(..逗号分割的name-value?..)”组成。其中成员可以按照Q何的序。如果annotationcd定义了某个成员的默认|则这个成员可以被省略。成员值必Mؓ(f)~译时常量、内嵌的annotation或者数l?br />
下面我们定义一个annotationcd名ؓ(f)ReviewsQ它有一个由@Review annotation数组构成的成员。这个@Review annotationcd有三个成员:(x)"reviewer"是一个字W串Q?comment" 是一个具有默认值的可选的字符Ԍ"grade"是一个Review.Grade枚Dcd倹{?br />

@Reviews({  // Single-value annotation, so "value=" is omitted here
    @Review(grade=Review.Grade.EXCELLENT,
            reviewer="df"),
    @Review(grade=Review.Grade.UNSATISFACTORY,
            reviewer="eg",
            comment="This method needs an @Override annotation")
})

annotation语法的另一个重要规则是没有E序成员可以有多于一个的同一annotation实例。例如在一个类中简单的攄多个@Review annotation。这也是在上面代码中定义@Reviews annotationcd数组的原因?br />
4。Annotation成员cd和|(x)

annotation成员必须是非I的~译时常量表辑ּ。可用的成员cd为:(x)primitivecd? String, Class, enumeratedcd, annotationcd, 和前面类型的数组?br />
下面我们定义了一个名为UncheckedExceptions 的annotationcdQ它的成员是一个扩展了RuntimeExceptioncȝcLl?br />

@UncheckedExceptions({
    IllegalArgumentException.class, StringIndexOutOfBoundsException.class
})


5。Annotation的目标:(x)

annotation通常被放在类型定义和成员定义的前面。然而它也出现在package、方法参数、本地变量的前面。下面,我们来讨Z下这些不大常用的写法Q?br />
package annotation出现在package声明的前面?br />下面的例子package-info.java中不包含M的公q型定义,却包含一个可选的javadoc注释?br />

/**
* This package holds my custom annotation types.
*/
@com.davidflanagan.annotations.Author("David Flanagan")
package com.davidflanagan.annotations;

当package-info.java文g被编译时Q它?yu)生名为包含annotationQ特D的接口Q声明的package-info.class的类。这个接口没有成员,它的名字package-info不是一个合法的java标识Q所以它不能用在java源代码中。这个接口的存在只是单的被看作一个ؓ(f)package annotation准备的占位符?br />
用于修饰Ҏ(gu)参数、catch参数、本地变量的annotation只是单的出现在这些程序成员的修饰W位|。javacL件格式没有ؓ(f)本地变量或者catch参数存储annotation作准备,所以这些annotationL保留在源代码U别Qsource retentionQ;Ҏ(gu)参数annotation能够保存在类文g中,也可以在保留到运行时?br />
最后,h意,枚Dcd定义中不允许M的修饰符修饰其枚丑ր{?br />
6。Annotation和默认|(x)
在Annotation中,没有默认值的成员必须有一个成员倹{而如何理解默认值是如何被处理就是一个很重要的细节:(x)annotationcd所定义的成员默认D存储在class文g中,不被~译到annotation里面。如果我们修改一个annotationcd使其成员的默认值发生了改变Q这个改变对于所有此cd的annotation中没有明提供成员值的成员产生影响Q即修改了该成员的成员|。即使在annotationcd使其成员的默认D改变后annotation从没被重新编译过Q该cd的annotation(改变前已l被~译?也受到媄响?br />
三、Annotation工作原理Q?/span>

Annotation与反?/span>
在java5.0中Java.lang.reflect提供的反API被扩充了dq行时annotation的能力。让我们回顾一下前面所讲的Q一个annotationcd被定义ؓ(f)runtime retention后,它才是在q行时可见,当class文g被装载时被保存在class文g中的annotation才会(x)被虚拟机d。那么reflect是如何帮助我们访问class中的annotation呢?

下文在java.lang.reflect用于annotation的新Ҏ(gu),其中java.lang.reflect.AnnotatedElement是重要的接口Q它代表了提供查询annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现Qƈ间接地被MethodcRConstructorcRjava.lang.reflect的Fieldcd现。而annotation中的Ҏ(gu)参数可以通过MethodcRConstructorcȝgetParameterAnnotations()Ҏ(gu)获得?br />
下面的代码用了AnnotatedElementcȝisAnnotationPresent()Ҏ(gu)判断某个Ҏ(gu)是否h@Unstable annotationQ从而断a此方法是否稳定:(x)

import java.lang.reflect.*;

Class c = WhizzBangClass.class;                          
Method m = c.getMethod("whizzy", int.class, int.class);  
boolean unstable = m.isAnnotationPresent(Unstable.class);

isAnnotationPresent()Ҏ(gu)对于查marker annotation是十分有用的Q因为marker annotation没有成员变量Q所以我们只要知道class的方法是否用了annotation修饰可以了。而当处理h成员的annotationӞ我们通过使用getAnnotation()Ҏ(gu)来获得annotation的成员信息(成员名称、成员|。这里我们看C一套优的java annotationpȝQ如果annotation存在Q那么实C相应的annotationcd接口的对象将被getAnnotation()Ҏ(gu)q回Q接着调用定义在annotationcd中的成员Ҏ(gu)可以方便地获得Q何成员倹{?br />
回想一下,前面介绍的@Reviews annotationQ如果这个annotationcd被声明ؓ(f)runtime retention的话Q我们通过下面的代码来讉K@Reviews annotation的成员|(x)

AnnotatedElement target = WhizzBangClass.class; //获得被查询的AnnotatedElement
// 查询AnnotatedElement的@Reviews annotation信息
Reviews annotation = target.getAnnotation(Reviews.class);
// 因ؓ(f)@Reviews annotationcd的成员ؓ(f)@Review annotationcd的数l,
// 所以下面声明了Review[] reviews保存@Reviews annotationcd的value成员倹{?br />Review[] reviews = annotation.value();
// 查询每个@Review annotation的成员信?br />for(Review r : reviews) {
    Review.Grade grade = r.grade();
    String reviewer = r.reviewer();
    String comment = r.comment();
    System.out.printf("%s assigned a grade of %s and comment '%s'%n",
                      reviewer, grade, comment);
}


四、如何自定义AnnotationQ?/span>

1Q详解annotation与接口的异同Q?/span>
因ؓ(f)annotationcd是一个非凡的接口Q所以两者之间存在着某些差异Q?br />
A.Annotationcd使用关键字@interface而不是interface?/span>
q个关键字声明隐含了一个信息:(x)它是l承了java.lang.annotation.Annotation接口Qƈ非声明了一个interface?br />
B.Annotationcd、方法定义是独特的、受限制的?/span>
Annotationcd的方法必d明ؓ(f)无参数、无异常抛出的。这些方法定义了annotation的成员:(x)Ҏ(gu)名成Z成员名,而方法返回值成Z成员的类型。而方法返回值类型必Mؓ(f)primitivecd、Classcd、枚丄型、annotationcd或者由前面cd之一作ؓ(f)元素的一l数l。方法的后面可以使用default和一个默认数值来声明成员的默认|null不能作ؓ(f)成员默认|q与我们在非annotationcd中定义方法有很大不同?br />Annotationcd和它的方法不能用annotationcd的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotationcd中用genericQ因为此Ҏ(gu)能够用类转换各U类型{换ؓ(f)Class?br />
C.Annotationcd又与接口有着q似之处?/span>
它们可以定义帔R、静态成员类型(比如枚Dcd定义Q。Annotationcd也可以如接口一般被实现或者ѝ?br />
2Q实例:(x)
下面Q我们将看到如何定义annotationcd的example。它展示了annotationcd声明以及(qing)@interface与interface之间的不同:(x)

package com.davidflanagan.annotations;
import java.lang.annotation.*;

/**
* 使用annotation来描q那些被标注的成员是不稳定的Q需要更?br />*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Unstable {}

下面的另一个example只定义了一个成员。ƈ通过这个成员命名ؓ(f)valueQ我们可以方便的用这Uannotation的快捷声明方式:(x)

/**
* 使用Authorq个annotation定义在程序中指出代码的作?br /> */
public @interface Author {
    /** q回作者名 */
    String value();
}

以下的example更加复杂。Reviews annotationcd只有一个成员,但是q个成员的类型是复杂的:(x)由Review annotationl成的数l。Review annotationcd?个成员:(x)枚Dcd成员grade、表CReview名称的字W串cd成员Reviewer、具有默认值的字符串类型成员Comment?br />

import java.lang.annotation.*;
        
/**
* Reviews annotationcd只有一个成员,
* 但是q个成员的类型是复杂的:(x)由Review annotationl成的数l?br /> */
@Retention(RetentionPolicy.RUNTIME)
public @interface Reviews {
    Review[] value();
}

/**
* Review annotationcd?个成员:(x)
* 枚Dcd成员grade?br />  * 表示Review名称的字W串cd成员Reviewer?br />  * h默认值的字符串类型成员Comment?br /> */
public @interface Review {
    // 内嵌的枚丄?br />    public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };

    // 下面的方法定义了annotation的成?br />    Grade grade();                
    String reviewer();          
    String comment() default "";  
}

最后,我们来定义一个annotationҎ(gu)用于|列出类q行中所有的unchecked异常Q上文已l提到这U情况不一定是错误Q。这个annotationcd一个数l作Z唯一的成员。数l中的每个元素都是异常类。ؓ(f)了加强对未检查的异常Q此cd帔R是在q行时抛出)q行报告Q我们可以在代码中对异常的类型进行限Ӟ(x)

public @interface UncheckedExceptions {
    Class<? extends RuntimeException>[] value();
}


五、Meta-Annotation

Annotationcd可以被它们自己所标注。Java5.0定义?个标准的meta-annotationcdQ它们被用来提供对其它annotationcd作说明。这些类型和它们所支持的类在java.lang.annotation包中可以扑ֈ。如果需要更详细的信息可以参考jdk5.0手册?br />
1Q再谈Target
作ؓ(f)meta-annotationcd的Target,它描qCannotation所修饰的程序成员的cd。当一个annotationcd没有TargetӞ它将被作为普通的annotation看待。当它修饰一个特定的E序成员Ӟ它将发挥其应用的作用Q例如:(x)Override用于修饰Ҏ(gu)Ӟ增加了@Targetq个meta-annotation׃ɾ~译器对annotation作检查,从而去掉修饰错误类型的Override?br />
Target meta-annotationcd有唯一的value作ؓ(f)成员。这个成员的cd是java.lang.annotation.ElementType[]cd的,ElementTypecd是可以被标注的程序成员的枚Dcd?br />
2QRetention的用?/span>
我们在文章的开头曾l提到过RetentionQ但是没有详l讲解。Retention描述了annotation是否被编译器丢弃或者保留在class文gQ如果保留在class文g中,是否在class文g被装载时被虚拟机d。默认情况下Qannotation被保存在class文g中,但在q行时ƈ不能被反访问。Retentionh三个取|(x)source、class、runtimeQ这些取值来自java.lang.annotation.RetentionPolicy的枚丄型倹{?br />
Retention meta-annotationcd有唯一的value作ؓ(f)成员Q它的取值来自java.lang.annotation.RetentionPolicy的枚丄型倹{?br />
3QDocumented
Documented是一个meta-annotationcdQ用于描q其它类型的annotation应该被作标注的程序成员的公共APIQ因此可以被例如javadoc此类的工h档化?br />
Documented是一个marker annotationQ没有成员?br />
4QInherited
@Inherited meta-annotation也是一个marker annotationQ它阐述了某个被标注的类型是被承的。如果一个用了@Inherited修饰的annotationcd被用于一个classQ则q个annotation被用于该class的子cR?br />
注意Q@Inherited annotationcd是被标注q的class的子cLl承。类q不从它所实现的接口承annotationQ方法ƈ不从它所重蝲的方法承annotation?br />
值得思考的是,当@Inherited annotationcd标注的annotation的Retention是RetentionPolicy.RUNTIMEQ则反射API增强了这U承性。如果我们用java.lang.reflectL询一个@Inherited annotationcd的annotationӞ反射代码查将展开工作Q检查class和其父类Q直到发现指定的annotationcd被发玎ͼ或者到辄l承l构的顶层?br />

作?cleverpig(http://blog.matrix.org.cn/page/cleverpig)


zeroone0 2006-04-20 15:30 发表评论
]]>
log4j日志理http://m.tkk7.com/zeroone0/articles/39694.htmlzeroone0zeroone0Thu, 06 Apr 2006 15:26:00 GMThttp://m.tkk7.com/zeroone0/articles/39694.htmlhttp://m.tkk7.com/zeroone0/comments/39694.htmlhttp://m.tkk7.com/zeroone0/articles/39694.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/39694.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/39694.html
Log4j的类?
LoggerQ日志写出器
Logger的输出方?
Logger的命名规?
Log level
CZ代码
关于logger的两点说?
AppenderQ日志目的地
ConsoleAppender
FileAppender
RollingFileAppender
LayoutQ日志格式化?
PatternLayout
patterns in PatternLayout
ConfigurationQ配|?
默认的log4j初始化过E?
BasicConfigurator.configure()
xml格式的log4j配置文g概述
在xml文g中配|a(chn)ppender和layout
我自q一个用xml文g配置log4j环境的很单的例子
Log4j的编码习(fn)?
参考资?

Log4j的类?

Logger - 日志写出器,供程序员输出日志信息
Appender - 日志目的圎ͼ把格式化好的日志信息输出到指定的地方?
ConsoleAppender - 目的Cؓ(f)控制台的Appender
FileAppender - 目的Cؓ(f)文g的Appender
RollingFileAppender - 目的Cؓ(f)大小受限的文件的Appender
Layout - 日志格式化器Q用来把E序员的logging request格式化成字符?
PatternLayout - 用指定的pattern格式化logging request的Layout

LoggerQ日志写出器
Logger对象是用来取代System.out或者System.err的日志写出器Q用来供E序员输出日志信息?

Logger的输出方?
Loggercd象提供一pdҎ(gu)供程序员输出日志信息?

------ Log4j APIs : class Logger ------

// Printing methods :

public void debug(Object msg);
public void debug(Object msg, Throwable t);

public void info(Object msg);
public void info(Object msg, Throwable t);

public void warn(Object msg);
public void warn(Object msg, Throwable t);

public void error(Object msg);
public void error(Object msg, Throwable t);

public void fatal(Object msg);
public void fatal(Object msg, Throwable t);

// Generic printing method :

public void log(Level l, Object msg);

Logger的命名规?
Logger׃个Stringcȝ名字识别Qlogger的名字是大小写敏感的Q且名字之间hl承的关p,子名有父名作为前~Q用点号.分隔。如Qx.y是x.y.z的父二Ӏ?

根logger (root logger)是所有logger的祖先,它具有如下属性:(x)1) 它L存在的;2) 它不可以通过名字获得?
通过调用public static Logger Logger.getRootLogger()获得root loggerQ通过调用public static Logger Logger.getLogger(String name)或者public static Logger Logger.getLogger(Class clazz)获得Q或者创建)一个named logger。后者相当于调用Logger.getLogger(clazz.getName())?

在某对象中,用该对象所属的cMؓ(f)参数Q调用Logger.getLogger(Class clazz)以获得logger被认为是目前所知的最理智的命名logger的方法?

Log level
每个logger都被分配了一个日志?(log level)Q用来控制日志信息的输出。未被分配level的logger承它最q的父logger的level?

每条输出到l(f)ogger的日志请?logging request)也都有一个levelQ如果该request的level大于{于该logger的levelQ则该request被处理Q称为enabledQ;否则该request被忽略。故可得知:(x)

logger的level低Q表Clogger详l?
logging request的level高Q表Clogging request优先输?

LevelcM预定义了五个levelQ它们的大小关系如下Q?

Level.ALL < Level.DEBUG < Level.INFO < Level.WARN < Level.ERROR < Level.FATAL < Level.OFF

CZ代码
以下代码用自己所属的cMؓ(f)参数Q创Z个loggerQ启用默认配|,讄其levelq向其输qlogging request?

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;

public class Log4jTest {
public static void main(String argv[]) {

// Create a logger by the name of class Log4jTest.

Logger logger = Logger.getLogger(Log4jTest.class);

// Use the default configuration.

BasicConfigurator.configure();

// Set the logger level to Level.INFO

logger.setLevel(Level.INFO);

// This request will be disabled since Level.DEBUG < Level.INFO.

logger.debug("This is debug.");

// These requests will be enabled.

logger.info("This is an info.");
logger.warn("This is a warning.");
logger.error("This is an error.");
logger.fatal("This is a fatal error.");

return;
}
}

关于logger的两点说?

用同名参数调用Logger.getLogger(String name)返回同一个logger的引用。故可以在一个地斚w|loggerQ在另外一个地方获得配|好的loggerQ而无ȝ互间传递logger的引用?
logger的创建可以按照Q意的序Q即Q父logger可以后于子logger被创建。log4j自动维护logger的承树(wi)?
AppenderQ日志目的地
每个logger都可以拥有一个或者多个appenderQ每个appender表示一个日志的输出目的圎ͼ比如console或者某个文件。可以用Logger.addAppender(Appender app)为logger增加一个appenderQ可以用Logger.removeAppender(Appender app)为loggerU除一个appender?

默认情况下,logger的additive标志被设|ؓ(f)trueQ表C子logger承父logger的所有appenders。该选项可以被重新设|,表示子logger不再承父logger的appenders?

root logger拥有目标为system.out的consoleAppenderQ故默认情况下,所有的logger都将l承该appender?

------ Log4j APIs : class Logger ------

// 为logger对象增加或者移除一个Appender对象 :.

public void appAppender(Appender app);
public void removeAppender(Appender app);

// 获得和设|a(chn)dditive标志Q是否承父logger的appenders :.
// 注意Q在讄a(chn)dditive标志为falseӞ必须保证已经logger讄了新的appenderQ?:.
// 否则log4j报错:(x)log4j:WARN No appenders could be found for logger (x.y.z). :.

public boolean getAdditivity();
public void setAdditivity(boolean additive);

ConsoleAppender
可以使用ConsoleAppender对象把日志输出到控制台。每个ConsoleAppender都有一个targetQ表C它的输出目的地。它可以是System.outQ标准输备(~冲昄屏)Q或者是System.errQ标准错误设备(不缓冲显C屏Q。ConsoleAppender的用方法参考如下API :.

------ Log4j APIs : class ConsoleAppender extends WriterAppender ------

// 构造方法,使用一个Layout对象构造一个ConsoleAppender对象 :.
// 默认情况下,ConsoleAppender的target是System.out :.

public ConsoleAppender(Layout layout);

// 构造方法,使用一个Layout对象和一个target字符串构造ConsoleAppender对象 :.
// target的可能取gؓ(f)ConsoleAppender.SYSTEM_OUT和ConsoleAppender.SYSTEM_ERR :.

public ConsoleAppender(Layout layout, String target);

FileAppender
可以使用FileAppender对象把日志输出到一个指定的日志文g中去。用方法可以参考如下的API :.

------ Log4j APIs : class FileAppender extends WriterAppender ------

// 构造方法,使用一个Layout对象和日志文件名构造一个FileAppender对象 :.

public FileAppender(Layout layout, String filename)
throws IOException;
public FileAppender(Layout layout, String filename, boolean append)
throws IOException;

RollingFileAppender
可以使用FileAppender的子cRollingFileAppender对象Q把日志输出C个指定的日志文g中。不同的是该日志文g的大受到限Ӟ当日志内容超出最大的寸Ӟ该文件将向上滚动Q最老的日志被擦除)。还可以在该cd象中指定为日志文件做多少个备份。具体用方法参考如下API :.

------ Log4j APIs : class RollingFileAppender extends FileAppender ------

// 构造方法,使用一个Layout对象和日志文件名构造一个RollingFileAppender对象 :.

public RollingFileAppender(Layout layout, String filename)
throws IOException;
public RollingFileAppender(Layout layout, String filename, boolean append)
throws IOException;

// 获得和设|日志备份文件的个数 :.

public int getMaxBackupIndex();
public void setMaxBackupIndex(int index);

// 获得和设|滚动日志文件的最大尺?:.

public long getMaximumFileSize();
public void setMaximumFileSize(long size);

LayoutQ日志格式化?
每个appender都和一个layout相联p;layout的Q务是格式化用L(fng)logging requestQappender的Q务是把layout格式化好的输出内定w往指定的目的地?

PatternLayout
PatternLayout是Layout的一个子c,用来使用cMC语言的printf函数中用的格式控制字符串来控制日志的输出格式。用方法参考如下API :.

------ Log4j APIs : class PatternLayout extends Layout ------

// 无参数构造方法,使用DEFAULT_CONVERSION_PATTERN构造一个PatternLayout :.
// 注意QDEFAULT_CONVERSION_PATTERN?%m%n"Q只打印消息信息 :.

public PatternLayout();

// 构造方法,使用自定义的pattern构造一个PatternLayout :.

public PatternLayout(String pattern);

// 获得和设|PatternLayout对象的日志pattern :.

public String getConversionPattern();
public void setConversionPattern(String pattern);

patterns in PatternLayout

ConfigurationQ配|?
对log4j环境的配|就是对root logger的配|,包括把root logger讄为哪个?level)Qؓ(f)它增加哪些appenderQ等{。这些可以通过讄pȝ属性的Ҏ(gu)来隐式地完成Q也可以在程序里调用XXXConfigurator.configure()Ҏ(gu)来显式地完成?

默认的log4j初始化过E?
Loggercȝ静态初始化?static initialization block)中对log4j的环境做默认的初始化。注意:(x)如果E序员已l通过讄pȝ属性的Ҏ(gu)来配|了log4j环境Q则不需要再昑ּ地调用XXXConfigurator.configure()Ҏ(gu)来配|log4j环境了?

Logger的静态初始化块在完成初始化过E时检查一pdlog4j定义的系l属性。它所做的事情如下Q?

查系l属性log4j.defaultInitOverrideQ如果该属性被讄为falseQ则执行初始化;否则Q只要不是falseQ无论是什么|甚至没有|都是否则Q,跌初始化?
把系l属性log4j.configuration的Dl变量resource。如果该pȝ变量没有被定义,则把resource赋gؓ(f)"log4j.properties"。注意:(x)在apache的log4j文档中徏议用定义log4j.configurationpȝ属性的Ҏ(gu)来设|默认的初始化文件是一个好Ҏ(gu)?
试图把resource变量转化成ؓ(f)一个URL对象url。如果一般的转化Ҏ(gu)行不通,p用org.apache.log4j.helpers.Loader.getResource(resource, Logger.class)Ҏ(gu)来完成{化?
如果url?.html"l尾Q则调用Ҏ(gu)DOMConfigurator.configure(url)来完成初始化Q否则,则调用方法PropertyConfigurator.configure(url)来完成初始化。如果url指定的资源不能被获得Q则跛_初始化过E?

BasicConfigurator.configure()
BasicConfigurator.configure()Ҏ(gu)使用最的方法配|log4j环境。注Q所谓配|log4j环境Q就是指配置root loggerQ因为所有其它的logger都是root logger的后代,所以它们(默认情况下)都将l承root logger的性质?br />
BasicConfigurator.configure()完成的Q务是Q?

用默认pattern创徏PatternLayout对象pQ?
PatternLayout p = new PatternLayout("%-4r[%t]%-5p%c%x - %m%n");
用p创徏ConsoleAppender对象aQ目标是system.outQ标准输备:(x)
ConsoleAppender a = new ConsoleAppender(p,ConsoleAppender.SYSTEM_OUT);
为root logger增加一个ConsoleAppender pQ?
rootLogger.addAppender(p);
把root logger的log level讄为DEBUGU别Q?
rootLogger.setLevel(Level.DEBUG);

xml格式的log4j配置文g概述
xml格式的log4j配置文g需要用org.apache.log4j.html.DOMConfigurator.configure()Ҏ(gu)来读入。对xml文g的语法定义可以在log4j的发布包中找刎ͼ(x)org/apache/log4j/xml/log4j.dtd?

log4j的xml配置文g的树(wi)状结?
log4j的xml配置文g的树(wi)状结构如下所C,注意下图只显CZ常用的部分?:.

xml declaration and dtd
|
log4j:configuration
|
+-- appender (name, class)
| |
| +-- param (name, value)
| +-- layout (class)
| |
| +-- param (name, value)
+-- logger (name, additivity)
| |
| +-- level (class, value)
| | |
| | +-- param (name, value)
| +-- appender-ref (ref)
+-- root
|
+-- param (name, class)
+-- level
| |
| +-- param (name, value)
+-- appender-ref (ref)


xml declaration and dtd
xml配置文g的头部包括两个部分:(x)xml声明和dtd声明。头部的格式如下Q?:.


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

log4j:configuration (root element)

xmlns:log4j [#FIXED attribute] : 定义log4j的名字空_(d)取定?http://jakarta.apache.org/log4j/"
appender [* child] : 一个appender子元素定义一个日志输出目的地
logger [* child] : 一个logger子元素定义一个日志写出器
root [? child] : root子元素定义了root logger

appender
appender元素定义一个日志输出目的地?

name [#REQUIRED attribute] : 定义appender的名字,以便被后文引?
class [#REQUIRED attribute] : 定义appender对象所属的cȝ全名
param [* child] : 创徏appender对象时传递给cL造方法的参数
layout [? child] : 该appender使用的layout对象

layout
layout元素定义与某一个appender相联pȝ日志格式化器?

class [#REQUIRED attribute] : 定义layout对象所属的cȝ全名
param [* child] : 创徏layout对象时传递给cL造方法的参数


logger
logger元素定义一个日志输出器?

name [#REQUIRED attribute] : 定义logger的名字,以便被后文引?
additivity [#ENUM attribute] : 取gؓ(f)"true"Q默认)或?false"Q是否承父logger的属?
level [? child] : 定义该logger的日志?
appender-ref [* child] : 定义该logger的输出目的地

root
root元素定义Ҏ(gu)志输出器root logger?

param [* child] : 创徏root logger对象时传递给cL造方法的参数
level [? child] : 定义root logger的日志?
appender-ref [* child] : 定义root logger的输出目的地

level
level元素定义logger对象的日志别?

class [#IMPLIED attribute] : 定义level对象所属的c,默认情况下是"org.apache.log4j.Levelc?
value [#REQUIRED attribute] : 为level对象赋倹{可能的取g到大依ơؓ(f)"all"?debug"?info"?warn"?error"?fatal"?off"。当gؓ(f)"off"时表C没有Q何日志信息被输出
param [* child] : 创徏level对象时传递给cL造方法的参数

appender-ref
appender-ref元素引用一个appender元素的名字,为logger对象增加一个appender?

ref [#REQUIRED attribute] : 一个appender元素的名字的引用
appender-ref元素没有子元?

param
param元素在创建对象时为类的构造方法提供参数。它可以成ؓ(f)appender、layout、filter、errorHandler、level、categoryFactory和root{元素的子元素?

name and value [#REQUIRED attributes] : 提供参数的一l名值对
param元素没有子元?

在xml文g中配|a(chn)ppender和layout
创徏不同的Appender对象或者不同的Layout对象要调用不同的构造方法。可以用param子元素来讑֮不同的参数倹{?

创徏ConsoleAppender对象
ConsoleAppender的构造方法不接受其它的参数?:.

... ... ... ...
<appender name="console.log" class="org.apache.log4j.ConsoleAppender">
<layout ... >
... ...
</layout>
</appender>
... ... ... ...

创徏FileAppender对象
可以为FileAppendercȝ构造方法传递两个参敎ͼ(x)File表示日志文g名;Append表示如文件已存在Q是否把日志q加到文件尾部,可能取gؓ(f)"true"?false"Q默认)?:.

... ... ... ...
<appender name="file.log" class="org.apache.log4j.FileAppender">
<param name="File" value="/tmp/log.txt" />
<param name="Append" value="false" />
<layout ... >
... ...
</layout>
</appender>
... ... ... ...

创徏RollingFileAppender对象
除了File和Append以外Q还可以为RollingFileAppendercȝ构造方法传递两个参敎ͼ(x)MaxBackupIndex备䆾日志文g的个敎ͼ默认?个)QMaxFileSize表示日志文g允许的最大字节数Q默认是10MQ?:.

... ... ... ...
<appender name="rollingFile.log" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="/tmp/rollingLog.txt" />
<param name="Append" value="false" />
<param name="MaxBackupIndex" value="2" />
<param name="MaxFileSize" value="1024" />
<layout ... >
... ...
</layout>
</appender>
... ... ... ...

创徏PatternLayout对象
可以为PatternLayoutcȝ构造方法传递参数ConversionPattern?:.

... ... ... ...
<layout class="org.apache.log4j.PatternLayout>
<param name="Conversion" value="%d [%t] %p - %m%n" />
</layout>
... ... ... ...

我自q一个用xml文g配置log4j环境的很单的例子
为WSOTA目开发java web start的胖客户端时Q用了如下的xml文g配置log4j环境Q文件名为wsota-rc.log4j.htmlQ:(x):.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

<!-- ================================================================= -->
<!-- a rolling file appender -->
<!-- ================================================================= -->

<appender name="wsota-rc.file.log" class="org.apache.log4j.RollingFileAppender">
<param name="File" value="/tmp/wsota-rc.log" />
<param name="Append" value="false" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %p - %m%n" />
</layout>
</appender>

<!-- ================================================================= -->
<!-- a console appender -->
<!-- debug can be turned off by setting level of root to "off" -->
<!-- ================================================================= -->

<appender name="wsota-rc.console.log" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %p - %m%n" />
</layout>
</appender>

<!-- use this to turn on debug to a rolling file. -->

<root>
<level value="debug" />
<appender-ref ref="wsota-rc.file.log" />
</root>

<!-- use this to turn on debug to console. -->
<!--
<root>
<level value="off" />
<appender-ref ref="wsota-rc.console.log" />
</root>
-->

<!-- use this to turn off debug. -->
<!--
<root>
<level value="off" />
<appender-ref ref="wsota-rc.console.log" />
</root>
-->

</log4j:configuration>


在胖客户E序中用了如下代码来用外部xml文g配置log4j环境Q注意该代码D位于程序的main class的静态初始化块中Q含有以下代码的cdxml配置文g在同一个目录下Q?.


import org.apache.log4j.html.DOMConfigurator;

public class SapFrame extends JFrame {
static {
DOMConfigurator.configure(SapFrame.class.getResource("wsota-rc.log4j.html"));
}
... ... ... ...
}

Log4j的编码习(fn)?

让每个类都拥有一个private static的Logger对象Q用来输cM的全部日志信?
使用xml文g来完成对log4j环境的配|。在目的main class中的静态初始化块里放log4j环境的配|代码。注意:(x)在一个项目中Qlog4j环境只需要被配置一ơ,而不是在每个使用了logger的类里都需要调用一?
用MyClass.class作ؓ(f)参数创徏该类的静态Logger对象
补充?..

from: http://zooo.51.net/heavyz_cs/notebook/log4j.html

zeroone0 2006-04-06 23:26 发表评论
]]>
Log4j明手?/title><link>http://m.tkk7.com/zeroone0/articles/39692.html</link><dc:creator>zeroone0</dc:creator><author>zeroone0</author><pubDate>Thu, 06 Apr 2006 15:18:00 GMT</pubDate><guid>http://m.tkk7.com/zeroone0/articles/39692.html</guid><wfw:comment>http://m.tkk7.com/zeroone0/comments/39692.html</wfw:comment><comments>http://m.tkk7.com/zeroone0/articles/39692.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/zeroone0/comments/commentRss/39692.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/zeroone0/services/trackbacks/39692.html</trackback:ping><description><![CDATA[Log4j明手?br /><br />1. 概述<br />本文主要描述Log4j的API的唯一Ҏ(gu)和它的设计原理。Log4j是一个基于许多作者的开放源码的目。它允许开发员以Q意的间隔来控制日志的输出。它通过讑֜外部的配|文件而达到运行时灉|的设|。最重要的是QLog4j有一个^E的学习(fn)曲线。注意:(x)Ҏ(gu)来自用户的反馈判断,它很Ҏ(gu)使h上瘾?br /><br />2. D<br /><br />几乎所有的大型应用E序都包括它的自q日志和跟tAPI。顺应这个规则,E.U. SEMPER 目军_写它自己的跟tPAI。这?996q初。在无数ơ加强,几次变Ş和许多工作后Q那个API变成了如今的Log4jQ一个流行的java日志包。这个包以Apache Software License协议发布Q一个成熟的开放源吗协议。最新的Log4j版本Q包括全部的源码Qclass文g和文档,你可以在http://jakarta.apache.org/Log4j/上找到。顺便,Log4j已经lC(j), C++, C#, Python, Ruby, and Eiffel 语言都提供了接口?br /><br />Z调试而插入日志输出到代码里是一个低技术成分的Ҏ(gu)Q但它可能也是唯一的方法,因ؓ(f)调试器ƈ不是一直可用或者可以适应的,其对于多线E的分布使式的大型程序而言?br /><br />l验指出调试是Y件开发周期中一个重要的l成部分?br /><br />Log4j拥有几个优点Q?br /><br />首先Q它提供关于q行E序的准的环境。一旦代码被插入Q不需要h工干预就可以产生调试信息?br /><br />其次Q日志输出可以被有计划的保存在永久媒体中以便日后研究?br /><br />另外Q除了在开发周期中Q一个充分详的日志包可以被用来作ؓ(f)以后的统计工兗?br /><br />Log4j当然q有它的~点Q它可能减慢E序。如果太详细Q它可能D屏幕盲目滚动。排除这些情况,Log4j是可靠的Q快速的Q可以扩展的。因为日志很是一个应用程序的主要目的, 设计者们正努力得Log4j API学习(fn)和用简单化?br /><br />3. 日志cd、输出源和布局<br /><br />Log4j有三个主要的lgQ日志类别(LoggersQ、输出源Q?AppendersQ和布局QLayoutsQ。这三种cd的组件一起工作得开发员可以Ҏ(gu)信息的类型和U别记录它们Qƈ且在q行时控制这些信息的输出格式和位|?br /><br />3.1 日志cd的层ơ结?Loggers)<br /><br />Log4j首要的相对于单的使用System.out.println()Ҏ(gu)的优Ҏ(gu)Z它的在禁止一些特定的信息输出的同时不妨碍其它信息的输出的能力。这个能力源自于日志命名I间Q也是_(d)所有日志声明的I间Q它Ҏ(gu)一些开发员选择的公式而分cR以前的观察引导我们选择cd作ؓ(f)包的中心概念。然而,自从Log4j?.2版本QLoggerc被CatalogcL取代Q对于那些熟(zhn)Log4j以前版本的h来说QLoggercd以被惌成仅仅是Category cȝ别名?br /><br />Loggers 被指定ؓ(f)实体QLogger的名字是大小写敏感的Q它们遵循以下的命名<br /><br />规则Q?br /><br />2 命名l承<br /><br />如果cd的名Uͼ后面加一个点Q是其子cd名称的前~Q则它就是另一个类别的辈?br /><br />如果一个类?Logger)和它的子cd之间没有其它的承关p,我们q之ؓ(f)parent与child的关pR?br /><br />例如Q类?com.foo"是类?com.foo.Bar"的parent。相似的Q?java"?java.util"的parentQ是"java.util.Vector"的父辈?q个命名规则应该被大多数的开发员所熟?zhn)?br /><br />?root) cd位于loggerl承l构的最上层。它有两U例外:(x)<br /><br />1.它一直存?br /><br />2.它不能根据名U而获得?<br /><br />调用cȝ静态方法Logger.getRootLogger可以得到它。其它所有的Logger可以通过静态方法Logger.getLogger而得到它们自q实例。这个方法取希望的Logger名作为参数。Logger的一些基本的Ҏ(gu)CZ如下Q?br /><br />package org.apache.Log4j;<br /><br />public Logger class {<br /><br />// Creation & retrieval methods:<br /><br />public static Logger getRootLogger();<br /><br />public static Logger getLogger(String name);<br /><br />// printing methods:<br /><br />public void debug(Object message);<br /><br />public void info(Object message);<br /><br />public void warn(Object message);<br /><br />public void error(Object message);<br /><br />// generic printing method:<br /><br />public void log(Level l, Object message);<br /><br />}<br /><br />Loggers可以被分配的U别。所有别的集合包括Q?br /><br />DEBUG<br /><br />INFO<br /><br />WARN<br /><br />ERROR<br /><br />FATAL<br /><br />它们被定义于org.apache.Log4j.Level cR虽然我们不鼓励Q但是你们可以通过l承LevelcL定义你们自己的别。我们随后将介绍一个比较好的方法?br /><br />如果一个Logger没有被分配一个别,那么它将从一个被分配了别的最接近它的ancestor哪里l承?br /><br />正规的说Q?br /><br />2 U别l承<br /><br />对于一个给定的Logger CQ它的承的U别{于从C开始上溯到的第一个拥有非I别的Logger的别?br /><br />Z保证所有的Logger最l能够承到一个别,根Logger通常有一个已l定义了的别?br /><br />以下四个表中的数据演CZҎ(gu)以上规则得到的结果?br /><br />cd?br />分配的?br />l承的?br /><br />root<br />Proot<br />Proot<br /><br />X <br />none<br />Proot<br /><br />X.Y <br />none<br />Proot<br /><br />X.Y.Z<br />none<br />Proot<br /><br />Example 1<br /><br /><br /><br />在例?中,只有根Logger定义了一个别,它的U别的?-"Proot"被所有其它的Loggers X, X.Y, 和X.Y.Z所l承?br /><br />cd?br />分配的?br />l承的?br /><br />root<br />Proot<br />Proot<br /><br />X <br />Px<br />Px<br /><br />X.Y <br />Pxy<br />Pxy<br /><br />X.Y.Z<br />Pxyz<br />Pxyz<br /><br />Example 2<br /><br /><br /><br />在例?中,所有的Logger都有一个被分配的别|所以它们不需要别ѝ?br /><br />cd?br />分配的?br />l承的?br /><br />root<br />Proot<br />Proot<br /><br />X <br />Px<br />Px<br /><br />X.Y <br />none<br />Px<br /><br />X.Y.Z<br />Pxyz<br />Pxyz<br /><br />Example 3<br /><br /><br /><br />在例?中,根LoggerQ以?qing)X和X.Y.Z被分别分配了U别ProotQPx和Pxyz。Logger X.Y从它的parent Xl承了别值Px?br /><br />cd?br />分配的?br />l承的?br /><br />root<br />Proot<br />Proot<br /><br />X <br />Px<br />Px<br /><br />X.Y <br />none<br />Px<br /><br />X.Y.Z<br />none<br />Px<br /><br />Example 4<br /><br /><br /><br />在例?中,根Logger和X被分别分配了U别"Proot"?Px"QLogger X.Y ?X.Y.Z从被分配了别的最接近它们的ancestor X那里得到l承?br /><br />我们需要通过调用Logger的输出的实例Ҏ(gu)之一来实现日志请求。这些输出的Ҏ(gu)是debug, info, warn, error, fatal ?log.<br /><br />通过定义输出Ҏ(gu)来区分日志的h的别。例如,如果c是一个Logger的实例,那么声明 c.info 是一个INFOU别的日志请求?br /><br />如果一个日志的h的别高于或{于日志的别那么它?yu)p被启用。反之,被用。一个没有被安排U别的Logger从它的父辈中得到ѝ这个规则ȝ如下?br /><br />2 基本的选择规则<br /><br />假如在一个别ؓ(f)q的Logger中发生一个别ؓ(f)p的日志请求,如果p>=q,那么h被启用?br /><br />q是Log4j的核心原则。它假设U别是有序的。对于标准别,我们定义DEBUG < INFO < WARN < ERROR < FATAL. <br /><br />以下是关于这条规则的一个例子?br /><br />// get a logger instance named "com.foo"<br /><br />Logger logger = Logger.getLogger("com.foo");<br /><br />// Now set its level. Normally you do not need to set the <br /><br />// level of a logger progamitcally. This is usually done <br /><br />// in configuration files.<br /><br />cat.setLevel(Level.INFO);<br /><br />Logger barlogger = Logger.getLogger("com.foo.Bar");<br /><br />// This request is enabled, because WARN >= INFO.<br /><br />logger.warn("Low fuel level.");<br /><br />// This request is disabled, because DEBUG < INFO.<br /><br />logger.debug("Starting search for nearest gas station."); <br /><br />// The logger instance barlogger, named "com.foo.Bar",<br /><br />// will inherit its level from the logger named <br /><br />// "com.foo" Thus, the following request is enabled <br /><br />// because INFO >= INFO. <br /><br />barlogger.info("Located nearest gas station."); <br /><br />// This request is disabled, because DEBUG < INFO.<br /><br />barlogger.debug("Exiting gas station search"); <br /><br />调用getLoggerҎ(gu)返回一个同名的Logger对象的实例?br /><br />例如Q?br /><br />Categoty x = Logger.getLogger("wombat");<br /><br />Categoty y = Logger.getLogger("wombat");<br /><br />x和y参照的是同一个Logger对象?br /><br />q样我们可以先定义一个LoggerQ然后在代码的其它地方不需传参可以重新得到我们已l定义了的Logger的实?<br /><br />同基本的生物学理?-父先于子相反QLog4j 的loggers可以以Q何顺序创造和配置。特别是Q一个后实例化的"parent"logger能够扑ֈq且q接它的子logger?br /><br />配置Log4j的环境通常在一个应用程序被初始化的时候进行,最好的Ҏ(gu)是通过M个配|文件。这个方法我们将短介l?br /><br />Log4j使得通过软glg命名logger很容易。我们可以通过Logger的静态的初始化方法在每一个类里定义一个loggerQo(h)logger的名字等于类名的全局名,而实现l(f)ogger的命名。这是一个实效的单的定义一个logger的方法。因为日志输出带有生日志的cȝ名字Q这个命名策略得我们更Ҏ(gu)定位C个日志信息的来源。虽然普通,但却是命名logger的常用策略之一。Log4j没有限制定义logger的可能。开发员可以自由的按照它们的意愿定义logger的名U?br /><br />然而,以类的所在位|来命名Logger好象是目前已知的最好方法?br /><br />3.2 输出源(AppendersQ和布局QLayoutsQ?br /><br />有选择的能用或者禁用日志请求仅仅是Log4j的一部分功能。Log4j允许日志h被输出到多个输出源。用Log4j的话_(d)一个输出源被称做一个Appender. 。Appender包括consoleQ控制台Q? filesQ文Ӟ, GUI componentsQ图形的lgQ? remote socket serversQsocket 服务Q? JMSQjava信息服务Q? NT Event LoggersQNT的事件日志), and remote UNIX Syslog daemonsQ远EUNIX的后台日志服务)。它也可以做到异步记录?br /><br />一个logger可以讄过一个的appender?br /><br />用addAppender Ҏ(gu)d一个appenderC个给定的logger。对于一个给定的logger它每个生效的日志h都被转发到该logger所有的appender上和该logger的父辈logger的appender上。换句话_(d)appender自动从它的父辈获得ѝD例来_(d)如果一个根logger拥有一个console appenderQ那么所有生效的日志h臛_?x)被输出到console上。如果一个名为C的logger有一个filecd的appenderQ那么它?yu)׃?x)对它自己以及(qing)所有它的子logger生效。我们也可以通过讄a(chn)ppender的additivity flag 为falseQ来重蝲appender的默认行为,以便l承的属性不在生效?br /><br />调节输出源(appenderQ添加性的规则如下?br /><br />输出源的可添加性(Appender Additivity Q?br /><br />一个名为C的logger的日志定义的输出gl到它自w以?qing)它的ancestor logger的appenders。这是术语"appender additivity"的含义?<br /><br />然而,logger C的一个ancestor logger PQ它的附加标志被设ؓ(f)falseQ那么C的输出将被定位到所有C的appenderQ以?qing)从它开始上溯到P的所有ancestor logger的appender?br /><br />Loggers的附加标讎ͼadditivity flagQ默认ؓ(f)true?br /><br />下表是一个例子?br /><br />Logger<br />Name <br />Added<br />Appenders <br />Additivity<br />Flag <br />Output Targets <br />Comment <br /><br />root <br />A1 <br />not applicable <br />A1 <br />The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root. <br /><br />x <br />A-x1, A-x2 <br />true <br />A1, A-x1, A-x2 <br />Appenders of "x" and root. <br /><br />x.y <br />none <br />true <br />A1, A-x1, A-x2 <br />Appenders of "x" and root. <br /><br />x.y.z <br />A-xyz1 <br />true <br />A1, A-x1, A-x2, A-xyz1 <br />Appenders in "x.y.z", "x" and root. <br /><br />security <br />A-sec <br />false <br />A-sec <br />No appender accumulation since the additivity flag is set to false. <br /><br />security.access <br />none <br />true <br />A-sec <br />Only appenders of "security" because the additivity flag in "security" is set to false. <br /><br /><br />l常Q用户希望自定义不但输出源,而且定义输出格式。这个是通过在一个appender上附加一个layout来完成的。layout是负责根据用L(fng)希望来格式化日志h。而appender是负责发送格式化的输出到它的目的地。PatternLayoutQ作为Log4j标准版中的一部分Q让用户指以cMC语言的printfҎ(gu)的格式来指定日志的输出格式?br /><br />例如Q{化模式ؓ(f)"%r [%t] %-5p %c - %m%n" 的PatternLayout 输出类似如下的信息Q?br /><br />176 [main] INFO org.foo.Bar - Located nearest gas station.<br /><br />W一个栏位是自从E序开始后消逝的毫秒数?br /><br />W二个栏位是做出日志的线E?br /><br />W三个栏位是log的别?br /><br />W四个栏位是日志h相关的logger的名字。?-"后的文字是信息的表述?br /><br />Log4j根据用户定义的公式来修饰日志信息的内容。例如,如果你经帔R要记录OrangesQ一个在你当前的目被用到的对象cdQ那么你可以注册一个OrangeRenderer Q它?yu)在一个orange需要被记录时被调用?br /><br />对象渲染cM的类的结构ѝ例如,假设oranges是fruitsQ如果你注册了一个FruitRendererQ所有的水果包括oranges被FruitRenderer所渲染。除非你注册了一个orange?br /><br />对象渲染必须实现ObjectRenderer接口?br /><br />4. 配置<br /><br />插入日志h到应用程序的代码中需要大量的预先计划和最l努力。观察显C大U?%的代码是用来输出的?br /><br />因此Q大适度的程序都被嵌入有成千个日志输句。ؓ(f)了以无需手工的方式管理这些日志的输出状态,l日志输Z~号和规范变得势在必行?<br /><br />Log4j在程序中有充分的可配|性。然而,用配|文仉|Log4jh更大的弹性。目前,它的配置文g支持xml和java propertiesQkey=valueQ文件两U格式?br /><br />让我们以一个例子来演示它是如何做的。假定有一个用了Log4j的程序MyApp?br /><br /><br />import com.foo.Bar;<br /><br />// Import Log4j classes.<br /><br />import org.apache.Log4j.Logger;<br /><br />import org.apache.Log4j.BasicConfigurator;<br /><br />public class MyApp {<br /><br />// Define a static logger variable so that it references the<br /><br />// Logger instance named "MyApp".<br /><br />static Logger logger = Logger.getLogger(MyApp.class);<br /><br />public static void main(String[] args) {<br /><br />// Set up a simple configuration that logs on the console.<br /><br />BasicConfigurator.configure();<br /><br />logger.info("Entering application.");<br /><br />Bar bar = new Bar();<br /><br />bar.doIt();<br /><br />logger.info("Exiting application.");<br /><br />}<br /><br />}<br /><br />MyApp以引入Log4j的相关类开始,接着它定义了一个静态logger变量Qƈl予gؓ(f)"MyApp"cȝ全\径名U?br /><br />MYApp用了定义在包com.foo中的cBar.<br /><br />package com.foo;<br /><br />import org.apache.Log4j.Logger;<br /><br />public class Bar {<br /><br />static Logger logger = Logger.getLogger(Bar.class);<br /><br />public void doIt() {<br /><br />logger.debug("Did it again!");<br /><br />}<br /><br />}<br /><br />调用BasicConfigurator.configure()Ҏ(gu)创徏了一个相当简单的Log4j的设|。它加入一<br /><br />个ConsoleAppender到根logger。输出将被采用了"%-4r [%t] %-5p %c %x - %m%n"模式<br /><br />的PatternLayout所格式化?br /><br />注意Q根logger默认被分配了Level.DEBUG的别?br /><br />MyApp的输Zؓ(f)Q?br /><br />0 [main] INFO MyApp - Entering application.<br /><br />36 [main] DEBUG com.foo.Bar - Did it again!<br /><br />51 [main] INFO MyApp - Exiting application.<br /><br />随后的图形描qC在调用BasicConfigurator.configure()Ҏ(gu)后MyApp的对象图?br /><br /><br /><br /><br /><br />一边要提醒的是QLog4j的子logger只连接到已经存在的它们的父代。特别的是,名ؓ(f)<br /><br />com.foo.bar的logger是直接连接到根loggerQ而不是围l着没用的com或com.foo<br /><br />logger。这显著的提高了E序性能q且减少的内存占用?br /><br />MyAppc配|Log4j是通过调用BasicConfigurator.configure Ҏ(gu)。其它的cM?br /><br />需要引入org.apache.Log4j.Logger c,扑ֈ它们希望用的loggerQƈ且用它就行?br /><br />以前的例子通常输出同样的日志信息。幸q的是,修改MyApp是容易的Q以便日志输<br /><br />出可以在q行时刻被控制。这里是一个小修改的版本?br /><br /><br /><br />import com.foo.Bar;<br /><br />import org.apache.Log4j.Logger;<br /><br />import org.apache.Log4j.PropertyConfigurator;<br /><br />public class MyApp {<br /><br />static Logger logger = Logger.getLogger(MyApp.class.getName());<br /><br />public static void main(String[] args) {<br /><br />// BasicConfigurator replaced with PropertyConfigurator.<br /><br />PropertyConfigurator.configure(args[0]);<br /><br />logger.info("Entering application.");<br /><br />Bar bar = new Bar();<br /><br />bar.doIt();<br /><br />logger.info("Exiting application.");<br /><br />}<br /><br />}<br /><br />修改后的 MyApp通知E序调用PropertyConfigurator()Ҏ(gu)解析一个配|文Ӟq且?br /><br />据这个配|文件来讄日志?br /><br />q里是一个配|文件的例子Q它?yu)生同以前BasicConfigurator 基本例子一?br /><br />的输出结果?br /><br /># Set root logger level to DEBUG and its only appender to A1.<br /><br />Log4j.rootLogger=DEBUG, A1<br /><br /># A1 is set to be a ConsoleAppender.<br /><br />Log4j.appender.A1=org.apache.Log4j.ConsoleAppender<br /><br /># A1 uses PatternLayout.<br /><br />Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout<br /><br />Log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n<br /><br />假设我们不在对com.foo包的Mcȝ输出感兴的话,随后的配|文件向我们展示<br /><br />了实现这个目的的Ҏ(gu)之一?br /><br />Log4j.rootLogger=DEBUG, A1<br /><br />Log4j.appender.A1=org.apache.Log4j.ConsoleAppender<br /><br />Log4j.appender.A1.layout=org.apache.Log4j.PatternLayout<br /><br /># Print the date in ISO 8601 format<br /><br />Log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n<br /><br /># Print only messages of level WARN or above in the package com.foo.<br /><br />Log4j.logger.com.foo=WARN<br /><br />以这个配|文仉|好的MyApp输出如下:(x)<br /><br />2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application.<br /><br />2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.<br /><br />当logger com.foo.bar没有被分配一个别,它将从com.fool承Q在配置文g?br /><br />它被讄了WARN的别。在Bar.doItҎ(gu)中定义的log为DEBUGU别Q低于WARNQ?br /><br />因此doIt() Ҏ(gu)的日志请求被用?br /><br />q里是另外一个配|文Ӟ它用了多个appenders.<br /><br />Log4j.rootLogger=debug, stdout, R<br /><br />Log4j.appender.stdout=org.apache.Log4j.ConsoleAppender<br /><br />Log4j.appender.stdout.layout=org.apache.Log4j.PatternLayout<br /><br /># Pattern to output the caller´s file name and line number.<br /><br />Log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n<br /><br />Log4j.appender.R=org.apache.Log4j.RollingFileAppender<br /><br />Log4j.appender.R.File=example.log<br /><br />Log4j.appender.R.MaxFileSize=100KB<br /><br /># Keep one backup file<br /><br />Log4j.appender.R.MaxBackupIndex=1<br /><br />Log4j.appender.R.layout=org.apache.Log4j.PatternLayout<br /><br />Log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n<br /><br />以这个配|文件调用加Z的MyAppcd输出如下信息.<br /><br />INFO [main] (MyApp2.java:12) - Entering application.<br /><br />DEBUG [main] (Bar.java:8) - Doing it again!<br /><br />INFO [main] (MyApp2.java:15) - Exiting application.<br /><br />另外,因ؓ(f)根logger有被分配W二个appender,所以输Z被定向到example.log文g?br /><br />q个文g大小辑ֈ100kb时将自动备䆾。备份时老版本的example.log文g自动被移?br /><br />文gexample.log.1中?br /><br />注意我们不需要重新编译代码就可以获得q些不同的日志行为。我们一样可以容?br /><br />的日志输出到UNIX Syslog daemon, 重定向所有的com.foo到NT Event logger,<br /><br />或者{发日志到一个远E的Log4j服务?它根据本地server的策略来q行日志输出。例<br /><br />如{发日志事件到W二个Log4j服务?<br /><br /><br /><br />5. 默认的初始化q程<br />Log4jcd不对它的环境做Q何假设。特别是没有默认的Log4j appender。在一些特?br /><br />的有着良好定义的环境下Qlogger的静态inializer尝试自动的配置Log4j?br /><br />java语言的特性保证类的静态initializer当且仅当装蝲cd内存之时只会(x)被调用一ơ?br /><br />要记住的重要一Ҏ(gu)Q不同的c装载器可能装蝲同一个类的完全不同的拯?br /><br />q些同样cȝ拯被虚拟机认ؓ(f)是完全不相干的?br /><br />默认的initialization是非常有用的Q特别是在一些应用程序所依靠的运行环境被准确?br /><br />定位的情况下。例如,同一L(fng)应用E序可以被用做一个标准的应用E序Q或一?br /><br />appletQ或一个在web-server控制下的servlet?br /><br />准确的默认的initialization原理被定义如下:(x)<br /><br />1.讄pȝ属性Log4j.defaultInitOverride?false"以外的其它|那么Log4j?br /><br />跌默认的initializationq程?br /><br />2.讄资源变量字符串给pȝ属性Log4j.configuration。定义默认initialization<br /><br />文g的最好的Ҏ(gu)是通过pȝ属性Log4j.configuration。万一pȝ属?br /><br />Log4j.configuration没有被定义,那么讄字符串变量resource l它的默认?br /><br />Log4j.properties?br /><br />3.试转换resource 变量Z个URL?br /><br />4.如果变量resource的g能被转换Z个URLQ例如由于MalformedURLExceptionq?br /><br />例,那么通过调用<br /><br />org.apache.Log4j.helpers.Loader.getResource(resource, Logger.class) Ҏ(gu)?br /><br />classpath中搜索resourceQ它?yu)返回一个URLQƈ通知"Log4j.properties"的值是一个错<br /><br />误的URL?br /><br />看See Loader.getResource(java.lang.String) 查看搜烦位置的列表?br /><br />5.如果没有URL被发玎ͼ那么攑ּ默认的initialization。否则用URL配置Log4j?br /><br />PropertyConfigurator用来解析URLQ配|Log4jQ除非URL?.xml"为结?br /><br />在这U情况下的话DOMConfigurator被调用。你可以有机?x)定义一个自定义?br /><br />configurator?br /><br />pȝ属性Log4j.configuratorClass 的值取自你的自定义的类名的全\径?br /><br />你自定义的configurator必须实现configurator接口?br /><br /><br /><br />6. 配置范例<br />6.1 Tomcat下的初始?br />默认的Log4j initialization典型的应用是在web-server 环境下。在tomcat3.x和tomcat4.x<br /><br />下,你应该将配置文gLog4j.properties攑֜你的web应用E序的WEB-INF/classes 目录<br /><br />下?br /><br />Log4j发现属性文Ӟq且以此初始化。这是它工作的最Ҏ(gu)的方法?br /><br />你也可以选择在运行tomcat前设|系l属性Log4j.configuration 。对于tomcat 3.xQ?br /><br />TOMCAT_OPTS pȝ变量是用来设|命令行的选项。对于tomcat4.0Q用pȝ环境?br /><br />量CATALINA_OPTS 代替了TOMCAT_OPTS?br /><br />Example 1 <br /><br />UNIX 命o(h)?br /><br />export TOMCAT_OPTS="-DLog4j.configuration=foobar.txt"<br /><br />告诉Log4j用文件foobar.txt作ؓ(f)默认的配|文件。这个文件应该放在WEB-INF/classes <br /><br />目录下。这个文件将被PropertyConfigurator所诅R每个web-application用不同的默?br /><br />配置文gQ因为每个文件是和它的web-application 相关的?br /><br />Example 2 <br /><br />UNIX 命o(h)?br /><br />export TOMCAT_OPTS="-DLog4j.debug -DLog4j.configuration=foobar.xml"<br /><br />告诉Log4j输出Log4j-internal的调试信息,q且用foobar.xml作ؓ(f)默认的配|文件?br /><br />q个文g应该攑֜你的web-application的WEB-INF/classes 目录下。因为有.xml?br /><br />扩展名,它将被DOMConfigurator所诅R每个web-application用不同的默?br /><br />配置文g。因为每个文仉和它所在的web-application 相关的?br /><br />Example 3 <br /><br />UNIX 命o(h)?br /><br />set TOMCAT_OPTS=-DLog4j.configuration=foobar.lcf -DLog4j.configuratorClass=com.foo.BarConfigurator<br /><br />告诉Log4j用文件foobar.lcf作ؓ(f)默认的配|文件。这个文件应该放在你?br /><br />web-application的WEB-INF/classes 目录下。因为定义了Log4j.configuratorClass pȝ?br /><br />性,文g用自定义的com.foo.barconfiguratorcL解析。每个web-application用?br /><br />同的默认配置文g。因为每个文仉和它所在的web-application 相关的?br /><br />Example 4 <br /><br />UNIX 命o(h)?br /><br />set TOMCAT_OPTS=-DLog4j.configuration=file:/c:/foobar.lcf<br /><br />告诉Log4j用文件foobar.lcf作ؓ(f)默认的配|文件。这个配|文件用URL file:/c:/foobar.lcf<br /><br />定义了全路径名。这样同L(fng)配置文g被所有的web-application所用?br /><br />不同的web-application通过它们自己的类装蝲器来装蝲Log4j。这P每个Log4j的环<br /><br />境将独立的运作,而没有Q何的怺同步。例如:(x)在多个web-application中定义了<br /><br />完全相同的输出源的FileAppenders尝试写同样的文件。结果好象是~Z安全性的?br /><br />你必ȝ保每个不同的web-application的Log4j配置没有用到同样的系l资源?br /><br /><br /><br />6.2 Servlet 的初始化<br />用一个特别的servlet来做Log4j的初始化也是可以的。如下是一个例子:(x)<br /><br />package com.foo;<br /><br />import org.apache.Log4j.PropertyConfigurator;<br /><br />import javax.servlet.http.HttpServlet;<br /><br />import javax.servlet.http.HttpServletRequest;<br /><br />import javax.servlet.http.HttpServletResponse;<br /><br />import java.io.PrintWriter;<br /><br />import java.io.IOException;<br /><br />public class Log4jInit extends HttpServlet {<br /><br />public void init() {<br /><br />String prefix = getServletContext().getRealPath("/");<br /><br />String file = getInitParameter("Log4j-init-file");<br /><br />// if the Log4j-init-file is not set, then no point in trying<br /><br />if(file != null) {<br /><br />PropertyConfigurator.configure(prefix+file);<br /><br />}<br /><br />}<br /><br />public void doGet(HttpServletRequest req, HttpServletResponse res) {<br /><br />}<br /><br />}<br /><br />在web.xml中定义随后的servletZ的web-application?br /><br /><servlet><br /><br /><servlet-name>Log4j-init</servlet-name><br /><br /><servlet-class>com.foo.Log4jInit</servlet-class><br /><br /><init-param><br /><br /><param-name>Log4j-init-file</param-name><br /><br /><param-value>WEB-INF/classes/Log4j.lcf</param-value><br /><br /></init-param><br /><br /><load-on-startup>1</load-on-startup><br /><br /></servlet><br /><br />写一个初始化的servlet是最有弹性的初始化Log4j的方法。代码中没有M限制Q你?br /><br />以在servlet的initҎ(gu)中定义它?br /><br />7. Nested Diagnostic Contexts<br /><br />在现实世界中的系l经怸得不同时处理多个客户端请求。在q样的一个典型的多线E的pȝ中,不同的线E将处理不同的客L(fng)。Logging特别能够适应q种复杂的分布式的应用程序的调试和跟t。一个常见的区分每个客户端所输出的Logging的方法是为每个客L(fng)实例化一个新的独立的Logger。这DLogger的大量生,理的成本也过了logging本n?<br /><br />唯一标识每个logh是一个轻量的技术。Neil Harrison 在名为“Patterns for Logging Diagnostic Messages”的书中描述了这个方法in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997). <br /><br />Z唯一标识每个hQ用h上下文信息推入NDC(Nested Diagnostic Context)中?br /><br />NDCcȝ例如下:(x)<br /><br />public class NDC {<br /><br />// Used when printing the diagnostic<br /><br />public static String get();<br /><br />// Remove the top of the context from the NDC.<br /><br />public static String pop();<br /><br />// Add diagnostic context for the current thread.<br /><br />public static void push(String message);<br /><br />// Remove the diagnostic context for this thread.<br /><br />public static void remove();<br /><br />}<br /><br />NDC如同一个堆栈样理每个U程。注意所有the org.apache.log4j.NDC cȝҎ(gu)都是静态的。假设NDC输出被开启,每次一个log h被生成时Q适当的log4jlg为将要输出log的线E包含完整的NDC堆栈。这是在没有用户的干预的情况下做到的Q用户只负责在NDC中定位正的信息Q通过在代码中正确位置插入很少的push和popҎ(gu)p了。相反的Q在代码中per-client实现Ҏ(gu)有着很大变化?br /><br />Z演示q个点,让我们以一个发送内容到匿名客户端的servletZ。这个servlet可以在开始执行每个其他代码前的初始化时徏立NDC。上下文信息可以是客户主机的名字和其他的h中固有的信息?br /><br />典型的信息包括cookies。因此,即servlet同时为多个客户同时提供服务,log 被同L(fng)代码初始化,例如属于同一个loggerQ依然可以被区别Q因为每个客戯求将有不同的NDC堆栈。与之相比,Contrast this with the complexity of passing a freshly instantiated logger to all code exercised during the client´s request?br /><br />不过Q一些诡异的E序Q例如虚拟主机的web server记录日志Q不是一般的依靠虚拟L的上下文Q还要依靠Y件的lg发出h。近来log4j的发布版本支持多层的?wi)Şl构。这个增强允许每个虚拟主机可以处理在?wi)型l构中属于它自己的logger?br /><br />8. 优化<br />一个经常引用的依靠于logging的参数是可以计算的花贏V这是一个合理的概念Q一个适度的应用程序可能生成千上万个日志h。许多努力花在测量和调试logging的优化上。Log4j要求快速和Ҏ(gu):(x)速度最重要Q弹性是其次?br /><br />用户应该注意随后的优化徏议?br /><br />1.日志为禁用时Q日志的优化?br /><br />当日志被d的关闭,一个日志请求的p{于一个方法的调用加上整数的比较时间?br /><br />?33mhz的Pentium II 机器上这个花贚w常?-50U秒之间?br /><br />然而,Ҏ(gu)调用包括参数构徏的隐藏花贏V?br /><br />例如Q对于logger catQ?br /><br />logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));<br /><br />引v了构Z息参数的pQ例如,转化整数i和entry[i]C个stringQƈ且连接中间字W串Q不信息是否被输出。这个参数的构徏p可能是很高,它主要决定于被调用的参数的大?br /><br />避免参数构徏的花费应如下Q?br /><br />if(logger.isDebugEnabled() {<br /><br />logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));<br /><br />}<br /><br />如果logger的debug被关闭这不?x)招致参数构建的p。另一斚wQ如果logger是debug的话Q它?yu)生两ơ判?logger是否能用的花贏V一ơ是在debugenabledQ一ơ是debug。这是无关紧要的Q因为判断日志的能用 只占日志实际p旉的约1%?br /><br />在Log4j里,日志h在Logger cȝ实例里。Logger 是一个类Q而不是一个接口。这大量的减了在方法调用上的弹性化的花贏V?br /><br />当然用户采用预处理或~译旉技术去~译出所有的日志声明。这导致完的执行成效。然而因Zq制应用E序不包括Q何的日志声明的结果,日志不可能对那个二进制程序开启。以我的观点Q以q种较大的代h换取较小的性能优化是不值得的?br /><br />2。当日志状态ؓ(f)启用Ӟ日志的优化?br /><br />q是本质上的优化logger的层ơ。当日志状态ؓ(f)开QLog4j依然需要比较请求的U别与logger的别。然而, logger可能没有被安排一个别;它们从它们的fatherl承。这P在承之前,logger可能需要搜索它的ancestor?br /><br />q里有一个认真的努力使层ơ的搜烦可能的快。例如,子logger仅仅q接到它的存在的father logger?br /><br />在先前展C的BasicConfigurator 例子中,名ؓ(f)com.foo.bar 的logger是连接到跟根loggerQ因此绕q?了不存在的logger com和com.foo。这显著的改善执行的速度Q特别是解析logger的层l构时?br /><br />典型的层ơ结构的解析的花Ҏ(gu)loggerd关闭时的三倍?br /><br />3.日志信息的输出时Q日志的优化?br /><br />q是主要p在日志输出的格式化和发送它到它的输出源上。这里我们再一ơ的付出努力以格式化执行的可能快。同appender一栗实际上典型的花费大U是100-300毫秒?br /><br />详情看org.apache.log4.performance.Logging?br /><br />虽然Log4j有许多特点,但是它的W一个设计目标还是速度。一些Log4j的组件已l被重写q很多次以改善性能。不q,投稿者经常提Z新的优化。你应该满意的知道,以SimpleLayout的配|执行测试已l展CZLog4j的输出同System.out.println一样快?br /><br />9. ȝ<br />Log4j是一个用java写成的流行的日志包。一个它与众不同的特Ҏ(gu)在logger中的l承的概c用logger的承可以以L的间隔控制日志的状态输出。这个减了体积和最化日志的代仗?br /><br />易管理性是Log4j API的优点之一。只要日志定义被加入C码中Q它们就可以用配|文件来控制。它们可以有选择的被用Qƈ且发送到不同的多个输出源上,以用户选择好的格式?br /><br />Log4j的包被设计成Q不需p沉重的执行代价就可以保留它们在品中?br /><br /><img src ="http://m.tkk7.com/zeroone0/aggbug/39692.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/zeroone0/" target="_blank">zeroone0</a> 2006-04-06 23:18 <a href="http://m.tkk7.com/zeroone0/articles/39692.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java 1.5http://m.tkk7.com/zeroone0/articles/39291.htmlzeroone0zeroone0Tue, 04 Apr 2006 23:51:00 GMThttp://m.tkk7.com/zeroone0/articles/39291.html 增强的for循环
  Zq代集合和数l,增强的for循环提供了一个简单、兼容的语法。有两点值得一提:(x)

Init表达?/b>
  在@环中Q初始化表达式只计算一ơ。这意味着(zhn)通常可以U除一个变量声明。在q个例子中,我们必须创徏一个整型数l来保存computeNumbers()的结果,以防止每一ơ@环都重新计算该方法。?zhn)可以看到Q下面的代码要比上面的代码整z一些,q且没有泄露变量numbersQ?
未增强的ForQ?
int sum = 0;
Integer[] numbers = computeNumbers();
for (int i=0; i < numbers.length ; i++)
    sum += numbers[i];
增强后的ForQ?
int sum = 0;

for ( int number: computeNumbers() )
    sum += number;
局限?/b>
有时需要在q代期间讉Kq代器或下标Q看h增强的for循环应该允许该操作,但事实上不是q样Q请看下面的例子Q?
for (int i=0; i < numbers.length ; i++) {
    if (i != 0) System.out.print(",");
    System.out.print(numbers[i]);
}

  我们希望数l中的值打Cؓ(f)一个用逗号分隔的清单。我们需要知道目前是否是W一,以便定是否应该打印逗号。用增强的for循环是无法获知这U信息的。我们需要自׃留一个下标或一个布?yu)(dng)值来指示是否l过了第一V?  q是另一个例子:(x)

for (Iterator<integer> it = n.iterator() ; it.hasNext() ; )
    if (it.next() < 0)
        it.remove();

  在此例中Q我们想从整数集合中删除负数Vؓ(f)此,需要对q代器调用一个方法,但是当用增强的for 循环Ӟq代器对我们来说是看不到的。因此,我们只能使用Java 5之前版本的P代方法?  Z说一下,q里需要注意的是,׃Iterator是泛型,所以其声明是Iterator<Integer>。许多h都忘Cq一点而用了Iterator的原始格式?

注释
  注释处理是一个很大的话题。因为本文只x核心的语aҎ(gu),所以我们不打算늛它所有的可能形式和陷阱。  我们讨论内|的注释QSuppressWarningsQDeprecated和OverrideQ以?qing)一般注释处理的局限性?

Suppress Warnings
  该注释关闭了cLҎ(gu)U别的编译器警告。有时候?zhn)比编译器更清楚地知道Q代码必M用一个被否决的方法或执行一些无法静态确定是否类型安全的动作Q而用:(x)

@SuppressWarnings("deprecation")
public static void selfDestruct() {
    Thread.currentThread().stop();
}

  q可能是内置注释最有用的地斏V遗憄是,1.5.0_04的javac不支持它。但?.6支持它,q且Sun正在努力其向后UL?.5中?
Eclipse 3.1中支持该注释Q其他IDE也可能支持它。这允许(zhn)把代码dC警告中解脱出来。如果在~译时出现警告,可以定是?zhn)刚刚把它dq来——以帮助查看那些可能不安全的代码。随着泛型的添加,它用v来将更趁手?

Deprecated
  遗憾的是QDeprecated没那么有用。它本来旨在替换@deprecated javadoc标签Q但是由于它不包含Q何字D,所以也没有方法来deprecatedcLҎ(gu)的用户应该用什么做为替代品。大多数用法都同旉要javadoc标签和这个注释?

Override
  Override表示Q它所注释的方法应该重写超cMh相同{֐的方法:(x)

@Override
public int hashCode() {
    ...
}

  看上面的例子Q如果没有在hashCode中将“C”大写,在编译时不会(x)出现错误Q但是在q行时将无法像期望的那样调用该方法。通过dOverride标签Q编译器?x)提C它是否真正地执行了重写?
  在超cd生改变的情况中,q也很有帮助。如果向该方法中d一个新参数Q而且Ҏ(gu)本n也被重命名了Q那么子cdH然不能~译Q因为它不再重写类的Q何东ѝ?

其它注释
  注释在其他场景中非常有用。当不是直接修改行ؓ(f)而是增强行ؓ(f)Ӟ特别是在dh代码的情况下Q注释在诸如EJB?a target="_blank">Web servicesq样的框架中q行得非常好?
注释不能用做预处理器。Sun的设计特别预防了完全因ؓ(f)注释而修改类的字节码。这样可以正地理解该语a的成果,而且IDE之类的工具也可以执行深入的代码分析和重构之类的功能?
注释不是银弹。第一ơ遇到的时候,Z试图试各种技巧。请看下面这个从别h那里获得的徏议:(x)

public class Foo {
 
    @Property
    private int bar;
 
}

  其思想是ؓ(f)U有字段bar自动创徏getter和setterҎ(gu)。遗憄是,q个x有两个失败之处:(x)1)它不能运行,2)它代码难以阅读和处理?  它是无法实现的,因ؓ(f)前面已经提到了,Sun特别L了对出现注释的类q行修改?
  即是可能的Q它也不是一个好LQ因为它使代码可L差。第一ơ看到这D代码的Z(x)不知道该注释创徏了方法。此外,如果来(zhn)需要在q些Ҏ(gu)内部执行一些操作,注释也是没用的?  MQ不要试囄注释d那些常规代码可以完成的事情?

枚D
  enum非常像public static final int声明Q后者作为枚丑ր已l用了很多q。对int所做的最大也是最明显的改q是cd安全——?zhn)不能错误地用枚D的一U类型代替另一U类型,q一点和int不同Q所有的int对编译器来说都是一L(fng)。除L数例外的情况,通常都应该用enum实例替换全部的枚N格的intl构?
  枚D提供了一些附加的Ҏ(gu)。EnumMap和EnumSetq两个实用类是专门ؓ(f)枚D优化的标准集合实现。如果知道集合只包含枚DcdQ那么应该用这些专门的集合来代替HashMap或HashSet?
  大部分情况下Q可以用enum对代码中的所有public static final int做插入替换。它们是可比的,q且可以静态导入,所以对它们的引用看h是等同的Q即使是对于内部c(或内部枚丄型)。注意,比较枚Dcd的时候,声明它们的指令表明了它们的顺序倹{?

“隐藏的”静态方?
  两个静态方法出现在所有枚丄型声明中。因为它们是枚D子类上的静态方法,而不是Enum本n的方法,所以它们在java.lang.Enum的javadoc中没有出现?
  W一个是values()Q返回一个枚丄型所有可能值的数组?
  W二个是valueOf()Qؓ(f)提供的字W串q回一个枚丄型,该枚丄型必ȝ地匚w源代码声明?
Ҏ(gu)
  关于枚DcdQ我们最喜欢的一个方面是它可以有Ҏ(gu)。过L可能需要编写一些代码,对public static final intq行转换Q把它从数据库类型{换ؓ(f)JDBC URL。而现在则可以让枚丄型本w带一个整理代码的Ҏ(gu)。下面就是一个例子,包括DatabaseType枚Dcd的抽象方法以?qing)每个枚丑֮例中提供的实玎ͼ?x)

  public enum  DatabaseType {
  ORACLE {
  public String getJdbcUrl() {...}
  },
  MYSQL {
  public String getJdbcUrl() {...}
  };
  public abstract String getJdbcUrl();
  }
  现在枚Dcd可以直接提供它的实用Ҏ(gu)。例如:(x)

DatabaseType dbType = ...;
String jdbcURL = dbType.getJdbcUrl();

  要获取URLQ必预先知道该实用Ҏ(gu)在哪里?


可变参数(Vararg)
  正确C用可变参数确实可以清理一些垃圾代码。典型的例子是一个带有可变的String参数个数的logҎ(gu)Q?

    Log.log(String code)
    Log.log(String code,  String arg)
    Log.log(String code,  String arg1, String arg2)
    Log.log(String code,  String[] args)
  当讨论可变参数时Q比较有的是,如果用新的可变参数替换前四个例子Q将是兼容的Q?
Log.log(String code, String... args)
  所有的可变参数都是源兼容的——那是_(d)如果重新~译log()Ҏ(gu)的所有调用程序,可以直接替换全部的四个方法。然而,如果需要向后的二进制兼Ҏ(gu),那么需要舍d三个Ҏ(gu)。只有最后那个带一个字W串数组参数的方法等效于可变参数版本Q因此可以被可变参数版本替换?

cd强制转换

  如果希望调用E序了解应该使用哪种cd的参敎ͼ那么应该避免用可变参数进行类型强制{换。看下面q个例子Q第一希望是StringQ第二项希望是ExceptionQ?
    Log.log(Object...  objects) {
    String message = (String)objects[0];
    if (objects.length > 1) {
    Exception e = (Exception)objects[1];
    // Do something with the exception
    }
    }
  Ҏ(gu){֐应该如下所C,相应的可变参数分别用String和Exception声明Q?

Log.log(String message, Exception e, Object... objects) {...}

  不要使用可变参数破坏cdpȝ。需要强cd化时才可以用它。对于这个规则,PrintStream.printf()是一个有的例外Q它提供cd信息作ؓ(f)自己的第一个参敎ͼ以便E后可以接受那些cd?

协变q回

  协变q回的基本用法是用于在已知一个实现的q回cd比API更具体的时候避免进行类型强制{换。在下面q个例子中,有一个返回Animal对象的Zoo接口。我们的实现q回一个AnimalImpl对象Q但是在JDK 1.5之前Q要q回一个Animal对象必d明?
    public interface Zoo  {
    public Animal getAnimal();
    }
  public class ZooImpl  implements Zoo {
  public Animal getAnimal(){
  return new AnimalImpl();
  }
  }
  协变q回的用替换了三个反模式:(x)

  • 直接字段讉K。ؓ(f)了规避API限制Q一些实现把子类直接暴露为字D:(x)

ZooImpl._animal

  • 另一UŞ式是Q在知道实现的实际上是特定的子类的情况下Q在调用E序中执行向下{换:(x)

((AnimalImpl)ZooImpl.getAnimal()).implMethod();

  • 我看到的最后一UŞ式是一个具体的Ҏ(gu)Q该Ҏ(gu)用来避免׃个完全不同的{֐所引发的问题:(x)

ZooImpl._getAnimal();

  q三U模式都有它们的问题和局限性。要么是不够整洁Q要么就是暴露了不必要的实现l节?

协变

  协变q回模式比较整z、安全ƈ且易于维护,它也不需要类型强制{换或特定的方法或字段Q?
public AnimalImpl getAnimal(){
return new AnimalImpl();
}
  使用l果Q?
ZooImpl.getAnimal().implMethod();

使用泛型
  
我们从两个角度来了解泛型:(x)使用泛型和构造泛型。我们不讨论List、Set和Map的显而易见的用法。知道泛型集合是强大的ƈ且应该经怋用就_了?
  我们讨论泛型方法的使用以及(qing)~译器推断类型的Ҏ(gu)。通常q些都不?x)出问题Q但是当出问题时Q错误信息会(x)非常令h费解Q所以需要了解如何修复这些问题?

泛型Ҏ(gu)
  
除了泛型cdQJava 5q引入了泛型Ҏ(gu)。在q个来自java.util.Collections的例子中Q构造了一个单元素列表。新的List的元素类型是Ҏ(gu)传入Ҏ(gu)的对象的cd来推断的Q?
static <T> List<T> Collections.singletonList(T o)
CZ用法Q?
public List<Integer> getListOfOne() {
    return Collections.singletonList(1);
}

  在示例用法中Q我们传入了一个int。所以方法的q回cd是List<Integer>。编译器把T推断为Integer。这和泛型类型是不同的,因ؓ(f)(zhn)通常不需要显式地指定cd参数?
q也昄了自动装和泛型的相互作用。类型参数必L引用cdQ这是Z么我们得到的是List<Integer>而不是List<int>?

不带参数的泛型方?br />  emptyList()Ҏ(gu)与泛型一起引入,作ؓ(f)java.util.Collections中EMPTY_LIST字段的类型安全置换:(x)
static <T> List<T> Collections.emptyList()
CZ用法Q?
public List<Integer> getNoIntegers() {
    return Collections.emptyList();
}

  与先前的例子不同Q这个方法没有参敎ͼ那么~译器如何推断T的类型呢Q基本上Q它?yu)尝试用一ơ参数。如果没有v作用Q它再次试使用q回或赋值类型。在本例中,q回的是List<Integer>Q所以T被推断ؓ(f)Integer?
  如果在返回语句或赋D句之外的位置调用泛型Ҏ(gu)?x)怎么样呢Q那么编译器无法执行类型推断的W二ơ传送。在下面q个例子中,emptyList()是从条gq算W内部调用的Q?

public List<Integer> getNoIntegers() {
    return x ? Collections.emptyList() : null;
}

  因ؓ(f)~译器看不到q回上下文,也不能推断TQ所以它攑ּq用Object。?zhn)看C个错误消息,比如Q“无法将List<Object>转换为List<Integer>。?
Z修复q个错误Q应昑ּ地向Ҏ(gu)调用传递类型参数。这P~译器就不会(x)试图推断cd参数Q就可以获得正确的结果:(x)

return x ? Collections.<Integer>emptyList() : null;

  q种情况l常发生的另一个地Ҏ(gu)在方法调用中。如果一个方法带一个List<String>参数Qƈ且需要ؓ(f)那个参数调用q个传递的emptyList()Q那么也需要用这个语法?

集合之外
  q里有三个泛型类型的例子Q它们不是集合,而是以一U新颖的方式使用泛型。这三个例子都来自标准的Java库:(x)
  • Class<T>
    Class在类的类型上被参数化了。这׃无需cd强制转换而构造一个newInstance成ؓ(f)可能?
  • Comparable<T>
    Comparable被实际的比较cd参数化。这在compareTo()调用时提供了更强的类型化。例如,String实现Comparable<String>。对除String之外的Q何东西调用compareTo()Q都?x)在~译时失败?
  • Enum<E extends Enum<E>>
    Enum被枚丄型参数化。一个名为Color的枚丄型将扩展Enum<Color>。getDeclaringClass()Ҏ(gu)q回枚Dcd的类对象Q在q个例子中就是一个Color对象。它与getClass()不同Q后者可能返回一个无名类?
通配W?br />  泛型最复杂的部分是寚w配W的理解。我们将讨论三种cd的通配W以?qing)它们的用途?
  首先让我们了解一下数l是如何工作的。可以从一个Integer[]Z个Number[]赋倹{如果尝试把一个Float写到Number[]中,那么可以~译Q但在运行时?x)失败,出现一个ArrayStoreExceptionQ?
Integer[] ia = new Integer[5];
Number[] na = ia;
na[0] = 0.5; // compiles, but fails at runtime
如果试图把该例直接{换成泛型Q那么会(x)在编译时p|Q因值是不被允许的:(x)
List<Integer> iList = new ArrayList<Integer>();
List<Number> nList = iList; // not allowed
nList.add(0.5);

  如果使用泛型Q只要代码在~译时没有出现警告,׃?x)遇到运行时ClassCastException?

上限通配W?/b>
  我们惌的是一个确切元素类型未知的列表Q这一点与数组是不同的?
List<Number>是一个列表,其元素类型是具体cdNumber?
List<? extends Number>是一个确切元素类型未知的列表。它是Number或其子类型?

上限
  
如果我们更新初始的例子,q赋值给List<? extends Number>Q那么现在赋值就?x)成功了Q?

List<Integer> iList = new ArrayList<Integer>();
List<? extends Number> nList = iList;
Number n = nList.get(0);
nList.add(0.5); // Not allowed

  我们可以从列表中得到NumberQ因为无论列表的切元素cd是什么(F(tun)loat、Integer或NumberQ,我们都可以把它赋值给Number?
  我们仍然不能把Q点类型插入列表中。这?x)在~译时失败,因ؓ(f)我们不能证明q是安全的。如果我们想要向列表中添加Q点类型,它将破坏iList的初始类型安全——它只存储Integer?
  通配W给了我们比数组更多的表达能力?

Z么用通配W?br />  在下面这个例子中Q通配W用于向API的用户隐藏类型信息。在内部QSet被存储ؓ(f)CustomerImpl。而API的用户只知道他们正在获取一个SetQ从中可以读取Customer?
此处通配W是必需的,因ؓ(f)无法从Set<CustomerImpl>向Set<Customer>赋|(x)
public class CustomerFactory {
    private Set<CustomerImpl> _customers;
    public Set<? extends Customer> getCustomers() {
        return _customers;
    }
}

通配W和协变q回
  通配W的另一U常见用法是和协变返回一起用。与赋值相同的规则可以应用到协变返回上。如果希望在重写的方法中q回一个更具体的泛型类型,声明的方法必M用通配W:(x)

public interface NumberGenerator {
    public List<? extends Number> generate();
}
public class FibonacciGenerator extends NumberGenerator {
    public List<Integer> generate() {
        ...
    }
}

  如果要用数l,接口可以q回Number[]Q而实现可以返回Integer[]?

下限
  我们所谈的主要是关于上限通配W的。还有一个下限通配W。List<? super Number>是一个确切“元素类型”未知的列表Q但是可能是MnumberQ或者Number的超cd。所以它可能是一个List<Number>或一个List<Object>?
  下限通配W远没有上限通配W那样常见,但是当需要它们的时候,它们是必需的?

下限与上?br />
List<? extends Number> readList = new ArrayList<Integer>();
Number n = readList.get(0);

List<? super Number> writeList = new ArrayList<Object>();
writeList.add(new Integer(5));

  W一个是可以从中L的列表?
  W二个是可以向其写数的列表?

无界通配W?/b>
  最后,List<?>列表的内容可以是McdQ而且它与List<? extends Object>几乎相同。可以随时读取ObjectQ但是不能向列表中写入内宏V?

公共API中的通配W?
  MQ正如前面所_(d)通配W在向调用程序隐藏实现细节方面是非常重要的,但即使下限通配W看h是提供只读访问,׃remove(int position)之类的非泛型Ҏ(gu)Q它们也q如此。如果?zhn)惌一个真正不变的集合Q可以用java.util.Collection上的Ҏ(gu)Q比如unmodifiableList()?
  ~写API的时候要记得通配W。通常Q在传递泛型类型时Q应该尝试用通配W。它使更多的调用E序可以讉KAPI?
  通过接收List<? extends Number>而不是List<Number>Q下面的Ҏ(gu)可以p多不同类型的列表调用Q?

void removeNegatives(List<? extends Number> list);

构造泛型类?/b>
  现在我们讨论构造自q泛型cd。我们将展示一些例子,其中通过使用泛型可以提高cd安全性,我们q将讨论一些实现泛型类型时的常见问题?/p>

集合风格(Collection-like)的函?/b>
  W一个泛型类的例子是一个集合风格的例子。Pair有两个类型参敎ͼ而且字段是类型的实例Q?

public final class Pair<A,B> {
    public final A first;
    public final B second;

    public Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }
}

  q从方法返回两个项而无需为每个两U类型的l合~写专用的类成ؓ(f)可能。另一U方法是q回Object[]Q而这hcd不安全或者不整洁的?
在下面的用法中,我们从方法返回一个File和一个Boolean。方法的客户端可以直接用字D而无需cd强制转换Q?

public Pair<File,Boolean> getFileAndWriteStatus(String path){
    // create file and status
    return new Pair<File,Boolean>(file, status);
}

Pair<File,Boolean> result = getFileAndWriteStatus("...");
File f = result.first;
boolean writeable = result.second;

集合之外
  在下面这个例子中Q泛型被用于附加的编译时安全性。通过把DBFactorycd数化为所创徏的PeercdQ?zhn)实际上是在强制Factory子类q回一个Peer的特定子cdQ?

public abstract class DBFactory<T extends DBPeer> {
    protected abstract T createEmptyPeer();
    public List<T> get(String constraint) {
        List<T> peers = new ArrayList<T>();
        // database magic
        return peers;
    }
}
通过实现DBFactory<Customer>QCustomerFactory必须从createEmptyPeer()q回一个CustomerQ?
public class CustomerFactory extends DBFactory<Customer>{

    public Customer createEmptyPeer() {
        return new Customer();
    }
}

泛型Ҏ(gu)
  不管惌对参C间还是参Cq回cd之间的泛型类型施加约束,都可以用泛型方法:(x)
  例如Q如果编写的反{函数是在位置上反转,那么可能不需要泛型方法。然而,如果希望反{q回一个新的ListQ那么可能会(x)希望新List的元素类型与传入的List的类型相同。在q种情况下,需要一个泛型方法:(x)


<T> List<T> reverse(List<T> list)

具体?br />  当实C个泛型类Ӟ(zhn)可能想要构造一个数lT[]。因为泛型是通过擦除(erasure)实现的,所以这是不允许的?
  (zhn)可以尝试把Object[]强制转换为T[]。但q是不安全的?

具体化解x?br />  按照泛型教程的惯例,解决Ҏ(gu)使用的是“类型o(h)牌”,通过向构造函数添加一个Class<T>参数Q可以强制客L(fng)为类的类型参数提供正的cd象:(x)
public class ArrayExample<T> {
    private Class<T> clazz;

    public ArrayExample(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T[] getArray(int size) {
        return (T[])Array.newInstance(clazz, size);
    }
}

  Z构造ArrayExample<String>Q客L(fng)必须把String.class传递给构造函敎ͼ因ؓ(f)String.class的类型是Class<String>?
拥有cd象构造一个具有正元素类型的数组成ؓ(f)可能?


引用 http://dev2dev.bea.com.cn/techdoc/20060105718.html



zeroone0 2006-04-05 07:51 发表评论
]]>
jsp面中的下蝲功能实现http://m.tkk7.com/zeroone0/articles/38831.htmlzeroone0zeroone0Mon, 03 Apr 2006 02:02:00 GMThttp://m.tkk7.com/zeroone0/articles/38831.htmlhttp://m.tkk7.com/zeroone0/comments/38831.htmlhttp://m.tkk7.com/zeroone0/articles/38831.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/38831.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/38831.html<%

String filename="test.jpg";
String dirName=application.getRealPath("/WEB-INF/upload");
java.io.File ff=null;
String dd=dirName+System.getProperties().getProperty("file.separator")+filename;
try{
ff=new java.io.File(dd);
}
catch(Exception e){
e.printStackTrace();
}
if (ff!=null&&ff.exists()&&ff.isFile())
{
long filelength = ff.length();
InputStream inStream=new FileInputStream(dd);
//讄输出的格?
response.reset();
response.setContentType("application/x-msdownload");
response.setContentLength((int)filelength);
response.addHeader("Content-Disposition","attachment; filename=\"" + toUtf8String(filename) + "\"");
//循环取出中的数?
byte[] b = new byte[100];
int len;
while((len=inStream.read(b)) >0)
response.getOutputStream().write(b,0,len);
inStream.close();

}
%>



zeroone0 2006-04-03 10:02 发表评论
]]>
Web文g的ContentTypecdhttp://m.tkk7.com/zeroone0/articles/38830.htmlzeroone0zeroone0Mon, 03 Apr 2006 01:59:00 GMThttp://m.tkk7.com/zeroone0/articles/38830.htmlhttp://m.tkk7.com/zeroone0/comments/38830.htmlhttp://m.tkk7.com/zeroone0/articles/38830.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/38830.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/38830.htmlWeb文g的ContentTypecd
 
 
".*"="application/octet-stream"
".001"="application/x-001"
".301"="application/x-301"
".323"="text/h323"
".906"="application/x-906"
".907"="drawing/907"
".a11"="application/x-a11"
".acp"="audio/x-mei-aac"
".ai"="application/postscript"
".aif"="audio/aiff"
".aifc"="audio/aiff"
".aiff"="audio/aiff"
".anv"="application/x-anv"
".asa"="text/asa"
".asf"="video/x-ms-asf"
".asp"="text/asp"
".asx"="video/x-ms-asf"
".au"="audio/basic"
".avi"="video/avi"
".awf"="application/vnd.adobe.workflow"
".biz"="text/xml"
".bmp"="application/x-bmp"
".bot"="application/x-bot"
".c4t"="application/x-c4t"
".c90"="application/x-c90"
".cal"="application/x-cals"
".cat"="application/vnd.ms-pki.seccat"
".cdf"="application/x-netcdf"
".cdr"="application/x-cdr"
".cel"="application/x-cel"
".cer"="application/x-x509-ca-cert"
".cg4"="application/x-g4"
".cgm"="application/x-cgm"
".cit"="application/x-cit"
".class"="java/*"
".cml"="text/xml"
".cmp"="application/x-cmp"
".cmx"="application/x-cmx"
".cot"="application/x-cot"
".crl"="application/pkix-crl"
".crt"="application/x-x509-ca-cert"
".csi"="application/x-csi"
".css"="text/css"
".cut"="application/x-cut"
".dbf"="application/x-dbf"
".dbm"="application/x-dbm"
".dbx"="application/x-dbx"
".dcd"="text/xml"
".dcx"="application/x-dcx"
".der"="application/x-x509-ca-cert"
".dgn"="application/x-dgn"
".dib"="application/x-dib"
".dll"="application/x-msdownload"
".doc"="application/msword"
".dot"="application/msword"
".drw"="application/x-drw"
".dtd"="text/xml"
".dwf"="Model/vnd.dwf"
".dwf"="application/x-dwf"
".dwg"="application/x-dwg"
".dxb"="application/x-dxb"
".dxf"="application/x-dxf"
".edn"="application/vnd.adobe.edn"
".emf"="application/x-emf"
".eml"="message/rfc822"
".ent"="text/xml"
".epi"="application/x-epi"
".eps"="application/x-ps"
".eps"="application/postscript"
".etd"="application/x-ebx"
".exe"="application/x-msdownload"
".fax"="image/fax"
".fdf"="application/vnd.fdf"
".fif"="application/fractals"
".fo"="text/xml"
".frm"="application/x-frm"
".g4"="application/x-g4"
".gbr"="application/x-gbr"
".gcd"="application/x-gcd"
".gif"="image/gif"
".gl2"="application/x-gl2"
".gp4"="application/x-gp4"
".hgl"="application/x-hgl"
".hmr"="application/x-hmr"
".hpg"="application/x-hpgl"
".hpl"="application/x-hpl"
".hqx"="application/mac-binhex40"
".hrf"="application/x-hrf"
".hta"="application/hta"
".htc"="text/x-component"
".htm"="text/html"
".html"="text/html"
".htt"="text/webviewhtml"
".htx"="text/html"
".icb"="application/x-icb"
".ico"="image/x-icon"
".ico"="application/x-ico"
".iff"="application/x-iff"
".ig4"="application/x-g4"
".igs"="application/x-igs"
".iii"="application/x-iphone"
".img"="application/x-img"
".ins"="application/x-internet-signup"
".isp"="application/x-internet-signup"
".IVF"="video/x-ivf"
".java"="java/*"
".jfif"="image/jpeg"
".jpe"="image/jpeg"
".jpe"="application/x-jpe"
".jpeg"="image/jpeg"
".jpg"="image/jpeg"
".jpg"="application/x-jpg"
".js"="application/x-javascript"
".jsp"="text/html"
".la1"="audio/x-liquid-file"
".lar"="application/x-laplayer-reg"
".latex"="application/x-latex"
".lavs"="audio/x-liquid-secure"
".lbm"="application/x-lbm"
".lmsff"="audio/x-la-lms"
".ls"="application/x-javascript"
".ltr"="application/x-ltr"
".m1v"="video/x-mpeg"
".m2v"="video/x-mpeg"
".m3u"="audio/mpegurl"
".m4e"="video/mpeg4"
".mac"="application/x-mac"
".man"="application/x-troff-man"
".math"="text/xml"
".mdb"="application/msaccess"
".mdb"="application/x-mdb"
".mfp"="application/x-shockwave-flash"
".mht"="message/rfc822"
".mhtml"="message/rfc822"
".mi"="application/x-mi"
".mid"="audio/mid"
".midi"="audio/mid"
".mil"="application/x-mil"
".mml"="text/xml"
".mnd"="audio/x-musicnet-download"
".mns"="audio/x-musicnet-stream"
".mocha"="application/x-javascript"
".movie"="video/x-sgi-movie"
".mp1"="audio/mp1"
".mp2"="audio/mp2"
".mp2v"="video/mpeg"
".mp3"="audio/mp3"
".mp4"="video/mpeg4"
".mpa"="video/x-mpg"
".mpd"="application/vnd.ms-project"
".mpe"="video/x-mpeg"
".mpeg"="video/mpg"
".mpg"="video/mpg"
".mpga"="audio/rn-mpeg"
".mpp"="application/vnd.ms-project"
".mps"="video/x-mpeg"
".mpt"="application/vnd.ms-project"
".mpv"="video/mpg"
".mpv2"="video/mpeg"
".mpw"="application/vnd.ms-project"
".mpx"="application/vnd.ms-project"
".mtx"="text/xml"
".mxp"="application/x-mmxp"
".net"="image/pnetvue"
".nrf"="application/x-nrf"
".nws"="message/rfc822"
".odc"="text/x-ms-odc"
".out"="application/x-out"
".p10"="application/pkcs10"
".p12"="application/x-pkcs12"
".p7b"="application/x-pkcs7-certificates"
".p7c"="application/pkcs7-mime"
".p7m"="application/pkcs7-mime"
".p7r"="application/x-pkcs7-certreqresp"
".p7s"="application/pkcs7-signature"
".pc5"="application/x-pc5"
".pci"="application/x-pci"
".pcl"="application/x-pcl"
".pcx"="application/x-pcx"
".pdf"="application/pdf"
".pdf"="application/pdf"
".pdx"="application/vnd.adobe.pdx"
".pfx"="application/x-pkcs12"
".pgl"="application/x-pgl"
".pic"="application/x-pic"
".pko"="application/vnd.ms-pki.pko"
".pl"="application/x-perl"
".plg"="text/html"
".pls"="audio/scpls"
".plt"="application/x-plt"
".png"="image/png"
".png"="application/x-png"
".pot"="application/vnd.ms-powerpoint"
".ppa"="application/vnd.ms-powerpoint"
".ppm"="application/x-ppm"
".pps"="application/vnd.ms-powerpoint"
".ppt"="application/vnd.ms-powerpoint"
".ppt"="application/x-ppt"
".pr"="application/x-pr"
".prf"="application/pics-rules"
".prn"="application/x-prn"
".prt"="application/x-prt"
".ps"="application/x-ps"
".ps"="application/postscript"
".ptn"="application/x-ptn"
".pwz"="application/vnd.ms-powerpoint"
".r3t"="text/vnd.rn-realtext3d"
".ra"="audio/vnd.rn-realaudio"
".ram"="audio/x-pn-realaudio"
".ras"="application/x-ras"
".rat"="application/rat-file"
".rdf"="text/xml"
".rec"="application/vnd.rn-recording"
".red"="application/x-red"
".rgb"="application/x-rgb"
".rjs"="application/vnd.rn-realsystem-rjs"
".rjt"="application/vnd.rn-realsystem-rjt"
".rlc"="application/x-rlc"
".rle"="application/x-rle"
".rm"="application/vnd.rn-realmedia"
".rmf"="application/vnd.adobe.rmf"
".rmi"="audio/mid"
".rmj"="application/vnd.rn-realsystem-rmj"
".rmm"="audio/x-pn-realaudio"
".rmp"="application/vnd.rn-rn_music_package"
".rms"="application/vnd.rn-realmedia-secure"
".rmvb"="application/vnd.rn-realmedia-vbr"
".rmx"="application/vnd.rn-realsystem-rmx"
".rnx"="application/vnd.rn-realplayer"
".rp"="image/vnd.rn-realpix"
".rpm"="audio/x-pn-realaudio-plugin"
".rsml"="application/vnd.rn-rsml"
".rt"="text/vnd.rn-realtext"
".rtf"="application/msword"
".rtf"="application/x-rtf"
".rv"="video/vnd.rn-realvideo"
".sam"="application/x-sam"
".sat"="application/x-sat"
".sdp"="application/sdp"
".sdw"="application/x-sdw"
".sit"="application/x-stuffit"
".slb"="application/x-slb"
".sld"="application/x-sld"
".slk"="drawing/x-slk"
".smi"="application/smil"
".smil"="application/smil"
".smk"="application/x-smk"
".snd"="audio/basic"
".sol"="text/plain"
".sor"="text/plain"
".spc"="application/x-pkcs7-certificates"
".spl"="application/futuresplash"
".spp"="text/xml"
".ssm"="application/streamingmedia"
".sst"="application/vnd.ms-pki.certstore"
".stl"="application/vnd.ms-pki.stl"
".stm"="text/html"
".sty"="application/x-sty"
".svg"="text/xml"
".swf"="application/x-shockwave-flash"
".tdf"="application/x-tdf"
".tg4"="application/x-tg4"
".tga"="application/x-tga"
".tif"="image/tiff"
".tif"="application/x-tif"
".tiff"="image/tiff"
".tld"="text/xml"
".top"="drawing/x-top"
".torrent"="application/x-bittorrent"
".tsd"="text/xml"
".txt"="text/plain"
".uin"="application/x-icq"
".uls"="text/iuls"
".vcf"="text/x-vcard"
".vda"="application/x-vda"
".vdx"="application/vnd.visio"
".vml"="text/xml"
".vpg"="application/x-vpeg005"
".vsd"="application/vnd.visio"
".vsd"="application/x-vsd"
".vss"="application/vnd.visio"
".vst"="application/vnd.visio"
".vst"="application/x-vst"
".vsw"="application/vnd.visio"
".vsx"="application/vnd.visio"
".vtx"="application/vnd.visio"
".vxml"="text/xml"
".wav"="audio/wav"
".wax"="audio/x-ms-wax"
".wb1"="application/x-wb1"
".wb2"="application/x-wb2"
".wb3"="application/x-wb3"
".wbmp"="image/vnd.wap.wbmp"
".wiz"="application/msword"
".wk3"="application/x-wk3"
".wk4"="application/x-wk4"
".wkq"="application/x-wkq"
".wks"="application/x-wks"
".wm"="video/x-ms-wm"
".wma"="audio/x-ms-wma"
".wmd"="application/x-ms-wmd"
".wmf"="application/x-wmf"
".wml"="text/vnd.wap.wml"
".wmv"="video/x-ms-wmv"
".wmx"="video/x-ms-wmx"
".wmz"="application/x-ms-wmz"
".wp6"="application/x-wp6"
".wpd"="application/x-wpd"
".wpg"="application/x-wpg"
".wpl"="application/vnd.ms-wpl"
".wq1"="application/x-wq1"
".wr1"="application/x-wr1"
".wri"="application/x-wri"
".wrk"="application/x-wrk"
".ws"="application/x-ws"
".ws2"="application/x-ws"
".wsc"="text/scriptlet"
".wsdl"="text/xml"
".wvx"="video/x-ms-wvx"
".xdp"="application/vnd.adobe.xdp"
".xdr"="text/xml"
".xfd"="application/vnd.adobe.xfd"
".xfdf"="application/vnd.adobe.xfdf"
".xhtml"="text/html"
".xls"="application/vnd.ms-excel"
".xls"="application/x-xls"
".xlw"="application/x-xlw"
".xml"="text/xml"
".xpl"="audio/scpls"
".xq"="text/xml"
".xql"="text/xml"
".xquery"="text/xml"
".xsd"="text/xml"
".xsl"="text/xml"
".xslt"="text/xml"
".xwd"="application/x-xwd"
".x_b"="application/x-x_b"
".x_t"="application/x-x_t"


zeroone0 2006-04-03 09:59 发表评论
]]>
Java加密技?/title><link>http://m.tkk7.com/zeroone0/articles/38829.html</link><dc:creator>zeroone0</dc:creator><author>zeroone0</author><pubDate>Mon, 03 Apr 2006 01:55:00 GMT</pubDate><guid>http://m.tkk7.com/zeroone0/articles/38829.html</guid><wfw:comment>http://m.tkk7.com/zeroone0/comments/38829.html</wfw:comment><comments>http://m.tkk7.com/zeroone0/articles/38829.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/zeroone0/comments/commentRss/38829.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/zeroone0/services/trackbacks/38829.html</trackback:ping><description><![CDATA[ <div> <p> <strong>W??基础知识</strong> </p> <p>1.1. 单钥密码体制 <br />单钥密码体制是一U传l的加密法Q是指信息的发送方和接收方共同使用同一把密钥进行加解密?/p> <p>通常,使用的加密算?比较侉K?密钥短,加解密速度快,破译极其困难。但是加密的安全性依靠密钥保的安全?在公开的计机|络上安全地传送和保管密钥是一个严ȝ问题Qƈ且如果在多用L(fng)情况下密钥的保管安全性也是一个问题?/p> <p>单钥密码体制的代表是国的DES</p> <p>1.2. 消息摘要 <br />一个消息摘要就是一个数据块的数字指UV即对一个Q意长度的一个数据块q行计算Q生一个唯一指印Q对于SHA1是生一?0字节的二q制数组Q?/p> <p>消息摘要有两个基本属性:(x) </p> <p>两个不同的报文难以生成相同的摘要 <br />难以Ҏ(gu)定的摘要生成一个报文,而由该报文反推算指定的摘?br />代表Q美国国家标准技术研I所的SHA1和麻省理工学院Ronald Rivest提出的MD5</p> <p>1.3. Diffie-Hellman密钥一致协?<br />密钥一致协议是由公开密钥密码体制的奠ZhDiffie和Hellman所提出的一U思想?/p> <p>先决条g,允许两名用户在公开媒体上交换信息以生成"一??可以׃n的密?/p> <p>代表Q指数密钥一致协?Exponential Key Agreement Protocol)</p> <p>1.4. 非对U算法与公钥体系 <br />1976q_(d)Dittie和Hellman军_钥管理问题,在他们的奠基性的工作"密码学的新方?一文中Q提ZU密钥交换协议,允许在不安全的媒体上通过通讯双方交换信息Q安全地传送秘密密钥。在此新思想的基上,很快出现了非对称密钥密码体制Q即公钥密码体制。在公钥体制中,加密密钥不同于解密密钥,加密密钥公之于众Q谁都可以用;解密密钥只有解密q道。它们分别称为公开密钥QPublic keyQ和U密密钥QPrivate keyQ?</p> <p>q今为止的所有公钥密码体pMQRSApȝ是最著名、最多用的一U。RSA公开密钥密码pȝ是由R.Rivest、A.Shamir和L.Adleman俊教授于1977q提出的。RSA的取名就是来自于q三位发明者的姓的W一个字?/p> <p>1.5. 数字{֐ <br />所谓数字签名就是信息发送者用其私钥对从所传报文中提取出的特征数据Q或U数字指U)q行RSA法操作Q以保证发信人无法抵赖曾发过该信息(即不可抵赖性)Q同时也保信息报文在经{֐后末被篡改(卛_整性)。当信息接收者收到报文后Q就可以用发送者的公钥Ҏ(gu)字签名进行验证。  </p> <p>在数字签名中有重要作用的数字指纹是通过一cȝD的散列函数QHASH函数Q生成的Q对q些HASH函数的特D要求是Q?</p> <p>接受的输入报文数据没有长度限Ӟ <br />对Q何输入报文数据生成固定长度的摘要Q数字指U)输出 <br />从报文能方便地算出摘要; <br />难以Ҏ(gu)定的摘要生成一个报文,而由该报文反推算指定的摘要; <br />两个不同的报文难以生成相同的摘要</p> <p>代表QDSA</p> <p> <strong>W??在JAVA中的实现</strong> </p> <p>2.1. 相关 <br />Diffie-Hellman密钥一致协议和DESE序需要JCE工具库的支持,可以?<a ><font color="#000033">http://java.sun.com/security/index.html</font></a> 下蝲JCE,q进行安装。简易安装把 jce1.2.1\lib 下的所有内容复制到 %java_home%\lib\ext?如果没有ext目录自行建立,再把jce1_2_1.jar和sunjce_provider.jard到CLASSPATH?更详l说明请看相应用h?/p> <p>2.2. 消息摘要MD5和SHA的?<br />使用Ҏ(gu):</p> <p>首先用生成一个MessageDigestc?定计算Ҏ(gu)</p> <p>java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");</p> <p>d要进行计摘要的信息</p> <p>alga.update(myinfo.getBytes());</p> <p>计算出摘?/p> <p>byte[] digesta=alga.digest();</p> <p>发送给其他Z的信息和摘要</p> <p>其他人用相同的方法初始化,d信息,最后进行比较摘要是否相?/p> <p>algb.isEqual(digesta,algb.digest())</p> <p>相关AIP</p> <p>java.security.MessageDigest c?/p> <p>static getInstance(String algorithm)</p> <p>q回一个MessageDigest对象,它实现指定的法</p> <p>参数:法??SHA-1 或MD5</p> <p>void update (byte input)</p> <p>void update (byte[] input)</p> <p>void update(byte[] input, int offset, int len)</p> <p>d要进行计摘要的信息</p> <p>byte[] digest()</p> <p>完成计算,q回计算得到的摘?对于MD5?6?SHA?0?</p> <p>void reset()</p> <p>复位</p> <p>static boolean isEqual(byte[] digesta, byte[] digestb)</p> <p>比效两个摘要是否相同</p> <p>代码Q?<br />import java.security.*;<br />public class myDigest {<br />public static void main(String[] args) {</p> <p>myDigest my=new myDigest();<br />my.testDigest();</p> <p>}<br />public void testDigest()<br />{<br />try {<br />String myinfo="我的试信息";</p> <p>//java.security.MessageDigest alg=java.security.MessageDigest.getInstance("MD5");<br />java.security.MessageDigest alga=java.security.MessageDigest.getInstance("SHA-1");<br />alga.update(myinfo.getBytes());<br />byte[] digesta=alga.digest();<br />System.out.println("本信息摘要是:"+byte2hex(digesta));<br />//通过某中方式传给其他Z的信?myinfo)和摘?digesta) Ҏ(gu)可以判断是否更改或传输正?br />java.security.MessageDigest algb=java.security.MessageDigest.getInstance("SHA-1");<br />algb.update(myinfo.getBytes());<br />if (algb.isEqual(digesta,algb.digest())) {<br />System.out.println("信息查正?);<br />}<br />else<br />{<br />System.out.println("摘要不相?);<br />}</p> <p>}<br />catch (java.security.NoSuchAlgorithmException ex) {<br />System.out.println("非法摘要法");<br />}</p> <p>}<br />public String byte2hex(byte[] b) //二行制{字符?br />{<br />String hs="";<br />String stmp="";<br />for (int n=0;n {<br />stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));<br />if (stmp.length()==1) hs=hs+"0"+stmp;<br />else hs=hs+stmp;<br />if (n }<br />return hs.toUpperCase();<br />}</p> <p>}</p> <p> </p> <p>2.3. 数字{֐DSA</p> <p>对于一个用h讲首先要生成他的密钥?q且分别保存 <br />生成一个KeyPairGenerator实例 <br />java.security.KeyPairGenerator keygen=java.security.KeyPairGenerator.getInstance("DSA");<br />如果讑֮随机产生器就用如总码初始化<br />SecureRandom secrand=new SecureRandom(); </p> <p>secrand.setSeed("tttt".getBytes()); //初始化随Z生器<br />keygen.initialize(512,secrand); //初始化密钥生成器<br />否则<br />keygen.initialize(512);<br />生成密钥公钥pubkey和私钥prikey<br />KeyPair keys=keygen.generateKeyPair(); //生成密钥l?br />PublicKey pubkey=keys.getPublic();<br />PrivateKey prikey=keys.getPrivate();<br />分别保存在myprikey.dat和mypubkey.dat?以便下次不在生成<br />(生成密钥对的旉比较?br />java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));<br />out.writeObject(prikey);<br />out.close();<br />out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));<br />out.writeObject(pubkey);<br />out.close();</p> <p> </p> <p> <br />用他Uh密钥(prikey)对他所认的信?info)q行数字{֐产生一个签名数l?<br />从文件中dUh密钥(prikey) <br />java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));<br />PrivateKey myprikey=(PrivateKey)in.readObject();<br />in.close();<br />初始一个Signature对象,q用U钥对信息签?br />java.security.Signature signet=java.security.Signature.getInstance("DSA");<br />signet.initSign(myprikey);<br />signet.update(myinfo.getBytes());<br />byte[] signed=signet.sign();<br />把信息和{֐保存在一个文件中(myinfo.dat)<br />java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));<br />out.writeObject(myinfo);<br />out.writeObject(signed);<br />out.close();<br />把他的公钥的信息?qing)签名发l其它用?/p> <p> </p> <p> <br />其他用户用他的公共密?pubkey)和签?signed)和信?info)q行验证是否׃{֐的信?<br />d公钥<br />java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat")); <br />PublicKey pubkey=(PublicKey)in.readObject(); <br />in.close();</p> <p>d{֐和信?br />in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat")); <br />String info=(String)in.readObject(); <br />byte[] signed=(byte[])in.readObject(); <br />in.close();</p> <p>初始一个Signature对象,q用公钥和签名进行验?br />java.security.Signature signetcheck=java.security.Signature.getInstance("DSA"); <br />signetcheck.initVerify(pubkey); <br />signetcheck.update(info.getBytes()); <br />if (signetcheck.verify(signed)) { System.out.println("{֐正常");}</p> <p>对于密钥的保存本文是用对象流的方式保存和传送的,也可可以用编码的方式保存.注意?br />import java.security.spec.* <br />import java.security.*</p> <p>具休说明如下</p> <p>public key是用X.509~码?例码如下: <br />byte[] bobEncodedPubKey=mypublic.getEncoded(); //生成~码<br />//传送二q制~码<br />//以下代码转换~码为相应key对象<br />X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(bobEncodedPubKey);<br />KeyFactory keyFactory = KeyFactory.getInstance("DSA");<br />PublicKey bobPubKey = keyFactory.generatePublic(bobPubKeySpec);</p> <p> </p> <p>对于Private key是用PKCS#8~码,例码如下: <br />byte[] bPKCS=myprikey.getEncoded();<br />//传送二q制~码<br />//以下代码转换~码为相应key对象<br />PKCS8EncodedKeySpec priPKCS8=new PKCS8EncodedKeySpec(bPKCS);<br />KeyFactory keyf=KeyFactory.getInstance("DSA");<br />PrivateKey otherprikey=keyf.generatePrivate(priPKCS8);</p> <p> </p> <p> <br />常用API <br />java.security.KeyPairGenerator 密钥生成器类 <br />public static KeyPairGenerator getInstance(String algorithm) throws NoSuchAlgorithmException <br />以指定的法q回一个KeyPairGenerator 对象 <br />参数: algorithm 法??"DSA","RSA"</p> <p>public void initialize(int keysize)</p> <p> <br />以指定的长度初始化KeyPairGenerator对象,如果没有初始化系l以1024长度默认讄</p> <p> <br />参数:keysize 法位长.其范围必d 512 ?1024 之间Q且必须?64 的倍数</p> <p>public void initialize(int keysize, SecureRandom random)<br />以指定的长度初始化和随机发生器初始化KeyPairGenerator对象<br />参数:keysize 法位长.其范围必d 512 ?1024 之间Q且必须?64 的倍数<br />random 一个随Z的来?对于initialize(int keysize)使用了默认随机器</p> <p>public abstract KeyPair generateKeyPair()<br />产生新密钥对</p> <p>java.security.KeyPair 密钥对类<br />public PrivateKey getPrivate()<br />q回U钥</p> <p>public PublicKey getPublic()<br />q回公钥</p> <p>java.security.Signature {֐c?br />public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException<br />q回一个指定算法的Signature对象<br />参数 algorithm ?"DSA"</p> <p>public final void initSign(PrivateKey privateKey)<br />throws InvalidKeyException<br />用指定的U钥初始?br />参数rivateKey 所q行{֐时用的私?/p> <p>public final void update(byte data)<br />throws SignatureException<br />public final void update(byte[] data)<br />throws SignatureException<br />public final void update(byte[] data, int off, int len)<br />throws SignatureException </p> <p>d要签名的信息</p> <p>public final byte[] sign()<br />throws SignatureException<br />q回{֐的数l?前提是initSign和update</p> <p>public final void initVerify(PublicKey publicKey)<br />throws InvalidKeyException<br />用指定的公钥初始?br />参数ublicKey 验证时用的公?/p> <p>public final boolean verify(byte[] signature)<br />throws SignatureException<br />验证{֐是否有效,前提是已linitVerify初始?br />参数: signature {֐数组 <br />*/<br />import java.security.*;<br />import java.security.spec.*;<br />public class testdsa {<br />public static void main(String[] args) throws java.security.NoSuchAlgorithmException,java.lang.Exception {<br />testdsa my=new testdsa();<br />my.run();<br />}<br />public void run()<br />{</p> <p>//数字{֐生成密钥<br />//W一步生成密钥对,如果已经生成q?本过E就可以跌,对用h讲myprikey.dat要保存在本地<br />//而mypubkey.datl发布给其它用户<br />if ((new java.io.File("myprikey.dat")).exists()==false) {<br />if (generatekey()==false) {<br />System.out.println("生成密钥对|");<br />return;<br />};<br />}<br />//W二?此用?br />//从文件中dU钥,对一个字W串q行{֐后保存在一个文?myinfo.dat)?br />//q且再把myinfo.dat发送出?br />//Z方便数字{֐也放q了myifno.dat文g?当然也可分别发?br />try {<br />java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("myprikey.dat"));<br />PrivateKey myprikey=(PrivateKey)in.readObject();<br />in.close();</p> <p>// java.security.spec.X509EncodedKeySpec pubX509=new java.security.spec.X509EncodedKeySpec(bX509);</p> <p>//java.security.spec.X509EncodedKeySpec pubkeyEncode=java.security.spec.X509EncodedKeySpec<br />String myinfo="q是我的信息"; //要签名的信息<br />//用私钥对信息生成数字{֐<br />java.security.Signature signet=java.security.Signature.getInstance("DSA");<br />signet.initSign(myprikey);<br />signet.update(myinfo.getBytes());<br />byte[] signed=signet.sign(); //对信息的数字{֐<br />System.out.println("signed({֐内容)="+byte2hex(signed));<br />//把信息和数字{֐保存在一个文件中<br />java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myinfo.dat"));<br />out.writeObject(myinfo);<br />out.writeObject(signed);<br />out.close();<br />System.out.println("{֐q生成文件成?);<br />}<br />catch (java.lang.Exception e) {<br />e.printStackTrace();<br />System.out.println("{֐q生成文件失?);<br />};</p> <p>//W三?br />//其他人通过公共方式得到此户的公钥和文g<br />//其他人用此户的公?Ҏ(gu)件进行检?如果成功说明是此用户发布的信?<br />//<br />try {</p> <p>java.io.ObjectInputStream in=new java.io.ObjectInputStream(new java.io.FileInputStream("mypubkey.dat"));<br />PublicKey pubkey=(PublicKey)in.readObject();<br />in.close();<br />System.out.println(pubkey.getFormat());</p> <p>in=new java.io.ObjectInputStream(new java.io.FileInputStream("myinfo.dat"));<br />String info=(String)in.readObject();<br />byte[] signed=(byte[])in.readObject();<br />in.close();</p> <p>java.security.Signature signetcheck=java.security.Signature.getInstance("DSA");<br />signetcheck.initVerify(pubkey);<br />signetcheck.update(info.getBytes());<br />if (signetcheck.verify(signed)) {<br />System.out.println("info="+info);<br />System.out.println("{֐正常");<br />}<br />else System.out.println("非签名正?);<br />}<br />catch (java.lang.Exception e) {e.printStackTrace();};</p> <p> <br />}</p> <p>//生成一Ҏ(gu)件myprikey.dat和mypubkey.dat---U钥和公?<br />//公钥要用户发?文g,|络{方?l其它用?U钥保存在本?br />public boolean generatekey()<br />{<br />try {<br />java.security.KeyPairGenerator keygen=java.security.KeyPairGenerator.getInstance("DSA");<br />// SecureRandom secrand=new SecureRandom();<br />// secrand.setSeed("tttt".getBytes()); //初始化随Z生器<br />// keygen.initialize(576,secrand); //初始化密钥生成器<br />keygen.initialize(512);<br />KeyPair keys=keygen.genKeyPair();<br />// KeyPair keys=keygen.generateKeyPair(); //生成密钥l?br />PublicKey pubkey=keys.getPublic();<br />PrivateKey prikey=keys.getPrivate();</p> <p>java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("myprikey.dat"));<br />out.writeObject(prikey);<br />out.close();<br />System.out.println("写入对象 prikeys ok");<br />out=new java.io.ObjectOutputStream(new java.io.FileOutputStream("mypubkey.dat"));<br />out.writeObject(pubkey);<br />out.close();<br />System.out.println("写入对象 pubkeys ok");<br />System.out.println("生成密钥Ҏ(gu)?);<br />return true;<br />}<br />catch (java.lang.Exception e) {<br />e.printStackTrace();<br />System.out.println("生成密钥对失?);</p> <p>return false;<br />};</p> <p>}</p> <p>public String byte2hex(byte[] b)<br />{<br />String hs="";<br />String stmp="";<br />for (int n=0;n {<br />stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));<br />if (stmp.length()==1) hs=hs+"0"+stmp;<br />else hs=hs+stmp;<br />if (n }<br />return hs.toUpperCase();<br />}</p> <p>}</p> </div> <div> <div> <p>2.4. DESede/DES对称法 <br />首先生成密钥,q保?q里q没的保存的代码,可参考DSA中的Ҏ(gu))</p> <p>KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);</p> <p>SecretKey deskey = keygen.generateKey();</p> <p>用密钥加密明?myinfo),生成密文(cipherByte)</p> <p>Cipher c1 = Cipher.getInstance(Algorithm);</p> <p>c1.init(Cipher.ENCRYPT_MODE,deskey);</p> <p>byte[] cipherByte=c1.doFinal(myinfo.getBytes());</p> <p>传送密文和密钥,本文没有相应代码可参考DSA</p> <p>.............</p> <p>用密钥解密密?/p> <p>c1 = Cipher.getInstance(Algorithm);</p> <p>c1.init(Cipher.DECRYPT_MODE,deskey);</p> <p>byte[] clearByte=c1.doFinal(cipherByte);</p> <p>相对来说对称密钥的用是很简单的,对于JCE来讲支技DES,DESede,Blowfish三种加密?/p> <p>对于密钥的保存各传送可使用对象或者用二进制编?相关参考代码如?<br />SecretKey deskey = keygen.generateKey();<br />byte[] desEncode=deskey.getEncoded();<br />javax.crypto.spec.SecretKeySpec destmp=new javax.crypto.spec.SecretKeySpec(desEncode,Algorithm);<br />SecretKey mydeskey=destmp;</p> <p> </p> <p>相关API</p> <p>KeyGenerator 在DSA中已l说?在添加JCE后在instanceq可以如下参?/p> <p>DES,DESede,Blowfish,HmacMD5,HmacSHA1</p> <p>javax.crypto.Cipher ?解密?public static final Cipher getInstance(java.lang.String transformation)<br />throws java.security.NoSuchAlgorithmException,<br />NoSuchPaddingException<br />q回一个指定方法的Cipher对象</p> <p>参数:transformation Ҏ(gu)?可用 DES,DESede,Blowfish)</p> <p>public final void init(int opmode, java.security.Key key)<br />throws java.security.InvalidKeyException</p> <p>用指定的密钥和模式初始化Cipher对象</p> <p>参数pmode 方式(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)</p> <p>key 密钥</p> <p> <br />public final byte[] doFinal(byte[] input)<br />throws java.lang.IllegalStateException,<br />IllegalBlockSizeException,<br />BadPaddingException</p> <p> </p> <p> <br />对input内的?q行~码处理,q回处理后二q制?是返回解密文q是加解文由init时的opmode军_</p> <p>注意:本方法的执行前如果有update,是对updat和本ơinput全部处理,否则是本inout的内?/p> <p>/*<br />安全E序 DESede/DES试<br />*/<br />import java.security.*;<br />import javax.crypto.*;<br />public class testdes {<br />public static void main(String[] args){<br />testdes my=new testdes();<br />my.run();<br />}<br />public void run() {<br />//d新安全算?如果用JCEp把它dq去<br />Security.addProvider(new com.sun.crypto.provider.SunJCE());<br />String Algorithm="DES"; //定义 加密法,可用 DES,DESede,Blowfish<br />String myinfo="要加密的信息";<br />try {<br />//生成密钥<br />KeyGenerator keygen = KeyGenerator.getInstance(Algorithm);<br />SecretKey deskey = keygen.generateKey();</p> <p>//加密<br />System.out.println("加密前的二进?"+byte2hex(myinfo.getBytes()));<br />System.out.println("加密前的信息:"+myinfo);<br />Cipher c1 = Cipher.getInstance(Algorithm);<br />c1.init(Cipher.ENCRYPT_MODE,deskey);<br />byte[] cipherByte=c1.doFinal(myinfo.getBytes());<br />System.out.println("加密后的二进?"+byte2hex(cipherByte));<br />//解密<br />c1 = Cipher.getInstance(Algorithm);<br />c1.init(Cipher.DECRYPT_MODE,deskey);<br />byte[] clearByte=c1.doFinal(cipherByte);<br />System.out.println("解密后的二进?"+byte2hex(clearByte));<br />System.out.println("解密后的信息:"+(new String(clearByte)));</p> <p>}<br />catch (java.security.NoSuchAlgorithmException e1) {e1.printStackTrace();}<br />catch (javax.crypto.NoSuchPaddingException e2) {e2.printStackTrace();}<br />catch (java.lang.Exception e3) {e3.printStackTrace();}<br />}<br />public String byte2hex(byte[] b) //二行制{字符?br />{<br />String hs="";<br />String stmp="";<br />for (int n=0;n {<br />stmp=(java.lang.Integer.toHexString(b[n] & 0XFF));<br />if (stmp.length()==1) hs=hs+"0"+stmp;<br />else hs=hs+stmp;<br />if (n }<br />return hs.toUpperCase();<br />}</p> <p>}</p> <p> </p> <p>2.5. Diffie-Hellman密钥一致协?<br />公开密钥密码体制的奠ZhDiffie和Hellman所提出?"指数密钥一致协?(Exponential Key Agreement Protocol),该协议不要求别的安全?先决条g,允许两名用户在公开媒体上交换信息以生成"一??可以׃n的密钥。在JCE的中实现用户alice生成DHcd的密钥对,如果长度?024生成的时间请,推荐W一ơ生成后保存DHParameterSpec,以便下次使用直接初始?使其速度加快 </p> <p>System.out.println("ALICE: 产生 DH ?...");<br />KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");<br />aliceKpairGen.initialize(512);<br />KeyPair aliceKpair = aliceKpairGen.generateKeyPair();</p> <p> </p> <p>alice生成公钥发送组bob byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();</p> <p> </p> <p>bob从alice发送来的公钥中dDH密钥对的初始参数生成bob的DH密钥?/p> <p>注意q一步一定要?要保证每个用L(fng)相同的初始参数生成的 <br />DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();<br />KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");<br />bobKpairGen.initialize(dhParamSpec);<br />KeyPair bobKpair = bobKpairGen.generateKeyPair();</p> <p> </p> <p>bobҎ(gu)a(chn)lice的公钥生成本地的DES密钥<br />KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");<br />bobKeyAgree.init(bobKpair.getPrivate());<br />bobKeyAgree.doPhase(alicePubKey, true);<br />SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");</p> <p> </p> <p>bob已经生成了他的DES密钥,他现把他的公钥发lalice,<br />byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();</p> <p> </p> <p>aliceҎ(gu)bob的公钥生成本地的DES密钥<br />,,,,,,解码<br />KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");<br />aliceKeyAgree.init(aliceKpair.getPrivate());<br />aliceKeyAgree.doPhase(bobPubKey, true);<br />SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");</p> <p> </p> <p>bob和alice能过q个q程q成了相同的DES密钥,在这U基可q行安全能信</p> <p>常用API</p> <p>java.security.KeyPairGenerator 密钥生成器类<br />public static KeyPairGenerator getInstance(String algorithm)<br />throws NoSuchAlgorithmException<br />以指定的法q回一个KeyPairGenerator 对象<br />参数: algorithm 法??原来是DSA,现在d?DiffieHellman(DH)</p> <p>public void initialize(int keysize)<br />以指定的长度初始化KeyPairGenerator对象,如果没有初始化系l以1024长度默认讄<br />参数:keysize 法位长.其范围必d 512 ?1024 之间Q且必须?64 的倍数<br />注意:如果?024生长的时间很?最好生成一ơ后׃?下次׃用生成了</p> <p>public void initialize(AlgorithmParameterSpec params)<br />throws InvalidAlgorithmParameterException<br />以指定参数初始化</p> <p>javax.crypto.interfaces.DHPublicKey<br />public DHParameterSpec getParams()<br />q回<br />java.security.KeyFactory</p> <p>public static KeyFactory getInstance(String algorithm)<br />throws NoSuchAlgorithmException<br />以指定的法q回一个KeyFactory<br />参数: algorithm 法名SH,DH</p> <p>public final PublicKey generatePublic(KeySpec keySpec)<br />throws InvalidKeySpecException<br />Ҏ(gu)指定的key说明,q回一个PublicKey对象</p> <p>java.security.spec.X509EncodedKeySpec<br />public X509EncodedKeySpec(byte[] encodedKey)<br />Ҏ(gu)指定的二q制~码的字串生成一个key的说?br />参数:encodedKey 二进制编码的字串(一般能qPublicKey.getEncoded()生成)<br />javax.crypto.KeyAgreement 密码一至类</p> <p>public static final KeyAgreement getInstance(java.lang.String algorithm)<br />throws java.security.NoSuchAlgorithmException<br />q回一个指定算法的KeyAgreement对象<br />参数:algorithm 法?现在只能是DiffieHellman(DH)</p> <p>public final void init(java.security.Key key)<br />throws java.security.InvalidKeyException<br />用指定的U钥初始?br />参数:key 一个私?/p> <p>public final java.security.Key doPhase(java.security.Key key,<br />boolean lastPhase)<br />throws java.security.InvalidKeyException,<br />java.lang.IllegalStateException<br />用指定的公钥q行定位,lastPhase定q是否是最后一个公?对于两个用户?br />情况下就可以多次定次,最后确?br />参数:key 公钥<br />lastPhase 是否最后公?/p> <p>public final SecretKey generateSecret(java.lang.String algorithm)<br />throws java.lang.IllegalStateException,<br />java.security.NoSuchAlgorithmException,<br />java.security.InvalidKeyException<br />Ҏ(gu)指定的算法生成密?br />参数:algorithm 加密法(可用 DES,DESede,Blowfish)</p> <p> <br />*/<br />import java.io.*;<br />import java.math.BigInteger;<br />import java.security.*;<br />import java.security.spec.*;<br />import java.security.interfaces.*;<br />import javax.crypto.*;<br />import javax.crypto.spec.*;<br />import javax.crypto.interfaces.*;<br />import com.sun.crypto.provider.SunJCE;</p> <p>public class testDHKey {</p> <p> <br />public static void main(String argv[]) {<br />try {<br />testDHKey my= new testDHKey();<br />my.run();<br />} catch (Exception e) {<br />System.err.println(e);</p> <p>}<br />}</p> <p>private void run() throws Exception {<br />Security.addProvider(new com.sun.crypto.provider.SunJCE());</p> <p>System.out.println("ALICE: 产生 DH ?...");<br />KeyPairGenerator aliceKpairGen = KeyPairGenerator.getInstance("DH");<br />aliceKpairGen.initialize(512);<br />KeyPair aliceKpair = aliceKpairGen.generateKeyPair(); //生成旉?</p> <p>// 张三(Alice)生成公共密钥 alicePubKeyEnc q发送给李四(Bob) ,<br />//比如用文件方?socket.....<br />byte[] alicePubKeyEnc = aliceKpair.getPublic().getEncoded();</p> <p>//bob接收到alice的编码后的公?其解码<br />KeyFactory bobKeyFac = KeyFactory.getInstance("DH");<br />X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec (alicePubKeyEnc);<br />PublicKey alicePubKey = bobKeyFac.generatePublic(x509KeySpec);<br />System.out.println("alice公钥bob解码成功");<br />// bob必须用相同的参数初始化的他的DH KEY?所以要从Alice发给他的公开密钥,<br />//中读出参?再用q个参数初始化他?DH key?/p> <p>//从alicePubKye中取alice初始化时用的参数<br />DHParameterSpec dhParamSpec = ((DHPublicKey)alicePubKey).getParams();<br />KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");<br />bobKpairGen.initialize(dhParamSpec);<br />KeyPair bobKpair = bobKpairGen.generateKeyPair();<br />System.out.println("BOB: 生成 DH key Ҏ(gu)?);<br />KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");<br />bobKeyAgree.init(bobKpair.getPrivate());<br />System.out.println("BOB: 初始化本地key成功");<br />//李四(bob) 生成本地的密?bobDesKey<br />bobKeyAgree.doPhase(alicePubKey, true);<br />SecretKey bobDesKey = bobKeyAgree.generateSecret("DES");<br />System.out.println("BOB: 用alice的公钥定位本地key,生成本地DES密钥成功");<br />// Bob生成公共密钥 bobPubKeyEnc q发送给Alice,<br />//比如用文件方?socket.....,使其生成本地密钥<br />byte[] bobPubKeyEnc = bobKpair.getPublic().getEncoded();<br />System.out.println("BOB向ALICE发送公?);</p> <p>// alice接收?bobPubKeyEnc后生成bobPubKey<br />// 再进行定?使aliceKeyAgree定位在bobPubKey<br />KeyFactory aliceKeyFac = KeyFactory.getInstance("DH");<br />x509KeySpec = new X509EncodedKeySpec(bobPubKeyEnc);<br />PublicKey bobPubKey = aliceKeyFac.generatePublic(x509KeySpec);<br />System.out.println("ALICE接收BOB公钥q解码成?);<br />;<br />KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");<br />aliceKeyAgree.init(aliceKpair.getPrivate());<br />System.out.println("ALICE: 初始化本地key成功");</p> <p>aliceKeyAgree.doPhase(bobPubKey, true);<br />// 张三(alice) 生成本地的密?aliceDesKey<br />SecretKey aliceDesKey = aliceKeyAgree.generateSecret("DES");<br />System.out.println("ALICE: 用bob的公钥定位本地key,q生成本地DES密钥");</p> <p>if (aliceDesKey.equals(bobDesKey)) System.out.println("张三和李四的密钥相同");<br />//现在张三和李四的本地的deskey是相同的所?完全可以q行发送加?接收后解?辑ֈ<br />//安全通道的的目的</p> <p>/*<br />* bob用bobDesKey密钥加密信息<br />*/<br />Cipher bobCipher = Cipher.getInstance("DES");<br />bobCipher.init(Cipher.ENCRYPT_MODE, bobDesKey);<br />String bobinfo= "q是李四的机密信?;<br />System.out.println("李四加密前原?"+bobinfo);<br />byte[] cleartext =bobinfo.getBytes();<br />byte[] ciphertext = bobCipher.doFinal(cleartext);</p> <p>/*<br />* alice用aliceDesKey密钥解密<br />*/<br />Cipher aliceCipher = Cipher.getInstance("DES");<br />aliceCipher.init(Cipher.DECRYPT_MODE, aliceDesKey);<br />byte[] recovered = aliceCipher.doFinal(ciphertext);<br />System.out.println("alice解密bob的信?"+(new String(recovered)));<br />if (!java.util.Arrays.equals(cleartext, recovered))<br />throw new Exception("解密后与原文信息不同");<br />System.out.println("解密后相?);</p> <p>}</p> <p>}</p> <p> </p> <p> <strong>W??结 </strong> </p> <p> <strong> <br /> </strong>在加密术中生成密钥对Ӟ密钥对的当然是越长越好,但费时也多Q请从中从实际出发选取合适的长度Q大部分例码中的密钥是每ơ运行就从新生成Q在实际的情况中是生成后在一D|间保存在文g中,再次q行直接从文件中dQ从而加快速度。当然定时更新和加强密钥保管的安全性也是必ȝ?br /></p> </div> <br /> <br /> <div id="tflreog" class="dashed"> </div> <br />文章引用? <a target="_blank"><font color="#000033">http://www.nihaoblog.com/1_1751.html</font></a></div> <img src ="http://m.tkk7.com/zeroone0/aggbug/38829.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/zeroone0/" target="_blank">zeroone0</a> 2006-04-03 09:55 <a href="http://m.tkk7.com/zeroone0/articles/38829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAR文g包及(qing)jar命o(h)详解http://m.tkk7.com/zeroone0/articles/38826.htmlzeroone0zeroone0Mon, 03 Apr 2006 01:51:00 GMThttp://m.tkk7.com/zeroone0/articles/38826.htmlhttp://m.tkk7.com/zeroone0/comments/38826.htmlhttp://m.tkk7.com/zeroone0/articles/38826.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/38826.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/38826.html
  JAR 文g是 Java Archive FileQ顾名思意Q它的应用是与 Java 息息相关的,是 Java 的一U文档格式。JAR 文g非常cM ZIP 文g??准确的说Q它?yu)是 ZIP 文gQ所以叫它文件包。JAR 文g与 ZIP 文g唯一的区别就是在 JAR 文g的内容中Q包含了一个 META-INF/MANIFEST.MF 文gQ这个文件是在生成 JAR 文g的时候自动创建的。D个例子,如果我们h如下目录l构的一些文Ӟ(x) 

  == 

  `-- test 

    `-- Test.class 

  把它压羃成 ZIP 文g test.zipQ则q个 ZIP 文g的内部目录结构ؓ(f)Q?br />
  test.zip 

  `-- test 

    `-- Test.class 

  如果我们使用 JDK 的 jar 命o(h)把它打成 JAR 文g包 test.jarQ则q个 JAR 文g的内部目录结构ؓ(f)Q?br />
  test.jar 

  |-- META-INF 

  |  `-- MANIFEST.MF 

  `-- test 

    `--Test.class 

  2. 创徏可执行的 JAR 文g包?br />
  制作一个可执行的 JAR 文g包来发布你的E序是 JAR 文g包最典型的用法。?br />
  Java E序是由若干个?class 文gl成的。这些?class 文g必须Ҏ(gu)它们所属的包不同而分U分目录存放Q运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命o(h)的?cp 参数Q运行时q要到控制台下去使用 java 命o(h)来运行,如果需要直接双击运行必d Windows 的批处理文g (.bat) 或者 Linux 的 Shell E序。因此,许多QJava 是一U方便开发者苦了用L(fng)E序设计语言。?br />
  其实不然Q如果开发者能够制作一个可执行的 JAR 文g包交l用P那么用户使用h方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候,安装文g?x)将?jar 文g映射l javaw.exe 打开。那么,对于一个可执行的 JAR 文g包,用户只需要双d?yu)可以运行程序了Q和阅读 .chm 文档一h便?.chm 文档默认是由 hh.exe 打开?。那么,现在的关键,是如何来创个可执行的 JAR 文g包。?br />
  创徏可执行的 JAR 文g包,需要用带 cvfm 参数的 jar 命o(h)Q同样以上述 test 目录ZQ命令如下:(x) 

jar cvfm test.jar manifest.mf test 

  q里 test.jar 和 manifest.mf 两个文gQ分别是对应的参敊Wf 和 mQ其重头戏在 manifest.mf。因创徏可执行的 JAR 文g包,光靠指定一个 manifest.mf 文g是不够的Q因为 MANIFEST 是 JAR 文g包的特征Q可执行的 JAR 文g包和不可执行的 JAR 文g包都包含 MANIFEST。关键在于可执行 JAR 文g包的 MANIFESTQ其内容包含了 Main-Class 一V这在 MANIFEST 中书写格式如下:(x) 

  Main-Class: 可执行主cd?包含包名) 

  例如Q假设上例中的 Test.class 是属于 test 包的Q而且是可执行的类 (定义了 public static void main(String[]) Ҏ(gu))Q那么这个 manifest.mf 可以~辑如下Q?br />
Main-Class: test.Test <回R> 

  q个 manifest.mf 可以攑֜M位置Q也可以是其它的文g名,只需要有 Main-Class: test.Test 一行,且该行以一个回车符l束卛_。创Z manifest.mf 文g之后Q我们的目录l构变ؓ(f)Q?br />
  == 

  |-- test 

  |  `-- Test.class 

  `-- manifest.mf 

  q时候,需要到 test 目录的上U目录中M用 jar 命o(h)来创建 JAR 文g包。也是在目录树(wi)中用?=”表C的那个目录中,使用如下命o(h)Q?br />
jar cvfm test.jar manifest.mf test 

  之后在?=”目录中创徏了 test.jarQ这个 test.jar 是执行的 JAR 文g包。运行时只需要用 java -jar test.jar 命o(h)卛_。?br />
  需要注意的是,创徏的 JAR 文g包中需要包含完整的、与 Java E序的包l构对应的目录结构,像上例一栗而 Main-Class 指定的类Q也必须是完整的、包含包路径的类名,如上例的 test.TestQ而且在没有打成 JAR 文g包之前可以用 java <cd> 来运行这个类Q即在上例中 java test.Test 是可以正运行的 (当然要在 CLASSPATH 正确的情况下)?br />
  3. jar 命o(h)详解 

  jar 是随 JDK 安装的,在 JDK 安装目录下的 bin 目录中,W(xu)indows 下文件名为 jar.exeQLinux 下文件名为 jar。它的运行需要用刊WJDK 安装目录下 lib 目录中的 tools.jar 文g。不q我们除了安装 JDK 什么也不需要做Q因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 攑ֈ CLASSPATH 中。?br />
  使用不带M的 jar 命o(h)我们可以看到 jar 命o(h)的用法如下:(x) 

  jar {ctxu}[vfm0M] [jar-文g] [manifest-文g] [-C 目录] 文g名?.. 

  其中 {ctxu} 是 jar 命o(h)的子命o(h)Q每ơ jar 命o(h)只能包含 ctxu 中的一个,它们分别表示Q?br />
   -c 创徏新的 JAR 文g包?br />
   -t 列出 JAR 文g包的内容列表 

   -x 展开 JAR 文g包的指定文g或者所有文件?br />
   -u 更新已存在的 JAR 文g包?d文g刊WJAR 文g包中) 

     [vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命o(h)的选项参数 

   -v 生成详细报告q打印到标准输出 

   -f 指定 JAR 文g名,通常q个参数是必ȝ 

   -m 指定需要包含的 MANIFEST 清单文g 

   -0 只存储,不压~,q样产生的 JAR 文g包会(x)比不用该参数产生的体U大Q但速度更快 

   -M 不生所有项的清单(MANIFEST〕文Ӟ此参C(x)忽略 -m 参数 

     [jar-文g] 即需要生成、查看、更新或者解开的 JAR 文g包,它是 -f 参数的附属参敊W?br />
     [manifest-文g] ꐠMANIFEST 清单文gQ它是?m 参数的附属参敊W?br />
     [-C 目录] 表示转到指定目录下去执行q个 jar 命o(h)的操作。它相当于先使用 cd 命o(h)转该目录下再执行不带 -C 参数的 jar 命o(h)Q它只能在创建和更新 JAR 文g包的时候可用。   

  文g名?.. 指定一个文?目录列表Q这些文?目录是要添加到 JAR 文g包中的文?目录。如果指定了目录Q那么 jar 命o(h)打包的时候会(x)自动把该目录中的所有文件和子目录打入包中。?br />
  下面举一些例子来说明 jar 命o(h)的用法:(x) 

  1) jar cf test.jar test 

  该命令没有执行过E的昄Q执行结果是在当前目录生成了 test.jar 文g。如果当前目录已l存在 test.jarQ那么该文g被覆盖。?br />
  2) jar cvf test.jar test 

  该命令与上例中的l果相同Q但是由于 v 参数的作用,昄Z打包q程Q如下:(x) 

  标明清单(manifest) 

  增加Qtest/(d= 0) (写出= 0)(存储了?%) 

  增加Qtest/Test.class(d= 7) (写出= 6)(压羃了?4%) 

  3) jar cvfM test.jar test 

  该命令与 2) l果cMQ但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文gQ打包过E的信息也略有差别:(x) 

  增加Qtest/(d= 0) (写出= 0)(存储了?%) 

  增加Qtest/Test.class(d= 7) (写出= 6)(压羃了?4%) 

  4) jar cvfm test.jar manifest.mf test 

  q行l果与?) 怼Q显CZ息也相同Q只是生成 JAR 包中的 META-INF/MANIFEST 内容不同Q是包含了 manifest.mf 的内容?br />
  5) jar tf test.jar 

  在 test.jar 已经存在的情况下Q可以查看 test.jar 中的内容Q如对于 2) 和?) 生成的 test.jar 分别应该此命令,l果如下Q?br />
  对于 2) 

  META-INF/ 

  META-INF/MANIFEST.MF 

  test/ 

  test/Test.class 

  对于 3) 

  test/ 

  test/Test.class 

  6) jar tvf test.jar 

  除显C?) 中显C的内容外,q包括包内文件的详细信息Q如Q?br />
  0 Wed Jun 19 15:39:06 GMT 2002 META-INF/ 

  86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF 

  0 Wed Jun 19 15:33:04 GMT 2002 test/ 

  7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class 

  7) jar xf test.jar 

  解开 test.jar 到当前目录,不显CZQ何信息,对于 2) 生成的 test.jarQ解开后的目录l构如下Q?br />
  == 

  |-- META-INF 

  |  `-- MANIFEST 

  `-- test 

    `--Test.class 

jar xvf test.jar 

  q行l果与?) 相同Q对于解压过E有详细信息昄Q如Q?br />
  创徏QMETA-INF/ 

  展开QMETA-INF/MANIFEST.MF 

  创徏Qtest/ 

  展开Qtest/Test.class 

  9) jar uf test.jar manifest.mf 

  在 test.jar 中添加了文g manifest.mfQ此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下,如果使用 -m 参数q指定 manifest.mf 文gQ那么 manifest.mf 是作为清单文件 MANIFEST 来用的Q它的内容会(x)被添加到 MANIFEST 中;但是Q如果作Z般文件添加到 JAR 文g包中Q它跟一般文件无异。?br />
  10) jar uvf test.jar manifest.mf 

  与?) l果相同Q同时有详细信息昄Q如Q?br />
  增加Qmanifest.mf(d= 17) (写出= 19)(压羃了?11%) 

  4. 关于 JAR 文g包的一些技巧?br />
  1) 使用 unzip 来解压 JAR 文g 

  在介l JAR 文g的时候就已经说过了,JAR 文g实际上就是 ZIP 文gQ所以可以用常见的一些解压 ZIP 文g的工h解压 JAR 文gQ如 Windows 下的 WinZip、WinRAR {和 Linux 下的 unzip {。用 WinZip 和 WinRAR {来解压是因为它们解压比较直观,方便。而用 unzipQ则是因为它解压时可以用?d 参数指定目标目录。?br />
  在解压一个 JAR 文g的时候是不能使用 jar 的?C 参数来指定解压的目标的,因ؓ(f) -C 参数只在创徏或者更新包的时候可用。那么需要将文g解压到某个指定目录下的时候就需要先这具 JAR 文g拯到目标目录下Q再q行解压Q比较麻烦。如果用 unzipQ就不需要这么麻烦了Q只需要指定一个?d 参数卛_。如Q?br />
  unzip test.jar -d dest/ 

  2) 使用 WinZip 或者 WinRAR {工具创建 JAR 文g 

  上面提到 JAR 文g是包含了 META-INF/MANIFEST 的 ZIP 文gQ所以,只需要用 WinZip、WinRAR {工具创建所需要 ZIP 压羃包,再往q个 ZIP 压羃包中d一个包含 MANIFEST 文g的 META-INF 目录卛_。对于用 jar 命o(h)的?m 参数指定清单文g的情况,只需要将q个 MANIFEST 按需要修改即可。?br />
  3) 使用 jar 命o(h)创徏 ZIP 文g 

  有些 Linux 下提供了 unzip 命o(h)Q但没有 zip 命o(h)Q所以需要可以对 ZIP 文gq行解压Q即不能创徏 ZIP 文g。如要创Z个 ZIP 文gQ用带 -M 参数的 jar 命o(h)卛_Q因为?M 参数表示制作 JAR 包的时候不d MANIFEST 清单Q那么只需要在指定目标 JAR 文g的地方将 .jar 扩展名改为?zip 扩展名,创徏的就是一个不折不扣的 ZIP 文g了,如将上一节的W?) 个例子略作改动:(x) 

  jar cvfM test.zip test 


zeroone0 2006-04-03 09:51 发表评论
]]>
Java Mail~程http://m.tkk7.com/zeroone0/articles/38822.htmlzeroone0zeroone0Mon, 03 Apr 2006 01:49:00 GMThttp://m.tkk7.com/zeroone0/articles/38822.htmlhttp://m.tkk7.com/zeroone0/comments/38822.htmlhttp://m.tkk7.com/zeroone0/articles/38822.html#Feedback0http://m.tkk7.com/zeroone0/comments/commentRss/38822.htmlhttp://m.tkk7.com/zeroone0/services/trackbacks/38822.html1.介绍:

Java Mail API的开发是SUN为Java开发者提供公用API框架的持l努力的良好例证。提倡公用框Ӟ反对受限于供应商的解x案,充分预示着一个日益开攄开发环境的建立?br />  Java Mail API的结构本w证明了它的开发者的基本目标之一--软g开发的工作量应该取决于应用E序本n的复杂程度以?qing)开发者所要求的控制程度。换句话_(d)Java Mail API可能地保持单。乍看v来,JavaMail API所拥有的类L以及(qing)cM间的关系可能让h误解p漫长的学?fn)时间。实际上Q一旦正式开始用,你就?x)发现该API不失为在应用E序中加入健壮的邮g/通讯支持的简单工兗?

2.安装:

安装前要保你的机子上安装得有标准版的JDK和W(xu)eb服务?q且已配|好,有关它们的安装方?请参考其它文?|上到处都有).

(1).安装JavaMail API。现在最常用?JavaMail API 版本?.3.
  要?JavaMail 1.3 APIQ请下蝲 JavaMail 1.3 实现Q解开Javamail-1_3.zip 文gQƈ?mail.jar 文gd?CLASSPATH 中。除了核心类Q随版本 1.3 实现一h供的q有 SMTP、IMAP4 ?POP3 供应商?br />  
(2).JavaBeans Activation Framework(1.0.2? 的安?br />JavaMail API 的所有版本都需?JavaBeans Activation Framework 来支持Q意数据块的输入及(qing)相应处理。功能似乎不多,但目前许多浏览器和邮件工具中都能扑ֈq种基本?MIME 型支持。下载完框架后,解开 jaf1_0_2.zip 文gQƈ?activation.jar 文gd?CLASSPATH 中?br />  

? 如果(zhn)用的JDK是J2EEQ就没有什么特定的事非要用基本 JavaMail API来做不可QJ2EE 的类p处理?因ؓ(f)它本w就包含有JavaMail API和JAF,(zhn)只需要确?j2ee.jar 文gd到?zhn)的CLASSPATH 中ƈ已全部设|好?br />
3.JavaMail的常用类介绍

事先说明:

没用qJavaMail的h可能看不懂这些介l?不过不要?后面的文章有具体的实?到时也可倒回来查看这些类的用?


(1) javax.mail.Propertiesc?br />  JavaMail需要Properties来创Z个session对象。它?yu)寻扑֭W串"mail.smtp.host"Q属性值就是发送邮件的L.

用法:
 Properties props = new Properties ();
   props.put("mail.smtp.host", "smtp.163.com");//可以换上你的smtpL名?br />

(2) javax.mail.Sessionc?br />  q个SessioncM表JavaMail 中的一个邮件session. 每一个基?JavaMail的应用程序至有一个session但是可以有Q意多的session?在这个例子中, Session对象需要知道用来处理邮件的SMTP 服务器?br />
用法:
   Session sendMailSession;
   sendMailSession = Session.getInstance(props, null);


(3) javax.mail.Transportc?br />  邮g是既可以被发送也可以被受到。JavaMail使用了两个不同的cL完成q两个功能:(x)Transport 和Store. Transport 是用来发送信息的Q而Store用来收信。对于这的教E我们只需要用到Transport对象?br />
用法Q?br />Transport transport;
  transport = sendMailSession.getTransport("smtp");

  用JavaMail Session对象的getTransport Ҏ(gu)来初始化Transport。传q去的字W串x了对象所要用的协议Q如"smtp"。这ؓ(f)我们省了很多旉。因为JavaMail以境内置了很多协议的实现Ҏ(gu)?br />
 注意: JavaMailq不是绝Ҏ(gu)持每一个协议,目前支持IMAP?SMTP?POP3.


(4) javax.mail.MimeMessagec?br />  Message对象存储我们实际发送的?sh)子邮g信息QMessage对象被作Z个MimeMessage对象来创建ƈ且需要知道应当选择哪一个JavaMail session?br />
  用法Q?br />Message newMessage = new MimeMessage(sendMailSession);


(5) javax.mail.InternetAddressc?br />一旦?zhn)创徏?Session ?MessageQƈ内容填入消息后Q就可以用Address定信g地址了。和 Message 一PAddress 也是个抽象类。?zhn)用的是Javax.mail.internet.InternetAddress c?

用法:
InternetAddress from=new InternetAddress("xxf@cafe.com");


(6) javax.mail.Storec?br />Storecd现特定邮件协议上的读、写、监视、查扄操作。通过Javax.mail.Storecd以访问Javax.mail.FoldercR?br />
用法:
Store store=s.getSorte("pop3");//sZ个邮件会(x)?br />store.connect(popserver,username,password);//通过你提供的pop地址,用户名和密码d你的邮箱


(7) javax.mail.Folderc?br />Foldercȝ于分U组l邮Ӟq提供照Javax.mail.Message格式讉Kemail的能力?

用法:
Folder folder=store.getFolder("INBOX");
folder.open(Folder.READ_ONLY);


(8) javax.mail.Internet.MimeMultpart
一般保存电(sh)子邮件内容的容器是Multipart抽象c?它定义了增加和删除及(qing)获得?sh)子邮g不同部分内容的方?׃Multipart是抽象类,我们必须为它使用一个具体的子类,JavaMail API提供javax.mail.Internet.MimeMultpartcL使用MimeMessage对象.

用法:
MimeMultipart multipart=new MimeMultipart();

?我们使用MimeMultipart对象的一个方法是addBodyPart(),它在我们的电(sh)子邮件内定wdBodyPart(BodyPartcd下面紧接着要介l?对象.消息可以有很多部?一个BodyPart可以代表一个部?


(9) javax.mail.Internet.MimeBodyPartc?br />
MimeBodyPart是BodyPart具体用于mimeMessage的一个子c?
MimeBodyPart对象代表一个MimeMessage对象内容的一部分.每个MimeBodyPart被认为有两部?
⊙一个MIMEcd
⊙匹配这个类型的内容

用法:
MimeBodyPart mdp=new MimeBodyPart();
String text="Hello JavaMail!";
mdp.setContent(text,"text/plain");//定义MIMEcd为text/plain,q设|MimeBodyPart的内?


(10) javax.activation.DataHandlerc?包含在JAF?
JavaMail API不限制信息只为文?M形式的信息都可能作茧(do)自缚(x)MimeMessage的一部分.除了文本信息,作ؓ(f)文g附g包含在电(sh)子邮件信息的一部分是很普遍?JavaMail API通过使用DataHandler对象,提供一个允许我们包含非文本BodyPart对象的简便方?

用法:
DataHandler dh=new DataHandler(text,type);
mdp.setDatahandler(dh);//mdp是一个MimeBodyPart对象


(11) javax.activation.FileDataSourcec?包含在JAF?
一个FileDataSource对象可以表示本地文g和服务器可以直接讉K的资?一个本地文件可以通过创徏一个新的MimeBodyPart对象附在一个mimeMessage对象?

用法:
MimeMultipart mm=new MimeMultipart();
MimeBodyPart mdp=new MimeBodyPart();
FileDataSource fds=new FileDataSource("c:/exam.txt");
mdp.setDataHandler(new DataHandler(fds)); //讄数据?br />mm.addBodyPart(mdp); //为当前消息MimeMultipart对象增加MimeBodyPart


(12) javax.activation.URLDataSourcec?包含在JAF?
q程资源,URL不会(x)指向它们,׃个URLDataSource对象表示.一个远E资源可以通过创徏一个新mimeBodyPart对象附在一个mimeMessage对象?同FileDataSource差不?.

用法:
与FileDataSource唯一不同的是数据源的讄:
URLDataSource uds=new URLDataSource(http://www.cnjsp.com/logo.gif);

4.试着~写W一个发送程?/div>
在前面我们已对JavaMail作了一些介l?下面我们可试着写自qE序?
首先,我们先写一个撰写邮件的htmlE序index.htm,如下:
-------------------------------------------------------------------------------------------
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>撰写邮g</title>
</head>
<body>
<form name="form1" method="post" action="testmail.jsp">
<table width="75" border="0" align="center" cellspacing="1" bgcolor="#006600" class="black">
<tr bgcolor="#FFFFFF">
<td width="24%">收信人地址:</td>
<td width="76%">
<input name="to" type="text" id="to"></td>
</tr>
<tr bgcolor="#FFFFFF">
<td>主题:</td>
<td>
<input name="title" type="text" id="title"></td>
</tr>
<tr>
<td height="107" colspan="2" bgcolor="#FFFFFF">
<textarea name="content" cols="50" rows="5" id="content"></textarea></td>
</tr>
<tr align="center">
<td colspan="2" bgcolor="#FFFFFF">
<input type="submit" name="Submit" value="发?>
<input type="reset" name="Submit2" value="重置">
</td>
</tr>
</table>
</form>
</body>
</html>

接着,我们再写一个处理程序testmail.jsp,如下:
-----------------------------------------------------------------------------------------
<%@ page contentType="text/html;charset=GB2312" %>
<%request.setCharacterEncoding("gb2312");%><!--中文处理代码-->
<!--引入要用到的cd-->
<%@ page import="java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>发送成?lt;/title>
</head>
<body>
<%
try{
//从html表单中获取邮件信?br />String tto=request.getParameter("to");
String ttitle=request.getParameter("title");
String tcontent=request.getParameter("content");
Properties props=new Properties();//也可用Properties props = System.getProperties();
props.put("mail.smtp.host","smtp.163.net");//存储发送邮件服务器的信?br />props.put("mail.smtp.auth","true");//同时通过验证
Session s=Session.getInstance(props);//Ҏ(gu)属性新Z个邮件会(x)?br />s.setDebug(true);
MimeMessage message=new MimeMessage(s);//由邮件会(x)话新Z个消息对?/div>
//讄邮g
InternetAddress from=new InternetAddress("boy@163.net");
message.setFrom(from);//讄发g?br />InternetAddress to=new InternetAddress(tto);
message.setRecipient(Message.RecipientType.TO,to);//讄收g?q设|其接收cd为TO
message.setSubject(ttitle);//讄主题
message.setText(tcontent);//讄信g内容
message.setSentDate(new Date());//讄发信旉
//发送邮?br />message.saveChanges();//存储邮g信息
Transport transport=s.getTransport("smtp");
transport.connect("smtp.163.net","boy","iloveyou");//以smtp方式d邮箱
transport.sendMessage(message,message.getAllRecipients());//发送邮?其中W二个参数是所?br />//已设好的收g人地址
transport.close();
%>
<div align="center">
<p><font color="#FF6600">发送成?</font></p>
<p><a href="recmail.jsp">ȝ看我的信?lt;/a><br>
<br>
<a href="index.htm">再发一?lt;/a> </p>
</div>
<%
}catch(MessagingException e){
out.println(e.toString());
}
%>
</body>
</html>
**********************************注意***************************************
有好多书上和|上的文章在关键部分都是q样写testmail.jsp?如下:
String tto=request.getParameter("to");
String ttitle=request.getParameter("title");
String tcontent=request.getParameter("content");
Properties props=new Properties();
props.put("mail.smtp.host","smtp.163.net");
Session s=Session.getInstance(props);
MimeMessage message=new MimeMessage(s);
InternetAddress from=new InternetAddress("boy@163.net");
message.setFrom(from);
InternetAddress to=new InternetAddress(tto);
message.setRecipient(Message.RecipientType.TO,to);
message.setSubject(ttitle);
message.setText(tcontent);
message.setSentDate(new Date());
Store store=s.getStore("pop3");
store.connect("pop.163.net","boy","iloveyou");//以pop3的方式登录邮?br />Transport transport=s.getTransport("smtp");
transport.send(message);
store.close();
事实?q种方式q不可靠,因ؓ(f)很多?sh)子邮局的smtp服务器要求我们通过验证,所以用q种方式发邮件时,只能发给同类邮箱(即相同smtp的邮?,甚至有时同类邮箱也发不出?以上两种方式我试q很多次,l果证明W一U方式是最可靠?

好了,我相信你应该?x)写最单的Email发送程序了.OK,下一ơ我们将说说怎样写发送HTML格式的邮?
5.发送HTML格式的邮?/div>
所谓HTML格式,是文本格?你的邮g可以用HTML代码~写,发给Ҏ(gu)?Ҏ(gu)收到的将是信息将是超文本,文本比U文本好看多?下以面是在以前例子的基础上修改的E序:
<%@ page contentType="text/html;charset=GB2312" %>
<%request.setCharacterEncoding("gb2312");%>
<%@ page import="java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>发送成?lt;/title>
</head>
<body>
<%
try{
String tto=request.getParameter("to");
String ttitle=request.getParameter("title");
String tcontent=request.getParameter("content");
Properties props=new Properties();
props.put("mail.smtp.host","127.0.0.1");
props.put("mail.smtp.auth","true");
Session s=Session.getInstance(props);
s.setDebug(true);
MimeMessage message=new MimeMessage(s);
//l消息对象设|发件h/收g?主题/发信旉
InternetAddress from=new InternetAddress("xxf@cafe.com");
message.setFrom(from);
InternetAddress to=new InternetAddress(tto);
message.setRecipient(Message.RecipientType.TO,to);
message.setSubject(ttitle);
message.setSentDate(new Date());

//l消息对象设|内?br />BodyPart mdp=new MimeBodyPart();//新徏一个存放信件内容的BodyPart对象
mdp.setContent(tcontent,"text/html;charset=gb2312");//lBodyPart对象讄内容和格?~码方式
Multipart mm=new MimeMultipart();//新徏一个MimeMultipart对象用来存放BodyPart?br />//?事实上可以存攑֤?
mm.addBodyPart(mdp);//BodyPart加入到MimeMultipart对象?可以加入多个BodyPart)
message.setContent(mm);//把mm作ؓ(f)消息对象的内?/div>
message.saveChanges();
Transport transport=s.getTransport("smtp");
transport.connect("127.0.0.1","xxf","coffee");
transport.sendMessage(message,message.getAllRecipients());
transport.close();
%>
<div align="center">
<p><font color="#FF6600">发送成?</font></p>
<p><a href="recmail.jsp">ȝ看我的信?lt;/a><br>
<br>
<a href="index.htm">再发一?lt;/a> </p>
</div>
<%
}catch(MessagingException e){
out.println(e.toString());
}
%>
</body>
</html
6.发送三U类型的附g
前面我们已学?x)了发送一般文本邮件和文本邮?今天我们让大家学会(x)~写三种cd的附件的邮g
发送程?(?撰写界面仍然用前面的)
<%@ page contentType="text/html;charset=GB2312" %>
<%request.setCharacterEncoding("gb2312");%>
<%@ page import="java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*"%>
<%@ page import="javax.activation.*"%><!--要发送附件必d入该?->
<%@ page import="java.net.*"%><!--要用到URLc?->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>发送成?lt;/title>
</head>
<body>
<%
try{
String tto=request.getParameter("to");
String ttitle=request.getParameter("title");
String tcontent=request.getParameter("content");
Properties props=new Properties();
props.put("mail.smtp.host","127.0.0.1");
props.put("mail.smtp.auth","true");
Session s=Session.getInstance(props);
s.setDebug(true);
MimeMessage message=new MimeMessage(s);
//l消息对象设|发件h/收g?主题/发信旉
InternetAddress from=new InternetAddress("xxf@cafe.com");
message.setFrom(from);
InternetAddress to=new InternetAddress(tto);
message.setRecipient(Message.RecipientType.TO,to);
message.setSubject(ttitle);
message.setSentDate(new Date());
Multipart mm=new MimeMultipart();//新徏一个MimeMultipart对象用来存放多个BodyPart对象
//讄信g文本内容
BodyPart mdp=new MimeBodyPart();//新徏一个存放信件内容的BodyPart对象
mdp.setContent(tcontent,"text/html;charset=gb2312");//lBodyPart对象讄内容和格?~码方式
mm.addBodyPart(mdp);//含有信件内容的BodyPart加入到MimeMultipart对象?/div>
//讄信g的附?(自定义附?直接所设文本内容加到自定义文g中作为附件发?
mdp=new MimeBodyPart();//新徏一个存N件的BodyPart
DataHandler dh=new DataHandler("JavaMail附g试","text/plain;charset=gb2312");
//新徏一个DataHandler对象,q设|其内容和格?~码方式
mdp.setFileName("xxf.txt");//加上q句作为附件发?否则作Z件的文本内容
mdp.setDataHandler(dh);//lBodyPart对象讄内容为dh
mm.addBodyPart(mdp);//含有附件的BodyPart加入到MimeMultipart对象?/div>
//讄信g的附?(用本C的文件作为附?
mdp=new MimeBodyPart();
FileDataSource fds=new FileDataSource("g:/xx.txt");
dh=new DataHandler(fds);
mdp.setFileName("dd.txt");//可以和原文g名不一?br />mdp.setDataHandler(dh);
mm.addBodyPart(mdp);
//讄信g的附?(用远E文件作为附?
mdp=new MimeBodyPart();
URLDataSource ur=new URLDataSource(new URL("http://localhost:8080/jspstudy/email/xx.gif"));
//?q里用的参数只能为URL对象,不能为URL字串,在前面类介绍时有?误?,q里U正一?
dh=new DataHandler(ur);
mdp.setFileName("ss.txt");
mdp.setDataHandler(dh);
mm.addBodyPart(mdp);
message.setContent(mm);//把mm作ؓ(f)消息对象的内?/div>
message.saveChanges();
Transport transport=s.getTransport("smtp");
transport.connect("127.0.0.1","xxf","coffee");
transport.sendMessage(message,message.getAllRecipients());
transport.close();
%>
<div align="center">
<p><font color="#FF6600">发送成?</font></p>
<p><a href="recmail.jsp">ȝ看我的信?lt;/a><br>
<br>
<a href="index.htm">再发一?lt;/a> </p>
</div>
<%
}catch(MessagingException e){
out.println(e.toString());
}
%>
</body>
</html>
?~写灉|的发送程?/div>
本小节没加什么新鲜的东西,但是l合了以前的所有内?可以让你灉|地发送你惌发的邮g.看了本小节之后对你会(x)感觉到非常有?
更改后的撰写界面E序如下:
-------------------------------------------------------------------------------------------
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>撰写邮g</title>
</head>
<body>
<form action="testall.jsp" method="post" name="form1">
<table width="75" border="0" align="center" cellspacing="1" bgcolor="#006600" class="black">
<tr bgcolor="#FFFFFF">
<td width="24%">收信人地址:</td>
<td width="76%"> <input name="to" type="text" id="to"></td>
</tr>
<tr bgcolor="#FFFFFF">
<td>主题:</td>
<td> <input name="title" type="text" id="title"></td>
</tr>
<tr>
<td height="18" colspan="2" bgcolor="#FFFFFF">信gcd
<select name="emailtype" id="emailtype">
<option value="text/plain" selected>Text</option>
<option value="text/html">Html</option>
</select></td>
</tr>
<tr>
<td height="53" colspan="2" bgcolor="#FFFFFF"><textarea name="content" cols="50" rows="5" id="content"></textarea></td>
</tr>
<tr align="center">
<td colspan="2" bgcolor="#FFFFFF">附g1(自定?:
<input name="fj1" type="text" id="fj1">
(输入文本信息) </td>
</tr>
<tr align="center" valign="bottom">
<td colspan="2" bgcolor="#FFFFFF">附g2(本地):
<input name="fj2" type="file" id="fj2" size="10"></td>
</tr>
<tr align="center">
<td colspan="2" bgcolor="#FFFFFF">附g3(q程):
<input name="fj3" type="text" id="fj3" value="http://">
(输入URL)</td>
</tr>
<tr align="center">
<td colspan="2" bgcolor="#FFFFFF"> <input type="submit" name="Submit" value="发?>
<input type="reset" name="Submit2" value="重置"></td>
</tr>
</table>
</form>
</body>
</html>

处理邮g的JSPE序如下:
----------------------------------------------------------------------------------------
<%@ page contentType="text/html;charset=GB2312" %>
<%request.setCharacterEncoding("gb2312");%>
<%@ page import="java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*"%>
<%@ page import="javax.activation.*"%><!--要发送附件必d入该?->
<%@ page import="java.net.*"%><!--要用到URLc?->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>发送成?lt;/title>
</head>
<body>
<%
try{
String tto=request.getParameter("to");
String ttitle=request.getParameter("title");
String emailtype=request.getParameter("emailtype");//获取emailcd
String tcontent=request.getParameter("content");
String tfj1=request.getParameter("fj1");
String tfj2=request.getParameter("fj2");
String tfj3=request.getParameter("fj3");
Properties props=new Properties();
props.put("mail.smtp.host","127.0.0.1");
props.put("mail.smtp.auth","true");
Session s=Session.getInstance(props);
s.setDebug(true);
MimeMessage message=new MimeMessage(s);
//l消息对象设|发件h/收g?主题/发信旉
InternetAddress from=new InternetAddress("xxf@cafe.com");
message.setFrom(from);
InternetAddress to=new InternetAddress(tto);
message.setRecipient(Message.RecipientType.TO,to);
message.setSubject(ttitle);
message.setSentDate(new Date());
Multipart mm=new MimeMultipart();//新徏一个MimeMultipart对象用来存放多个BodyPart对象
//讄信g文本内容
BodyPart mdp=new MimeBodyPart();//新徏一个存放信件内容的BodyPart对象
mdp.setContent(tcontent,emailtype+";charset=gb2312");//lBodyPart对象讄内容和格?~码方式
mm.addBodyPart(mdp);//含有信件内容的BodyPart加入到MimeMultipart对象?/div>
//讄信g的附?(自定义附?直接所设文本内容加到自定义文g中作为附件发?
mdp=new MimeBodyPart();//新徏一个存N件的BodyPart
DataHandler dh=new DataHandler(tfj1,"text/plain;charset=gb2312");
//新徏一个DataHandler对象,q设|其内容和格?~码方式
mdp.setFileName("text.txt");//加上q句作为附件发?否则作Z件的文本内容
mdp.setDataHandler(dh);//lBodyPart对象讄内容为dh
mm.addBodyPart(mdp);//含有附件的BodyPart加入到MimeMultipart对象?/div>
//讄信g的附?(用本C的文件作为附?
mdp=new MimeBodyPart();
FileDataSource fds=new FileDataSource(tfj2);
dh=new DataHandler(fds);
int ddd=tfj2.lastIndexOf("\\");
String fname=tfj2.substring(ddd);//提取文g?br />String ffname=new String(fname.getBytes("gb2312"),"ISO8859-1");//处理文g名是中文的情?br />mdp.setFileName(ffname);//可以和原文g名不一?但最好一?br />mdp.setDataHandler(dh);
mm.addBodyPart(mdp);
//讄信g的附?(用远E文件作为附?
mdp=new MimeBodyPart();

URL urlfj=new URL(tfj3);
URLDataSource ur=new URLDataSource(urlfj);
//?q里用的参数只能为URL对象,不能为URL字串,在前面类介绍时有?误?,q里U正一?
dh=new DataHandler(ur);
int ttt=tfj3.lastIndexOf("/");
String urlname=tfj3.substring(ttt);
//String urlfname=new String(urlname.getBytes("gb2312"),"ISO8859-1");//不知怎么回事,q里不能处理中文问题
mdp.setFileName(urlname);
mdp.setDataHandler(dh);
mm.addBodyPart(mdp);
message.setContent(mm);//把mm作ؓ(f)消息对象的内?/div>
message.saveChanges();
Transport transport=s.getTransport("smtp");
transport.connect("127.0.0.1","xxf","coffee");
transport.sendMessage(message,message.getAllRecipients());
transport.close();
%>
<div align="center">
<p><font color="#FF6600">发送成?</font></p>
<p><a href="recmail.jsp">ȝ看我的信?lt;/a><br>
<br>
<a href="index.htm">再发一?lt;/a> </p>
</div>
<%
}catch(MessagingException e){
out.println(e.toString());
}
%>
</body>
</html>



zeroone0 2006-04-03 09:49 发表评论
]]> վ֩ģ壺 ޵Ӱ߹ۿ| ֮4δɾ| ˾þں91| avһ| ߹ۿ| ƷƵƵѿ| ͼƬС˵| պƷרվ| aëƬվ| AVĻ| ÿĻ2019| 㻨߹ۿѹۿ| ޢva| ߹ۿѹۿַ| av벻߹ۿ| һӰ| ޾Ʒ߹ۿ| ˸һ91| ߾ƷƵѹۿ| þþƷav| Ƶ| ˳Ƶ߹ۿ| ޽Сxxxx| Ʒɫѿ| ޸Ƶ| һëƬ߹| þóѴƬ| ˸徫ƷѼ | aëƬѧѹۿ| һ| 99re6Ƶ| þþþþaŷAV| eeussӰԺֱ| avƷɫ߿| ޷A߷| þþžAVѾƷ| Ʒۺߵһ | ȫƵѹۿ߿| ߾ƷһС˵| ߲ѹۿ| aɻ߹ۿ|