学习java注解能帮助我们读懂别人的代码,特别是框架方面的代码。还能让编程更加简洁,代码更加清晰。
"注解"是JDK1.5引入的,Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法。
Java中的常见注解
JDK自带注解:
@override 表明这个方法是覆盖了父类的或实现的接口的一个方法
@Deprecated 表明这个方法过时了
@Suppvisewarnings 忽略过时警告
常见第三方注解
注解分类
自定义注解
@Target({ElementType.METHOD,ElementType.TYPE}) //作用域
@Retention(RetentionPolicy.RUNTIME) //生命周期
@Inherited
@Documented //以上为元注解
public @interface Description { //使用@interface关键字定义注解
String value(); //成员以无参数无异常方式声明
//String author();
//int age() default 18; //用default为成员指定一个默认值
//成员类型是受限的,合法的类型包括基本类型及String,Class,Annotation,Enumeration
}
Tips: 如果注解只有一个成员,则成员名必须取名为value(),使用时可以忽略成员名和赋值号(=)
元注解
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。
@Target:
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
注解Table 可以用于注解类、接口(包括注解类型) 或enum声明,而注解NoDBColumn仅可用于注解类的成员变量。
@Retention:
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。具体实例如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
Column注解的的RetentionPolicy的属性值是RUTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
@Documented:
@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited:
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
实例代码:
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
解析注解
@Description("I am class annotation")
public class Child implements Person {
@Override
@Description("I am method annotation")
public String name() {
return null;
}
}
解析注解类:
public class parseAnn {
public static void main(String[] args){
try {
//1.使用类加载器加载类
Class cs=Class.forName("com.ann.test.Child");
//2.找到类上面的注解
boolean isExist=cs.isAnnotationPresent(Description.class);
if(isExist){
//3.拿到注解实例
Description d=(Description) cs.getAnnotation(Description.class);
System.out.println(d.value());
}
//4.找到方法上的注解
Method[] ms=cs.getMethods();
for(Method m:ms){
boolean isMExist=m.isAnnotationPresent(Description.class);
if(isMExist){
Description d=(Description) m.getAnnotation(Description.class);
System.out.println(d.value());
}
}
//另一种解析方法
for(Method m:ms){
Annotation[] as=m.getAnnotations();
for(Annotation a:as){
if(a instanceof Description){
Description d=(Description) a;
System.out.println(d.value());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
I am class annotation
I am method annotation
I am method annotation
下面举个例子来模拟简单使用"注解"解决的持久层框架
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
Filter类:
@Table("user")
public class Filter {
@Column("id")
private int id;
@Column("user_name")
private String userName;
@Column("nick_name")
private String nickName;
@Column("age")
private int age;
@Column("city")
private String city;
@Column("email")
private String email;
@Column("mobile")
private String mobile;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
.............................
.........................
}
测试类:
public class Test {
public static String query(Object f){
StringBuilder sb=new StringBuilder();
//获取到f的Class
Class c=f.getClass();
//获取table的名字
boolean exists=c.isAnnotationPresent(Table.class);
if(!exists){
return null;
}
Table t=(Table) c.getAnnotation(Table.class);
String tablename=t.value();
sb.append("select * from ").append(tablename).append(" where 1=1");
//遍历所有的字段
Field[] fArray=c.getDeclaredFields();
for(Field field:fArray){
boolean fExists=field.isAnnotationPresent(Column.class);
if(!fExists){
continue;
}
Column column=field.getAnnotation(Column.class);
String columnName=column.value();
//拿到字段的名字
String fieldName=field.getName();
String getMethodName="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
Object fieldValue=null;
try {
Method getMethod=c.getMethod(getMethodName);
//拿到字段值
fieldValue=getMethod.invoke(f);
} catch (Exception e) {
e.printStackTrace();
}
//拼装sql
if(fieldValue==null||(fieldValue instanceof Integer &&(Integer)fieldValue==0)){
continue;
}
sb.append(" and ").append(columnName);
if(fieldValue instanceof String){
if(((String)fieldValue).contains(",")){
String[] values=((String)fieldValue).split(",");
sb.append(" in(");
for(String v:values){
sb.append("'").append(v).append("'").append(",");
}
sb.deleteCharAt(sb.length()-1);
sb.append(")");
}else{
sb.append("=").append("'").append(fieldValue).append("'");
}
}else if(fieldValue instanceof Integer){
sb.append("=").append(fieldValue);
}
}
return sb.append(";").toString();
}
public static void main(String[] args){
Filter f1=new Filter();
f1.setId(10); //查询id为10的用户
f1.setCity("浙江衢州");
Filter f2=new Filter();
f2.setEmail("liu@sina.com,zh@163.com,77777@qq.com");
String sql1=query(f1);
String sql2=query(f2);
System.out.println(sql1);
System.out.println(sql2);
}
}
运行结果:
select * from user where 1=1 and id=10 and city='浙江衢州';
select * from user where 1=1 and email in('liu@sina.com','zh@163.com','77777@qq.com');