注解说明
Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法。
JDK1.5版本引入的一个特性,可以声明在包、类、属性、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释,可以理解为注解就是一个标记
好处
1.能够读懂别人写的代码,特别是框架相关的代码
2.让编程更加简洁,代码更加清晰
分类
运行机制划分:源码注解、编译时注解、运行时注解
按照来源划分:JDK注解、第三方注解、自定义注解
元注解:定义注解的注解
常见第三方注解
通常在框架中出现,如SpringMVC、Spring、Mybatis
分类解析
源码注解:注解只在源码中存在,编译成 .class
文件就不存在了,如@Override
,@SuppressWarnings
编译时注解:注解在源码和 .class
文件中都存在
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解,如spring的@Autowired
,jdk的@Deprecated
jdk注解
@Override
作用是表示该方法是重写或覆盖父类的方法声明
使用@Override
注解的方法必须重写父类或者java.lang.Object
中的一个同名方法
@Deprecated
表示该方法已过时,或者说有更好的方法取代了它
如果使用过时的方法,会报警告信息 The method xxx from the type xxx is deprecated
@SuppressWarnings
该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默
@SuppressWarnings("deprecation")
如使用了过时方法加上该注解即可消除警告信息
自定义注解
语法说明
- 使用
@interface
关键字定义注解,也称为注释类型public @interface XXX{}
- 成员以无参无异常方式声明,如
String desc();
- 可以使用
default
为成员指定一个默认值,如int age() default 18;
- 成员类型是受限的,合法类型包括原始类型及
String
、Class
、Annotation
、Enumeration
- 如果注解只有一个成员,则成员名必须取名为
value()
,在使用时可以忽略成员名和赋值号(=),直接@XXX(val)
- 注解类可以没有成员,没有成员的注解称为标识注解,如:
@Autowired
@Target元注解
语法: @target(ElementType[] value)
如: @Target({ElementType.METHOD,ElementType.TYPE})
作用: 指示该注解(注释类型)所适用的程序元素的种类,如果没有该元注解,则声明的类型可以用在任一程序元素上
ElementType:枚举类,程序元素类型,配合target
注解以指定在什么情况下使用注释类型是合法的,取值如下
@Retention元注解
语法:@Retention(RetentionPolicy value)
如: @Retention(RetentionPolicy.RUNTIME)
作用:指示注释类型的注释要保留多久,如果没有该元注解,则保留策略默认为 RetentionPolicy.CLASS
RetentionPolicy:枚举类,注释保留策略,取值如下
CLASS
编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释
RUNTIME
编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以通过反射读取
SOURCE
只在源码显示,编译时会丢弃
标识元注解
@Inherited
允许子类继承父类,对于实现接口的子类将不会继承父类注释类型,对于子类重写的方法将不会继承父类该方法的注释类型
@Documented
生成javadoc时会包含注解信息(在Eclipse中项目右键-->export-->javadoc)
使用注解语法
@<注解名>(<成员名1>=<成员值1>,<成员名2>=<成员值2>,...)
注意:注解上定义的全部成员使用时都必须指明(有默认值的成员除外)
自定义注解示例
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
//在某个类的方法中使用该注解
@Description(desc = "I am eyeColor", author = "silly", age = 22)
public String eyeColor(){
return "red";
}
解析注解
通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑
相关API
以下是Class
类、Method
类、Field
类所共有方法
Annotation
对象获取后,使用该对象.成员
即可获取注解上的成员值
解析示例
有如下自定义注解及使用注解的类
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author() default "ruoxiyuan";
int age() default 18;
}
package com.rxy.annotation;
@Description(author = "dubbo", desc = "I am person")
public class Person {
@Description(author = "zookeeper", desc = "I am person method")
public String name(){
return null;
}
public int age(){
return 0;
}
}
package com.rxy.annotation;
@Description(desc = "I am class Annotation")
public class Child extends Person {
@Override
@Description(author = "activeMQ", desc = "I am method Annotation", age=20)
public String name() {
return null;
}
@Override
public int age() {
return 0;
}
}
对注解类进行注解解析
/**
* 注解解析类
*/
public class ParseAnn {
public static void main(String[] args) {
//获取类上的注解
try {
//1.使用类加载器加载类,获取字节码对象
Class clazz = Class.forName("com.rxy.annotation.Child");
//2.判断类是否含有指定注解
boolean isExist = clazz.isAnnotationPresent(Description.class);
if(isExist){
//获取注解对象
Description desc = (Description)clazz.getAnnotation(Description.class);
//获取注解成员值
System.out.println(desc.author() + "," + desc.desc() + "," + desc.age());
}
//获取方法上的注解
Method[] methods = clazz.getMethods();
for(Method m : methods){
boolean isMExist = m.isAnnotationPresent(Description.class);
if(isMExist){
Description d = m.getAnnotation(Description.class);
System.out.println(m.getName()+ ":" + d.author() + "," + d.desc() + "," + d.age());
}
}
//另一种解析方式
for(Method m : methods){
Annotation[] as = m.getDeclaredAnnotations();
for(Annotation a : as){
if(a instanceof Description){
Description d = (Description)a;
System.out.println(d.author() + "," + d.desc() + "," + d.age());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/* 打印结果
* ruoxiyuan,I am class Annotation,18
* name:activeMQ,I am method Annotation,20
* activeMQ,I am method Annotation,20
*/
/*
* 将Child中类与方法中的注解全部注释
* 此时打印结果为
* dubbo,I am person,18
*/
/*
* 将Child中类与方法中的注解全部注释,将name方法也注释
* 此时打印结果为
* dubbo,I am person,18
* name:zookeeper,I am person method,18
* zookeeper,I am person method,18
*/
}
}
综合实战
项目取自一个公司的持久层架构,用来代替Hibernate的解决方案,核心代码就是通过注解来实现的。
需求:
1.有一张用户表,字段包括用户ID,用户名,昵称,年龄,性别,所在城市,邮箱,手机号
2.方便的对每个字段或字段的组合条件进行检索,并打印出SQL
3.使用方式要足够简单
定义注解
自定义注解用于映射类与表、属性与表字段的对应关系
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
实体类
实体类可以有多个,此处以用户表为例
@Table("user")
public class Filter {
//省略getset方法
@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;
}
定义注解解析类
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
//测试
Filter f1 = new Filter();
f1.setId(10);// 查询id为10的用户
Filter f2 = new Filter();
f2.setAge(18);
f2.setUserName("lucy");// 查询用户名为lucy年龄18的用户
Filter f3 = new Filter();
f3.setEmail("liu@sina.com,zh@163.com,77@qq.com");// 查询邮箱为其中任意一个的用户
String sql1 = query(f1);
String sql2 = query(f2);
String sql3 = query(f3);
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
/*select * from student where 1=1 and id=10
select * from student where 1=1 and user_name='lucy' and age=18
select * from student where 1=1 and email in('liu@sina.com','zh@163.com','77@qq.com')*/
}
/**
* 该方法可以对多张表对象进行通用解析
* @param f
* @return
*/
@SuppressWarnings("unchecked")
private static String query(Object f) {
StringBuilder sb = new StringBuilder();
// 1.获取字节码对象
Class clazz = f.getClass();
// 2.获取table名称
boolean isExist = clazz.isAnnotationPresent(Table.class);
if (!isExist) {
return null;
}
Table t = (Table) clazz.getAnnotation(Table.class);
String tableName = t.value();
sb.append("select * from ").append(tableName).append(" where 1=1");
// 3.遍历所有字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 4.处理每个字段对应的sql
boolean isFExist = field.isAnnotationPresent(Column.class);
if (!isFExist) {
continue;
}
// 4.1获取字段名称
Column col = field.getAnnotation(Column.class);
String colName = col.value();
// 4.2获取字段值
String fieldName = field.getName();
String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue = null;
try {
Method method = clazz.getMethod(getMethodName);
fieldValue = method.invoke(f, null);
} catch (Exception e) {
e.printStackTrace();
}
// 4.3拼接sql
if (fieldValue == null || (fieldValue instanceof Integer && (Integer) fieldValue == 0)) {
continue;
}
sb.append(" and ").append(colName);
if (fieldValue instanceof String) {
if (((String) fieldValue).contains(",")) {
String[] values = ((String) fieldValue).split(",");
sb.append(" in(");
for (String s : values) {
sb.append("'").append(s).append("'").append(",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append(")");
} else {
sb.append("=").append("'").append(fieldValue).append("'");
}
} else {
sb.append("=").append(fieldValue);
}
}
return sb.toString();
}
}
网友评论