现在的位置: 首页 > 综合 > 正文

java学习脚印: 反射与注释(Annotation)

2014年06月29日 ⁄ 综合 ⁄ 共 11743字 ⁄ 字号 评论关闭
文章目录

java学习脚印: 反射与注释(Annotation)

1.为什么使用注释——作用与应用场合

提示: 注释的处理部分,需要利用java反射机制,如果感觉困难,请参见我的另一篇博客《java学习脚印:Class类与反射机制》

1.1 注释的引入

注释(Annontation,也译作注解)是那些插入到源代码中用于工具处理的标签。这些标签可以在与源代码层次上进行处理,或者可以通过编译器将它们纳入到类文件中。

值得注意的是除了少数系统提供的注释外,注释不会改变对编写的程序的编译方式;注释并不直接影响程序的执行,需要利用相关的注释处理工具来对注释进行分析,例如第三方框架,这在Hibernate,struts等框架中经常使用。

1.2 注释的应用场合

注释像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

下面是注释可能使用的场合:

  • 附属文件的自动生成,例如部署描述符或者bean信息类,例如最新的servlet有一个注释如:

          @WebServlet(description = "first servlet testing", urlPatterns = { "/HelloWorld" })可以用来映射servlet类文件和url路径。

  • 测试,日志,事物语义等代码的自动生成。例如Junit测试框架中经常使用的@Test来标记将某个方法加入到测试用例中。

1.3 初识注释的例子——不做细致探讨,直观感受即可

例子AnnotationDemo1给出一个JAXB(ava API for XML Processing,提供解析和验证XML文档的能力)根据注释,自动将一个类序列化和反序列化的例子(改编自:http://www.vogella.com/tutorials/JAXB/article.html),这里不要求去理解JAXB的细节,只是来直观感受下注释。

清单1.3-1 Book.java

package com.learningjava;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
 * original author: Lars Vogel
 * link: http://www.vogella.com/tutorials/JAXB/article.html
 */
@XmlRootElement(name = "书籍")
@XmlType(propOrder = { "name", "author","isbn","publisher" })
public class Book {
 
  // change this name for your XML-Output:
  @XmlElement(name = "书名")
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
  @XmlElement(name = "作者")
  public String getAuthor() {
    return author;
  }

  public void setAuthor(String author) {
    this.author = author;
  }
  @XmlElement(name = "出版社")
  public String getPublisher() {
    return publisher;
  }

  public void setPublisher(String publisher) {
    this.publisher = publisher;
  }
  @XmlElement(name = "国际标准书号")
  public String getIsbn() {
    return isbn;
  }

  public void setIsbn(String isbn) {
    this.isbn = isbn;
  }
  
  private String name;
  private String author;
  private String publisher;
  private String isbn;
} 

清单1.3-2  AnnotationDemo1.java

package com.learningjava;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
 * JAXB using annotation to serialize object to xml file
 * original author: Lars Vogel
 * link: http://www.vogella.com/tutorials/JAXB/article.html
 */
public class AnnotationDemo1 {
  
  public static void main(String[] args) throws JAXBException, IOException {

	final String BOOK_XML = "./books.xml";
    
	// create book
    Book book1 = new Book();
    book1.setIsbn("978-0060554736");
    book1.setName("The Game");
    book1.setAuthor("Neil Strauss");
    book1.setPublisher("Harpercollins");

    // create JAXB context and instantiate marshaller
    JAXBContext context = JAXBContext.newInstance(Book.class);
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

    // Write to System.out
    m.marshal(book1, System.out);

    // Write to File
    m.marshal(book1, new File(BOOK_XML));

    // reconstruct Book object from our xml file
    System.out.println();
    System.out.println("Output from our XML File: ");
    Unmarshaller um = context.createUnmarshaller();
    Book book2 = (Book) um.unmarshal(new FileReader(BOOK_XML));
    System.out.println("Book: " + book2.getName() + " from "
            + book2.getAuthor());
  }
} 

输出结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<书籍>
    <书名>The Game</书名>
    <作者>Neil Strauss</作者>
    <国际标准书号>978-0060554736</国际标准书号>
    <出版社>Harpercollins</出版社>
</书籍>

Output from our XML File:
Book: The Game from Neil Strauss

2.注释的定义与类型

2.1 注释的定义

一个注释是有一个注释接口定义的。注释定义的模板如下:

modifiers @interface AnnotationName {

    element declaration1

    element declaration2

}

其中元素声明具有这种形式: type elementName() default value;

每个元素的声明类似与方法的声明,都有名称和类型,注释元素的类型为下列之一(来自:《java核心技术卷二》机工第八版):

  • 基本类型
  • 一个String
  • 一个Class,具有一个可供选择的参数,如Class<? extends MyClass>
  • 一个enum
  • 一个注释类型
  • 一个如前所述类型的数组

注释一个项目时,提供默认值的元素,可以不用提供;只具有一个元素的注释,元素名也可以省去,这种注释称为单值注释。

这里定义一个简单注释如下(来自: http://tutorials.jenkov.com/java/annotations.html):

public @interface MyAnnotation {

    String   value();
    String   name();
    int      age();
    String[] newNames();
}

2.2 注释类型

注释分为两种,标准注释和自定义注释。

2.2.1标准注释

标准注释分为三个正规注释为编译器提供指示,还有四个注释是元注释(meta annotation,所谓元注释就是关于注释的注释,类似于数据库中的元数据)。

三个正规注释分别是(转载自: 51 cto zhao_xiao_long 的BLOG Java注解annotation用法和自定义注解处理器):

  • @Override,表示当前的方法定义覆盖了父类中的方法。必须要有相同的方法签名即(方法名,参数类型,参数顺序,参数个数)都一样。否则在编译过程中发出错误提示。
  • @Deprecated,对不应该再使用的方法添加注释,当使用这个方法的时候,会在编译时候显示提示信息。
  • @SuppressWarnings,关闭不当的编译器报警信息。

这个大家用的很熟悉.

重点记下元注释,元注释如下:

  • @Target,表示该注解可以用什么地方。

      包括8个地方:

  1. ElementType.ANNOTATION_TYPE 用在注释类型声明
  2. ElementType.CONSTRUCTOR    用在构造器
  3. ElementType.FIELD  用在域
  4. ElementType.LOCAL_VARIABLE 局部变量
  5. ElementType.METHOD  方法
  6. ElementType.PACKAGE 包
  7. ElementType.PARAMETER 方法或构造器参数
  8. ElementType.TYPE  
    类(包括enum)及接口(包括注释类型)

  •  @Retention,表示需要在什么级别保存该注释信息。

     SOURCE,进在源代码中,不包括在类文件和运行时;

     CLASS,注释保留在类文件中,但运行时不可获得。

     RUNTIME,类文件中的注释,并由虚拟机载入,可以通过反射机制获得。

  •  @Documented,将此注释包含到Javadoc中。

  •  @Inherited,允许子类继承父类的注释。

继承注释示例如下:

java.lang.annotation.Inherited
@Inherited
public @interface MyAnnotation {

}
@MyAnnotation
public class MySuperClass { ... }
public class MySubClass extends MySuperClass { ... }

这里由于MySuperClass定义了MyAnnotation注释,并且MyAnnotation注释具有继承属性,因此MySubClass也将具有MyAnnotation注释。

2.2.2 自定义注释

自定义注释需要做两个工作,按照上面注释定义所述标准定义你所需的注释,第二提供处理注释的工具,这种工具可以自己编写,也可以由其他方式获取。在综合案例部分我们给出一个自己定义的注释,来国际化菜单和工具栏。

3.综合案例——注释实现菜单及提示的国际化

通常要实现菜单和工具栏的国际化,需要获取不用语言包中的指定字符串,这里我自定义一个注释MenuItemAndAction来实现这一功能(尚未研究更科学的方法,这里提供一个参考方式,欢迎指教)。

通过选择语言组合框中一种,即可切换界面语言,运行结果如下:

英文界面:

中文界面:

法文界面:


对这一实例感兴趣的,可以到下载我的资源中去下载,此文也附上代码清单(编译工程时将语言包放到与src目录下即可)。注意重点理解注释的定义(MenuItemAndAction.java
)和处(MenuItemAndActionProcessor)两个部分,关于国际化部分了解即可。


清单3-1 MenuItemAndAction.java 定义注释

package com.learningjava;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
/**
 * this annotation used to set name and tooltip for MenuItem or action
 * @author wangdq
 * 2014-2-27
 */
public @interface MenuItemAndAction {
	String name();
	String tooltip() default "";
}

清单3-2 MenuItemAndActionProcessor.java 定义处理注释的工具类

package com.learningjava;

import java.lang.reflect.Field;
import java.util.Locale;
import java.util.ResourceBundle;

import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.Action;
/**
 * Annotation processor 
 * set name and tooltip for menu or action
 * @author wangdq
 * 2014-2-27
 */
public class MenuItemAndActionProcessor {
	/**
	 * annotation processer
	 * @param obj the object which to process
	 */
	public static void processAnnotation(Object obj) {
		Class<? extends Object> cl = obj.getClass();
		//get all fields
		Field[] fields = cl.getDeclaredFields();
		for(Field field: fields) {
			//get the specified annotation
			MenuItemAndAction annotation = field.getAnnotation(MenuItemAndAction.class);
			if(annotation != null) {
				
				if(!field.isAccessible()) {
					field.setAccessible(true);
				}
				//set the name and tooltip for the menu or action
				String name = resourceBundle.getString(annotation.name());
				String tooltip = "";
				if(!annotation.tooltip().isEmpty()) {
					tooltip = resourceBundle.getString(annotation.tooltip());
				}
				try {
					if(field.getType() == JMenuItem.class || field.getType() == JMenu.class) {
						JMenuItem menuItem = (JMenuItem)field.get(obj);
						menuItem.setText(name);
						System.out.println(name);
						menuItem.setToolTipText(tooltip);
					}else if(field.getType() == Action.class){
						Action action = (Action)field.get(obj);
						action.putValue(Action.NAME, name);
						action.putValue(Action.SHORT_DESCRIPTION, tooltip);
					}
				}catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
    //change the resource bundle according to the locale
	public static void setResourceBundle(Locale locale) {
		resourceBundle = ResourceBundle.getBundle("textResource",locale);
	}
	private static ResourceBundle resourceBundle;
	
	static {
		resourceBundle = ResourceBundle.getBundle("textResource");
	}
}


清单3-3 MainFrame.java 提供菜单和工具栏的Frame类

package com.learningjava;



import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Locale;

import javax.swing.*;
/**
 * Test frame with menu and toolbar 
 * @author wangdq
 * 2014-2-27
 */
public class MainFrame extends JFrame{
	
	public MainFrame() {
		this.setTitle("AnnotationDemo");
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		textArea = new JTextArea(10,10);
		menuAndToolBar = new MenuAndToolbar(textArea);
		panel.add(textArea);
		this.add(panel,BorderLayout.CENTER);
		//set menu
		JMenuBar menubar = new JMenuBar();
		menubar.add(menuAndToolBar.getFileMenu());
		this.setJMenuBar(menubar);
		//set toolbar
		JToolBar toolbar = menuAndToolBar.getToolbar();
		toolbar.setFloatable(false);
		this.add(toolbar,BorderLayout.NORTH);
		//init text 
		MenuItemAndActionProcessor.processAnnotation(menuAndToolBar);
	}
	private JTextArea textArea;
	private MenuAndToolbar menuAndToolBar;
	private static final long serialVersionUID = 1L;
}
/**
 * menu and toolbar to using annotation on the field
 * @author wangdq
 * 2014-2-27
 */
class MenuAndToolbar {
    
	public MenuAndToolbar(JTextArea textArea) {
    	this.textArea = textArea;
    	initMenuAndToolBar();
    }
	public JMenu getFileMenu() {
		return fileMenu;
	}
	public JToolBar getToolbar() {
		return toolbar;
	}
	private void initMenuAndToolBar() {
		fileMenu = new JMenu();
		toolbar = new JToolBar();
		//new file
		actNew = new AbstractAction(){
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				textArea.setText("new");
			}
			private static final long serialVersionUID = 1L;
		};
		fileMenu.add(new JMenuItem(actNew));
		toolbar.add(actNew);
		//open file
		actOpen = new AbstractAction(){
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				textArea.setText("open");
			}
			private static final long serialVersionUID = 1L;
		};
		fileMenu.add(new JMenuItem(actOpen));
		toolbar.add(actOpen);
		//save file
		actSave = new AbstractAction(){
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				textArea.setText("save");
			}
			private static final long serialVersionUID = 1L;
		};
		fileMenu.add(new JMenuItem(actSave));
		toolbar.add(actSave);
		fileMenu.addSeparator();
		toolbar.addSeparator();
		//exit
		actExit = new AbstractAction(){
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				System.exit(0);
			}
			private static final long serialVersionUID = 1L;
		};
		fileMenu.add(new JMenuItem(actExit));
		toolbar.add(actExit);
		//languange options combobox
		JComboBox<String> languangeBox = new JComboBox<String>
		(new String[]{"en_US","zh_CN","fr_FR"});
		languangeBox.addItemListener(new ItemListener(){
		
			@Override
			public void itemStateChanged(ItemEvent e) {
				// TODO Auto-generated method stub
				@SuppressWarnings("unchecked")
				JComboBox<String> box = (JComboBox<String>)e.getSource();
				String text = (String)box.getSelectedItem();
				String[] str = text.split("\\_");
				Locale locale = new Locale(str[0],str[1]);
				System.out.println(locale.toString());
				MenuItemAndActionProcessor.setResourceBundle(locale);
				MenuItemAndActionProcessor.processAnnotation(MenuAndToolbar.this);
			}
		});
		toolbar.add(languangeBox);
	}
	
	@MenuItemAndAction(name="New",tooltip="TipNew")
	private Action actNew;
	
	@MenuItemAndAction(name="Open",tooltip="TipOpen")
	private Action actOpen;
	
	@MenuItemAndAction(name="Save",tooltip="TipSave")
	private Action actSave;
	
	@MenuItemAndAction(name="Exit",tooltip="TipExit")
	private Action actExit;
	
	@MenuItemAndAction(name="File")
	private JMenu fileMenu;
	
	private JToolBar toolbar;
	private JTextArea textArea;
}

清单3-4 ProgramDrive.java 测试驱动类

package com.learningjava;



import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
 * test the annotation
 * @author wangdq
 * 2014-2-27
 */
public class ProgramDrive {
	public static void main(String[] args) {
		
		SwingUtilities.invokeLater(new Runnable(){

			@Override
			public void run() {
				// TODO Auto-generated method stub
				JFrame frame = new MainFrame();
				frame.setSize(400, 300);
				frame.setLocationRelativeTo(null);
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setVisible(true);
			}
		});
	}
}

清单3-5 默认语言包  textResource.properties

File = File
New = New
TipNew = create a new file
Open = Open
TipOpen = open a file
Save = Save
TipSave = save the current file
Exit = Exit
TipExit = exit the system

清单3-6 中文语言包  textResource_zh_CN.properties

File = \u6587\u4EF6
New = \u65B0\u5EFA
TipNew = \u65B0\u5EFA\u4E00\u4E2A\u6587\u4EF6
Open = \u6253\u5F00
TipOpen = \u6253\u5F00\u4E00\u4E2A\u6587\u4EF6
Save = \u4FDD\u5B58
TipSave = \u4FDD\u5B58\u5F53\u524D\u6587\u4EF6
Exit = \u9000\u51FA
TipExit = \u9000\u51FA\u7CFB\u7EDF

清单3-7 法语语言包  textResource_fr_FR.properties

File = dossier
New = nouveau
TipNew = Créer un nouveau fichier
Open = Ouvrir
TipOpen = Ouvrir un fichier
Save = sauver
TipSave = Enregistrez le fichier en cours
Exit = quitter
TipExit = Quittez le système

清单3-8 英文语言包   textResource _en_US.properties

File = File
New = New
TipNew = create a new file
Open = Open
TipOpen = open a file
Save = Save
TipSave = save the current file
Exit = Exit
TipExit = exit the system

:

抱歉!评论已关闭.