Java 注解详解

定义:

    注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及之后版本引入的一个特性,与@Retention类、接口、枚举是在同一个层次。它能够声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释,利用发射能够在运行时改变程序的逻辑。请往下看:java

    应该有人会看不懂这句话吧,“元数据”过高大上了,好抽象,云里雾里的,那咱们再看看元数据的定义。数据库

元数据:为描述数据的数据数组

综合理解注解:注解就是Java为类、接口、方法、字段、局部变量、方法参数能表达更丰富而添加的说明,就好像给它们(类、接口、方法、字段、局部变量、方法参数)加了一个标签,好比一个方法是被重写的,那么Java代码就是:安全

@Override
public void doDispatch(HttpServletRequest req){

}

 

语法:

    取一段代码为例来讲明注解的语法(不要被它吓到,接下来的篇幅会对他一点点的讲解)app

package com.jv.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

下面开始一点点的讲,最终获得上面的注解eclipse

1.定义最简单的注解ide

public @interface MyAnnotation {}

使用@interface就声明了一个注解,而后就能够在其余地方使用这个注解,如:函数

package com.jv.annotation;

@MyAnnotation
public class MyObject {
	
}

虽然这个注解如今尚未任何属性或者其余的修饰,但类或者它的元素被该注解修饰了,那么他就变得不同了。好比在运行时就能够扫描全部的类并判断是否是拥有该注解,而后作特殊的操做测试

2.给注解加属性ui

package com.jv.annotation;

public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

给注解加value和name两个属性,其中name是一个数组类型的,表示他能够接受多个String值。当注解有了属性,那么它就携带了更可能是信息,在使用(经过反射得到注解或者注解的值)的时候就可拿它们作逻辑判断

:注解没有方法

3.给注解添加注解

package com.jv.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

注解的注解就是用来讲明被修饰的注解在不一样功能上的限制

好比:@Target限制了注解能修饰那些元素(类、接口、方法、字段、局部变量、方法参数)

@Target是一种元注解,表示能够用来修饰注解的注解

 

元注解:

    元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable 5 种

    @Retention

    Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
    - RetentionPolicy.SOURCE     注解被保留到源码中,在编译器工做时它将被丢弃。 
    - RetentionPolicy.CLASS        注解被保留到Java和编译后的class文件中,但不会被加载到 JVM 中。 
    - RetentionPolicy.RUNTIME  注解被保留到Java和编译后的class文件中,且会被加载到JVM,因此在程序运行时能够获取到它们,并作不少逻辑判断

    若是对类加载有兴趣的同窗能够查看Java类加载机制

    @Documented    

    它的做用是可以将被@Documented 修饰的注解记录到 Javadoc 中

    @Target

    限定注解能修饰的元素(类、接口、方法、字段、局部变量、方法参数)

    - ElementType.ANNOTATION_TYPE         能够给一个注解进行注解

    - ElementType.CONSTRUCTOR                能够给构造方法进行注解

    - ElementType.FIELD                                能够给属性进行注解

    - ElementType.LOCAL_VARIABLE             能够给局部变量进行注解

    - ElementType.METHOD                          能够给方法进行注解

    - ElementType.PACKAGE                         能够给一个包进行注解

    - ElementType.PARAMETER                    能够给一个方法内的参数进行注解

    - ElementType.TYPE                                 能够给一个类进行注解,好比类、接口、枚举

    - ElementType.TYPE_PARAMETER           能够给一个类或者方法的参数声明进行注解(JDK1.8)

    - ElementType.TYPE_USE                         能够给任何使用到类型的地方进行声明(JDK1.8)

    @Inherited

    一个注解被@Inherited修饰后,那么被修饰的注解再去修饰其余类(A),A的子类B(B没有被其余注解修饰)会继承A的注解

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{}


@MyAnnotation
public class A {}


public class B extends A {}

    @Repeatable

    它是 Java 1.8 新加入的注解,表示能够重复修饰

@interface Cars {
    Car[]  value();
}


@Repeatable(Cars .class)
@interface Car{
    String value default "";
}


@Car("AA")
@Car("BB")
@Car("CC")
public class MyTest{

}

@Car具备一个value属性,那属性拿来干吗,又是怎么使用的喃

注解的属性

它和类的成员变量同样,能够用来保存在使用注解时指定的值

public @interface MyAnnotation {
	String value() default "";
	String[] name() default {};
}

定义了value 和 name属性,而且使用default指定它们的默认值,如何使用它们喃?

@MyAnnotation(value="abc",name= {"123","456"})
public class MyObject {
	
}

 

JDK内置注解

@Deprecated

用来修饰过期的元素,在使用高版本JDK时常常遇到该注解。编译时会发出警告,通知开发人员它是已过期的类或者方法

@Override

提示子类中的方法是重写的

@SuppressWarnings

忽略编译器发出的警告,在添加了这个注解,eclipse左边的小黄感叹号就会消失

@SafeVarargs

Jdk 1.7 加入的新注解,用于提醒开发者不要用参数作一些不安全的操做,它的存在会阻止编译器产生 unchecked 这样的警告,可是运行时程序可能会报错。

@FunctionalInterface

Jdk 1.8 加入的新注解(函数式接口),但愿了解的同窗能够看看(强烈推荐)Java8 Lambda表达式

反射提取注解

用一个例子来讲明属性或者方法上的注解如何被拿到,如何取得注解的属性值

注:测试JDK为1.8

1.定义注解

/*
 * 指定数据库名称
 */
@Target({ElementType.TYPE,ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTable{
	String value() default "";
}

/*
 * 指明JavaBean中某一个属性为主键,且一个类中该注解只能有一个
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyId {
	public String value() default "id";
}

/**
 * 指定JavaBean中属性对应的数据库字段名
 * @author jionsvolk
 *
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
	public String value() default "";
}

2.用到Bean

@MyTable("tb_cust")
public class Cust implements Serializable{
	private static final long serialVersionUID = 1L;
	
	@MyId("custId")
	private Integer custId;
	@MyField("custName")
	private String name;
	private String address;
	
	public Integer getCustId() {
		return custId;
	}
	public void setCustId(Integer custId) {
		this.custId = custId;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Cust(Integer custId, String name, String address) {
		super();
		this.custId = custId;
		this.name = name;
		this.address = address;
	}
	public Cust() {
		super();
	}
	
}
public class MyEntry {
	private String key;
	private Object value;
	
	public boolean isNotEmpty() {
		if(key == null || key.equals("") || value == null) {
			return false;
		}else {
			return true;
		}
	}
	
	
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public Object getValue() {
		return value;
	}
	public void setValue(Object value) {
		this.value = value;
	}
	
}

3.反射提取注解、提取方法、提取属性

public class DBHelper {
	private static final String serialFieldName = "serialVersionUID";
	public static String selectSql(Object obj) throws Exception{
		FieldAnnotationHandler fah = new IdFieldAnnotationHandler();
		StringBuilder sb = new StringBuilder();
		//1.拿到Class对象
		Class cl = obj.getClass();
		//2.拿到数据库表名的注解
		MyTable myTable = (MyTable)cl.getAnnotation(MyTable.class);
		String tblName = myTable.value();
		sb.append("select ");
		//3.获取对象全部定义的属性(包含私有属性)
		Field[] fields = cl.getDeclaredFields();
		//4.循环全部属性,获得注解名称和属性值
		for(Field f : fields) {
			//4.1 序列化字段无需处理
			if(f.getName().equals(serialFieldName)) {
				continue;
			}
			//4.2 拿到属性上的全部注解
			Annotation[] ans = f.getAnnotations();
			//4.3 循环处理全部注解,获得数据库表的字段名和它对应的值
			for(Annotation an : ans) {
				//使用责任链模式来处理每个属性上的注解(由于我这里假设只会出现MyId和MyField注解,因此只写了两个Handler)
				MyEntry entry = fah.handler(an , f , obj);
				if(entry.isNotEmpty()) {
					sb.append(entry.getKey()+",");
					break;
				}
			}
		}
		//5.剔除最后的","号
		sb = new StringBuilder(sb.toString().substring(0, sb.toString().lastIndexOf(",")!=-1?sb.toString().lastIndexOf(","):sb.toString().length()));
		
		sb.append(" from ").append(tblName);
		
		//处理主键
		MyEntry primaryEntry = ((IdFieldAnnotationHandler)fah).getPrimaryEntry();
		if(primaryEntry.isNotEmpty()) {
			sb.append(" where ").append(primaryEntry.getKey()).append(" = ").append(primaryEntry.getValue());
		}
		
		return sb.toString();
	}
}

4.处理属性和值的Handler

抽象类

public abstract class FieldAnnotationHandler {
	FieldAnnotationHandler handler;

	public FieldAnnotationHandler getHandler() {
		return handler;
	}

	public void setHandler(FieldAnnotationHandler handler) {
		this.handler = handler;
	}
	
	public abstract MyEntry handler(Annotation an,Field f,Object obj) throws Exception;
}

主键处理Handler

/*
 * 和其余的Handler不同,增长了primaryEntry属性,用于记住主键的信息
 */
public class IdFieldAnnotationHandler extends FieldAnnotationHandler{
	private MyEntry primaryEntry;
	
	public MyEntry getPrimaryEntry() {
		return primaryEntry;
	}
	
	public IdFieldAnnotationHandler(){
		this.primaryEntry = new MyEntry();
		this.handler = new MyFieldAnnotationHandler();
	}

	@Override
	public MyEntry handler(Annotation an,Field f,Object obj) throws Exception{
		MyEntry entry = new MyEntry();
		if(an instanceof MyId) {
			MyId myId = (MyId)an;
			primaryEntry.setKey(myId.value());
			if(primaryEntry.getKey() !=null && !primaryEntry.getKey().equals("")) {
				entry.setKey(primaryEntry.getKey());
				Method method = obj.getClass().getMethod("get"+f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1), null);
				entry.setValue(method.invoke(obj, null));
				primaryEntry.setValue(entry.getValue());
				return entry;
			}else {
				entry.setKey(f.getName());
				entry.setValue(f.get(obj));
				return entry;
			}
		}else {
			return handler.handler(an, f,obj);
		}
	}
	
}

字段处理Handler

public class MyFieldAnnotationHandler extends FieldAnnotationHandler{
	
	public MyFieldAnnotationHandler(){
	}

	@Override
	public MyEntry handler(Annotation an,Field f,Object obj) throws Exception{
		MyEntry entry = new MyEntry();
		if(an instanceof MyField) {
			MyField myField = (MyField)an;
			String columnName = myField.value();
			if(columnName != null && !columnName.equals("")) {
				entry.setKey(columnName);
				Method method = obj.getClass().getMethod("get"+f.getName().substring(0, 1).toUpperCase() + f.getName().substring(1), null);
				entry.setValue(method.invoke(obj, null));
				return entry;
			}else {
				entry.setKey(f.getName());
				entry.setValue(f.get(obj));
				return entry;
			}
		}else {
			return new MyEntry();
		}
	}
	
}

测试类

public class TestSql {
	@Test
	public void test() throws Exception {
		System.out.println(DBHelper.selectSql((new Cust(123,"Messi","巴塞罗那"))));
	}
}

输出:

select custId,custName from tb_cust where custId = 123

我并无按照反射的API一一写代码测试,有兴趣的朋友能够看中文或者官方的API

相关文章
相关标签/搜索