JDK6的新特性之一:Desktop类和SystemTray类
在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray,前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱 发邮件,用默认应用程序打开或编辑文件(比如,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托 盘程序.下面代码演示了Desktop和SystemTray的用法.
/**
*
* @author WuLi
*/
import java.awt.Desktop;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.Toolkit;
import java.awt.Image;
import java.awt.PopupMenu;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.AWTException;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DesktopTray {
private static Desktop desktop;
private static SystemTray st;
private static PopupMenu pm;
public static void main(String[] args) {
if(Desktop.isDesktopSupported()){//判断当前平台是否支持Desktop类
desktop = Desktop.getDesktop();
}
if(SystemTray.isSupported()){//判断当前平台是否支持系统托盘
st = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().getImage("netbeans.png");//定义托盘图标的图片
createPopupMenu();
TrayIcon ti = new TrayIcon(image, "Desktop Demo Tray", pm);
try {
st.add(ti);
} catch (AWTException ex) {
ex.printStackTrace();
}
}
}
public static void sendMail(String mail){
if(desktop!=null && desktop.isSupported(Desktop.Action.MAIL)){
try {
desktop.mail(new URI(mail));
} catch (IOException ex) {
ex.printStackTrace();
} catch (URISyntaxException ex) {
ex.printStackTrace();
}
}
}
public static void openBrowser(String url){
if(desktop!=null && desktop.isSupported(Desktop.Action.BROWSE)){
try {
desktop.browse(new URI(url));
} catch (IOException ex) {
ex.printStackTrace();
} catch (URISyntaxException ex) {
ex.printStackTrace();
}
}
}
public static void edit(){
if(desktop!=null && desktop.isSupported(Desktop.Action.EDIT)){
try {
File txtFile = new File("test.txt");
if(!txtFile.exists()){
txtFile.createNewFile();
}
desktop.edit(txtFile);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void createPopupMenu(){
pm = new PopupMenu();
MenuItem openBrowser = new MenuItem("Open My Blog");
openBrowser.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openBrowser("http://blog.csdn.net/WuLi ");
}
});
MenuItem sendMail = new MenuItem("Send Mail to me");
sendMail.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendMail("mailto:[email protected]");
}
});
MenuItem edit = new MenuItem("Edit Text File");
sendMail.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
edit();
}
});
MenuItem exitMenu = new MenuItem("&Exit");
exitMenu.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
pm.add(openBrowser);
pm.add(sendMail);
pm.add(edit);
pm.addSeparator();
pm.add(exitMenu);
}
}
如果在Windows中运行该程序,可以看到在系统托盘区有一个图标,右击该图标会弹出一个菜单,点击Open My Blog会打开IE,并浏览"http://blog.csdn.net/WuLi ";点击Send Mail to me会打开Outlook Express给我发邮件;点击Edit Text File会打开记事本编辑在程序中创建的文件test.txt
JDK6的新特性之二:使用JAXB2来实现对象与XML之间的映射
JDK6的新特性之二:使用JAXB2来实现对象与XML之间的映射
JAXB 是Java Architecture for XML Binding的缩写,可以将一个Java对象转变成为XML格式,反之亦然。我们把对象与关系数据库之间的映射称为ORM, 其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping). 原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。实际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档。 闲话不多说了,下面用代码演示在JDK6中如何来用JAXB2
public class JAXB2Tester {
public static void main(String[] args) throws JAXBException,IOException {
JAXBContext context = JAXBContext.newInstance(Person.class);
//下面代码演示将对象转变为xml
Marshaller m = context.createMarshaller();
Address address = new Address("China","Beijing","Beijing","ShangDi West","100080");
Person p = new Person(Calendar.getInstance(),"JAXB2",address,Gender.MALE,"SW");
FileWriter fw = new FileWriter("person.xml");
m.marshal(p,fw);
//下面代码演示将上面生成的xml转换为对象
FileReader fr = new FileReader("person.xml");
Unmarshaller um = context.createUnmarshaller();
Person p2 = (Person)um.unmarshal(fr);
System.out.println("Country:"+p2.getAddress().getCountry());
}
}
@XmlRootElement//表示person是一个根元素
class Person {
@XmlElement
Calendar birthDay; //birthday将作为person的子元素
@XmlAttribute
String name; //name将作为person的的一个属性
public Address getAddress() {
return address;
}
@XmlElement
Address address; //address将作为person的子元素
@XmlElement
Gender gender; //gender将作为person的子元素
@XmlElement
String job; //job将作为person的子元素
public Person(){
}
public Person(Calendar birthDay, String name, Address address, Gender gender, String job) {
this.birthDay = birthDay;
this.name = name;
this.address = address;
this.gender = gender;
this.job = job;
}
}
enum Gender{
MALE(true),
FEMALE (false);
private boolean value;
Gender(boolean _value){
value = _value;
}
}
class Address {
@XmlAttribute
String country;
@XmlElement
String state;
@XmlElement
String city;
@XmlElement
String street;
String zipcode; //由于没有添加@XmlElement,所以该元素不会出现在输出的xml中
public Address() {
}
public Address(String country, String state, String city, String street, String zipcode) {
this.country = country;
this.state = state;
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
public String getCountry() {
return country;
}
}
运行该程序,我们会得到一个person.xml的文件,如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<person name="JAXB2">
<birthDay>2006-12-28T08:49:27.203+00:00</birthDay>
<address country="China">
<state>Beijing</state>
<city>Beijing</city>
<street>ShangDi West</street>
</address>
<gender>MALE</gender>
<job>SW</job>
</person>
控制台会输出
Country:China
最后,想说一点,除了JAXB之外,我们还可以通过XMLBeans和Castor等来实现同样的功能。
JDK6的新特性之三:理解StAX
StAX(JSR 173)是JDK6.0中除了DOM和SAX之外的又一种处理XML文档的API
StAX的来历
在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).由于JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAX所以Sun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版 本). JDK6里面JAXP的版本就是1.4.
StAX简介
StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API.StAX通过提供一种基于事件迭代器(Iterator)的API让 程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件 然后处理该事件,之后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;SAX也是基于事件处理xml文档,但却是用推模式解析,解析器解析 完整个xml文档后,才产生解析事件,然后推给程序去处理这些事件;DOM采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点 和子结点以及兄弟节点的数据,但如果文档很大,将会严重影响性能。下面是这几种API的比较(转载自http://www.blogjava.net/hsith/archive/2006/06/29/55817.html )
XML Parser API Feature Summary Feature StAX SAX DOM TrAX
API Type Pull, streaming Push, streaming In memory tree XSLT Rule
Ease of Use High Medium High Medium
XPath Capability No No Yes Yes
CPU and Memory Efficiency Good Good Varies Varies
Forward Only Yes Yes No No
Read XML Yes Yes Yes Yes
Write XML Yes No Yes Yes
Create, Read, Update, Delete No No Yes No
StAX代码演示
下面代码演示了如何通过StAX读取xml文档和生成xml文档
public class StaxTester {
public static void main(String[] args) throws XMLStreamException, FileNotFoundException {
readXMLByStAX();//用XMLEventReader解析xml文档
writeXMLByStAX();//用XMLStreamWriter写xml文档
}
private static void readXMLByStAX() throws XMLStreamException, FileNotFoundException {
XMLInputFactory xmlif = XMLInputFactory.newInstance();
XMLEventReader xmler = xmlif.createXMLEventReader(StaxTester.class.getResourceAsStream("test.xml"));
XMLEvent event;
StringBuffer parsingResult = new StringBuffer();
while (xmler.hasNext()) {
event = xmler.nextEvent();
if (event.isStartElement()) { //如果解析的是起始标记
StartElement se = event.asStartElement();
parsingResult.append("<");
parsingResult.append(se.getName());
if(se.getName().getLocalPart().equals("catalog")) {
parsingResult.append(" id=/"");
parsingResult.append(se.getAttributeByName(new QName("id")).getValue());
parsingResult.append("/"");
}
parsingResult.append(">");
} else if (event.isCharacters()) { //如果解析的是文本内容
parsingResult.append(event.asCharacters().getData());
} else if(event.isEndElement()){ //如果解析的是结束标记
parsingResult.append("</");
parsingResult.append(event.asEndElement().getName());
parsingResult.append(">");
}
}
System.out.println(parsingResult);
}
private static void writeXMLByStAX() throws XMLStreamException, FileNotFoundException {
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
XMLStreamWriter xmlw = xmlof.createXMLStreamWriter(new FileOutputStream("output.xml"));
// 写入默认的 XML 声明到xml文档
xmlw.writeStartDocument();
xmlw.writeCharacters("/n");
// 写入注释到xml文档
xmlw.writeComment("testing comment");
xmlw.writeCharacters("/n");
// 写入一个catalogs根元素
xmlw.writeStartElement("catalogs");
xmlw.writeNamespace("myNS", "http://blog.csdn.net/WuLi ");
xmlw.writeAttribute("owner","WuLi");
xmlw.writeCharacters("/n");
// 写入子元素catalog
xmlw.writeStartElement("http://blog.csdn.net/WuLi ", "catalog");
xmlw.writeAttribute("id","007");
xmlw.writeCharacters("Apparel");
// 写入catalog元素的结束标签
xmlw.writeEndElement();
// 写入catalogs元素的结束标签
xmlw.writeEndElement();
// 结束 XML 文档
xmlw.writeEndDocument();
xmlw.close();
}
}
test.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<catalogs>
<catalog id="001">Book</catalog>
<catalog id="002">Video</catalog>
</catalogs>
运行上面程序后,控制台输出如下:
<catalogs>
<catalog id="001">Book</catalog>
<catalog id="002">Video</catalog>
</catalogs>
运行上面程序后,产生的output.xml文件如下:
<?xml version="1.0" ?>
<!-- testing comment-->
<catalogs xmlns:myNS="http://blog.csdn.net/WuLi " owner="WuLi">
<myNS:catalog id="007">Apparel</myNS:catalog>
</catalogs>
JDK6的新特性之四:使用Compiler API
现在我们可以用JDK6 的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。这个特性对于某些需要用到动态编译的应用程序相当有用, 比如JSP Web Server,当我们手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要我们产生另一个进程去 做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。 下面代码演示了Compiler API的使用
public class CompilerAPITester {
private static String JAVA_SOURCE_FILE = "DynamicObject.java";
private static String JAVA_CLASS_FILE = "DynamicObject.class";
private static String JAVA_CLASS_NAME = "DynamicObject";
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
generateJavaClass();
try {
//将产生的类文件拷贝到程序的ClassPath下面,下面这一行代码是特定于Windows+IntelliJ IDEA 6.0项目,不具有移植性
Runtime.getRuntime().exec("cmd /c copy "+JAVA_CLASS_FILE+" classes//production//JDK6Features");
Iterable<? extends JavaFileObject> sourcefiles = fileManager.getJavaFileObjects(JAVA_SOURCE_FILE);
compiler.getTask(null, fileManager, null, null, null, sourcefiles).call();
fileManager.close();
Class.forName(JAVA_CLASS_NAME).newInstance();//创建动态编译得到的DynamicObject类的实例
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void generateJavaClass(){
try {
FileWriter fw = new FileWriter(JAVA_SOURCE_FILE);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("public class "+JAVA_CLASS_NAME+"{");
bw.newLine();
bw.write("public "+JAVA_CLASS_NAME+"(){System.out.println(/"In the constructor of DynamicObject/");}}");
bw.flush();
bw.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
程序运行后,会产生DynamicObject.java和DynamicObject.class两个文件,并在控制台输出
In the constructor of DynamicObject
JDK6的新特性之五:轻量级Http Server
JDK6的新特性之五:轻量级Http Server
JDK6提供了一个 简单的Http Server API,据此我们可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在 这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法.下面代码演示了怎样创建自己的Http Server
/**
* Created by IntelliJ IDEA.
* User: WuLi
* Date: Dec 30, 2006
*/
public class HTTPServerAPITester {
public static void main(String[] args) {
try {
HttpServer hs = HttpServer.create(new InetSocketAddress(8888),0);//设置HttpServer的端口为8888
hs.createContext("/WuLi", new MyHandler());//用MyHandler类内处理到/WuLi的请求
hs.setExecutor(null); // creates a default executor
hs.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
String response = "<h3>Happy New Year 2007!--WuLi</h3>";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
运行程序后,在浏览器内输入http://localhost:8888/WuLi ,浏览器输出
Happy New Year 2007!--WuLi
JDK6的新特性之六:插入式注解处理API
JDK6的新特性之六:插入式注解处理API(Pluggable Annotation Processing API)
插 入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175),实际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境. JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列. JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,我们想建立一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间需要执行的测试 方法,如下所示:
@TestMethod
public void testCheckName(){
//do something here
}
这时我们就可以用JSR 269提供的API来处理测试类,根据Annotation提取出需要执行的测试方法.
另 一个例子是如果我们出于某种原因需要自行开发一个符合Java EE 5.0的Application Server(当然不建议这样做),我们就必须处理Common Annotations(JSR 250),Web Services Metadata(JSR 181)等规范的Annotations,这时可以用JSR 269提供的API来处理这些Annotations. 在现在的开发工具里面,Eclipse 3.3承诺将支持JSR 269
下面我用代码演示如何来用JSR 269提供的API来处理Annotations和读取Java源文件的元数据(metadata)
/**
* Created by IntelliJ IDEA.
* User: WuLi
* Date: Dec 31, 2006
*/
@SupportedAnnotationTypes("PluggableAPT.ToBeTested")//可以用"*"表示支持所有Annotations
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MyAnnotationProcessor extends AbstractProcessor {
private void note(String msg) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
}
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//annotations的值是通过@SupportedAnnotationTypes声明的且目标源代码拥有的所有Annotations
for(TypeElement te:annotations){
note("annotation:"+te.toString());
}
Set<? extends Element> elements = roundEnv.getRootElements();//获取源代码的映射对象
for(Element e:elements){
//获取源代码对象的成员
List<? extends Element> enclosedElems = e.getEnclosedElements();
//留下方法成员,过滤掉其他成员
List<? extends ExecutableElement> ees = ElementFilter.methodsIn(enclosedElems);
for(ExecutableElement ee:ees){
note("--ExecutableElement name is "+ee.getSimpleName());
List<? extends AnnotationMirror> as = ee.getAnnotationMirrors();//获取方法的Annotations
note("--as="+as);
for(AnnotationMirror am:as){
//获取Annotation的值
Map<? extends ExecutableElement, ? extends AnnotationValue> map= am.getElementValues();
Set<? extends ExecutableElement> ks = map.keySet();
for(ExecutableElement k:ks){//打印Annotation的每个值
AnnotationValue av = map.get(k);
note("----"+ee.getSimpleName()+"."+k.getSimpleName()+"="+av.getValue());
}
}
}
}
return false;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ToBeTested{
String owner() default "WuLi";
String group();
}
编译以上代码,然后再创建下面的Testing对象,不要编译Testing对象,我在后面会编译它
public class Testing{
@ToBeTested(group="A")
public void m1(){
}
@ToBeTested(group="B",owner="QQ")
public void m2(){
}
@PostConstruct//Common Annotation里面的一个Annotation
public void m3(){
}
}
下面我用以下命令编译Testing对象
javac -XprintRounds -processor PluggableAPT.MyAnnotationProcessor Testing.java
-XprintRounds表示打印round的次数,运行上面命令后在控制台会看到如下输出:
Round 1:
input files: {PluggableAPT.Testing}
annotations: [PluggableAPT.ToBeTested, javax.annotation.PostConstruct]
last round: false
Note: annotation:PluggableAPT.ToBeTested
Note: --ExecutableElement name is m1
Note: [email protected](group="A")
Note: ----m1.group=A
Note: --ExecutableElement name is m2
Note: [email protected](group="B", owner="QQ")
Note: ----m2.group=B
Note: ----m2.owner=QQ
Note: --ExecutableElement name is m3
Note: [email protected]
Round 2:
input files: {}
annotations: []
last round: true
本来想用JDK6.0的Compiler API来执行上面编译命令,可是好像现在Compiler API还不支持-processor参数,运行时总报以下错误
Exception in thread "main" java.lang.IllegalArgumentException: invalid flag: -processor PluggableAPT.MyAnnotationProcessor
调用Compiler API的代码是这样的
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> sourcefiles = fileManager.getJavaFileObjects("Testing.java");
Set<String> options = new HashSet<String>();
options.add("-processor PluggableAPT.MyAnnotationProcessor");
compiler.getTask(null, fileManager, null, options, null, sourcefiles).call();
不知道这是不是Compiler API的一个bug.