JAVA注解
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
1.注解分类
1.1按照运行机制分类
- 源码注解
注解只在源码中存在,编译成.class文件就不存在了
- 编译时注解
注解在源码和.class文件中都存在
比如:
@Override
@Deprecated
@Suppvisewarnings
- 运行时注解
在运行阶段还起作用,甚至会影响运行逻辑的注解。
1.2.按照来源分类
- 来自JDK的注解
- 来自第三方的注解
- 我们自己定义的注解
2.自定义注解
2.1.自定义注解的语法要求
- 在类上使用@interface关键字定义注解
- 成员以无参无异常方式声明
- 可以用default为成员指定一个默认值
- 成员类似是受限的,合法的类型包括原始类型以及String,Class,Annotation,Enumeration
- 如果注解只有一个成员,则成员名必须取名为Value(),在使用时可以忽略成员名和赋值号(=)
- 注解类可以没有成员,没有成员的注解称为标识注解
2.2.注解的注解(元注解)
是注解的注解,称为元注解
- Retention注解
这种类型的注解会被保留到那个阶段,
RetentionPolicy.SOURCE : 只在源码显示,编译时会丢弃
RetentionPolicy.CLASS :编译时会记录到class中,运行时忽略
RetentionPolicy.RUNTIME : 运行时存在,可以通过反射读取
- Inherited 注解
允许子类继承,
标识一个普通类继承一个父类,但是这个父类被该一个自定义注解(自定义注解上面有Inherited注解)标注的时候,子类可以继承父类上的注解
- Documented 注解
生产Javadoc的时候包含注解(默认是不包含的)
- Target 注解
标识该注解的作用域,例如:ElementType.Type 就只能标注在class类上面
2.3.使用自定义注解
- 使用注解的语法
@注解名(成员1=成员值1,成员2=成员2值….)
接下来,编写一个自定义注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 一个简单的自定义注解
*/
@Target({ElementType.TYPE,ElementType.FIELD}) //可以让本注解标识在类上,和属性上
@Retention(RetentionPolicy.RUNTIME) //表示在运行时可以获取该注解
@Inherited //标识一个普通类继承一个父类,但是这个父类被Person注解标注的时候,子类可以继承父类上的注解类型
@Documented //生产javadoc的时候,包含注解
public @interface Desc {
String detail(); //详细描述信息
String value() default "这个人很懒,什么都没有留下";
}
解析注解
通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
下面通过完成一个获取商品信息的小栗子来介绍下注解的解析
1. 定义一个父类,抽象商品的信息
/**
* 商品信息父类
*/
@Desc(value="抽象类Goods类上的value值",detail="抽象类Goods类上的详细描述")
public abstract class Goods {
@Desc(detail="抽象类Goods属性上的详细描述")
public String initInfo = "默认出生"; //商品的初始化信息介绍
}
- 定义具体的商品类
/** 商品苹果 */
public class Apple extends Goods{
public int price = 10; //价格
}
- 开始解析演示获取注解上面的信息
public class Test {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Apple apple = new Apple();
Class<? extends Apple> c = apple.getClass();
//获 取类上的指定类型注解
boolean flag = c.isAnnotationPresent(Desc.class);
if(flag){ //该类上面是否存在Desc 注解,@Inherited在这里就能看出来了,如果没有这个注解,这里是获取不到desc注解的
Desc classType = c.getAnnotation(Desc.class);
System.out.println(classType.value() + "," + classType.detail());
}
//获取属性上面的值
Field[] fields = c.getFields();
for (Field field : fields) {
if(field.isAnnotationPresent(Desc.class)){
Desc d = field.getAnnotation(Desc.class); //@Inherited 这里更加能看到,父类上的注解信息完全被继承了
System.out.println("属性:" + field.getName() + "上的注解的信息是:" + d.value() + d.detail());
}
}
}
}
执行结果:
抽象类Goods类上的value值,抽象类Goods类上的详细描述
属性:initInfo上的注解的信息是:这个人很懒,什么都没有留下抽象类Goods属性上的详细描述
3.综合小栗子
模拟一个简单的hibernate使用实体对象生成sql语句的例子
1.编写模拟表的注解
/** 模拟表的信息 **/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
2.编写模拟字段信息的注解
/** 模拟表字段信息 */
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
3.编写条件查询的对象
/** 条件查询对象 **/
public class Filter {
public static final String GT = "gt"; // 大于 >
// 装条件映射值
private Map<KV, String> map = new HashMap<KV, String>();
/** 设置条件
* @param key 属性名称
* @param value 对应的值
* @param type 查询的条件,默认为 == 。
*/
public void setValue(String key, String value,String type) {
map.put(new KV(key,value), type);
}
public Map<KV, String> getMap() {
return map;
};
}
class KV {
private String k;
private String v;
public KV(String k, String v) {
super();
this.k = k;
this.v = v;
}
public String getK() {
return k;
}
public void setK(String k) {
this.k = k;
}
public String getV() {
return v;
}
public void setV(String v) {
this.v = v;
}
}
4.编写实体类,使用我们自己编写的注解来映射表的信息
@Table("tab_apple")
public class Apple {
@Column("apple_name")
private String name; // 名称
@Column("apple_price")
private double price; // 价格
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
5.测试和编写生成sql的代码
import java.lang.reflect.Field;
import java.util.Map;
public class Test {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
Filter f = new Filter();
f.setValue("name", "苹果", null);
f.setValue("price", "5", Filter.GT);
String sql = query(f, Apple.class); //这里查询name=苹果并且价格大于 5 的商品信息的语句
System.out.println(sql); // 打印生成的sql语句
}
/**
* 生成sql语句
*
* @param f
* 条件对象
* @param clzz
* 实体表的类型
* @return
* @throws SecurityException
* @throws NoSuchFieldException
*/
public static String query(Filter f, Class<?> clzz) throws NoSuchFieldException, SecurityException {
// 1:获取实体上的注解信息,先生成基本的查询语句
if (!clzz.isAnnotationPresent(Table.class)) {
throw new RuntimeException("请传入一个实体类");
}
Table table = clzz.getAnnotation(Table.class);
String tableName = table.value();
boolean flag = false; // 追加sql的时候,标记sql语句后面是否已经有sql了
StringBuffer sb = new StringBuffer();
sb.append("select * from ").append(tableName);
// 2:获取条件所有的条件
Map<KV, String> map = f.getMap();
for (Map.Entry<KV, String> ent : map.entrySet()) {
KV kv = ent.getKey();
String type = ent.getValue();
Field field = clzz.getDeclaredField(kv.getK()); // 获取条件属性对应的属性对象
if (!field.isAnnotationPresent(Column.class)) { // 如果此属性不包含column注解标识,抛出异常
throw new RuntimeException("传入的参数有误");
}
Column column = field.getAnnotation(Column.class);
;
if (flag) {
sb.append(" and ").append(parse(column.value(), kv.getV(), type));
} else {
flag = true;
sb.append(" where ").append(parse(column.value(), kv.getV(), type));
}
}
return sb.toString();
}
/**
* 解析条件sql
*
* @param k
* 数据库表中的字段名称
* @param v
* 值
* @param type
* 条件类型
* @return
*/
public static String parse(String k, String v, String type) {
String sql = "";
if (type != null) {
if (type.equals(Filter.GT)) {
sql = k + " > " + v;
}
} else {
sql = k + " = " + v;
}
return sql;
}
}
执行结果:
select * from tab_apple where apple_name = 苹果 and apple_price > 5
写到最后
本例子很简陋。本人反射也没学好。将就着看吧
版权声明:本文为博主原创文章,未经博主允许不得转载。