注解(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 { }
@Deprecated
用来修饰过期的元素,在使用高版本JDK时常常遇到该注解。编译时会发出警告,通知开发人员它是已过期的类或者方法
提示子类中的方法是重写的
忽略编译器发出的警告,在添加了这个注解,eclipse左边的小黄感叹号就会消失
Jdk 1.7 加入的新注解,用于提醒开发者不要用参数作一些不安全的操做,它的存在会阻止编译器产生 unchecked 这样的警告,可是运行时程序可能会报错。
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