全面解析Java注解
一、 注解概述
1. JDK自带注解
- @Override 告诉编译器该方法覆盖了父类的方法
- @Deprecated 表示该方法已经过时
- @Suppvisewarnings 表示忽略了”deprecation”警告
2. 常见第三方注解
Spring中常见注解:@Autowired, @Service,@Repository
Mybatis中常见注解:@InsertProvider,@UpdataProvider,@Options
3. Java注解的分类
- 按运行机制分类
- 源码注解:注解只在源码中存在,编译成.class文件就不存在了。
- 编译时注解:注解在源码和.class文件中都存在,如JDK自带注解。
- 运行时注解:在运行阶段依然起作用,甚至会影响程序的运行逻辑。
- 按注解来源分类:
- JDK自带注解
- 第三方注解
- 自定义注解
二、自定义注解
1. 定义注解
自定义注解的语法要求
- 使用@interface关键字定义注解,其中成员必须以无参无异常方式声明,同时可以用default为成员指定一个默认值;
- 自定义注解的成员类型是受限的,合法的类型包括原始类型、String、Class、Annotation和Enumeration;
- 如果注解只有一个成员,那么成员名必须取名为value(),并且在使用时可以忽略成员名和赋值号(=);
- 注解类可以没有成员,没有成员的注解称为标识注解。
元注解说明
- @Target: 指定注解的作用域:
- CONSTRUCTOR,构造方法
- FIELD,属性
- LOCAL_VARIABLE,局部变量
- METHOD,方法
- PACKAGE,包
- PARAMETER,参数
- TYPE 类或接口
- @Retention: 指定注解的生命周期
- SOURCE,源码级别,编译时丢失
- CLASS,编译级别,运行时丢失
- RUNTIME,运行级别,可以通过反射获取
- @Inherited: 标识注解允许子类继承
- @Documented: 指定生成javadoc时会包含注解
例1: 自定义注解的实例:
@Target({ElementType.METHOD, ElementType.Type})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
2. 使用自定义注解
使用注解的语法: @<注解名>(<成员名1>=<成员值1>, <成员名2>=<成员值2>, …)
例2:使用自定义注解的实例
@Description(desc="I am eyeColor", author="Mooc Boy", age=18)
public String eyeColor() {
return "red";
}
三、解析注解
解析注解:通过反射获取类、函数或成员上的运行时注解信息,从而实现动态控制程序的运行逻辑。
例1:下面通过实例演示解析注解的过程
(1) 定义使用自定义注解的父类
@Description(desc = "I am annotation in parent class", author = "Parent")
public class Parent {
@Description(desc = "I am annotation in parent method", author = "Parent")
public String name() {
return "name:parent";
}
}
(2) 定义使用自定义注解的子类
@Description(desc = "I am annotation in child class", author = "Child")
public class Child extends Parent {
@Description(desc = "I am annotation in child method", author = "Child")
@Override
public String name() {
return "name:child";
}
}
(3) 解析类和方法上的注解
public class Analyzer {
public static void main(String[] args) {
try {
// 类注解解析方式
Class clazz = Class.forName("Child");
boolean exists = clazz.isAnnotationPresent(Description.class);
if(exists) {
Description description = (Description) clazz.getAnnotation(Description.class);
System.out.println(description.desc());
}
// 第一种方法注解解析方式
Method[] methods = clazz.getMethods();
for(Method method : methods) {
exists = method.isAnnotationPresent(Description.class);
if(exists) {
Description description = method.getAnnotation(Description.class);
System.out.println(description.desc());
}
}
// 第二种方法注解解析方法
for(Method method:methods) {
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation:annotations) {
if(annotation instanceof Description) {
Description description = (Description) annotation;
System.out.println(description.desc());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
解析结果如下:
I am annotation in child class
I am annotation in child method
I am annotation in child method
注意:
- @Inherited只能实现类上注解的继承,而无法实现接口上注解的继承,即接口的注解无法影响到实现接口的类上面。
- 父类方法的注解也无法被子类继承。
例2:修改注解如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
此时解析结果没有输出,说明该注解只作用于编译阶段,运行时注解丢失
例3:修改子类如下
public class Child extends Parent {
@Override
public String name() {
return "name:child";
}
}
此时解析结果如下,说明父类方法的注解无法被子类方法继承:
I am annotation in parent class
例4:修改父类如下
@Description(desc = "I am annotation in parent interface", author = "Parent")
public interface Parent {
String name();
}
修改子类如下:
public class Child implements Parent {
@Override
public String name() {
return "name:child";
}
}
此时解析结果没有输出,说明接口的注解无法影响到实现接口的类。
四、项目实战
定义类:
public class User {
private int id;
private String username;
private String nickname;
private int age;
private String city;
private String email;
private String mobile;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
需求:给定上面展示的实体类及设置了相关属性的对象,实现实体对象对应的SQL查询语句
public static void main(String[] args) {
// 查询id为10的用户信息
User user1 = new User();
user1.setId(10);
// 查询username为"lucy"的用户信息
User user2 = new User();
user2.setUsername("lucy");
// 查询email在"liu@sina.com,zh@163.com,777@qq.com"中的用户信息
User user3 = new User();
user3.setEmail("liu@sina.com,zh@163.com,777@qq.com");
String sql1 = query(user1);
String sql2 = query(user2);
String sql3 = query(user3);
System.out.println(sql1);
System.out.println(sql2);
System.out.println(sql3);
}
实现:通过自定义注解和注解解析实现SQL转化过程
(1) 定义注解Table
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
(2) 定义注解Column
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
(3) 给实体类添加注解
@Table("user")
public class User {
@Column("id")
private int id;
@Column("username")
private String username;
@Column("nickname")
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 String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
(4) 通过注解解析实现转化过程
private static String query(User user) {
StringBuilder sb = new StringBuilder();
Class clazz = user.getClass();
boolean exists = clazz.isAnnotationPresent(Table.class);
if(!exists)
return null;
Table table = (Table) clazz.getAnnotation(Table.class);
String tableName = table.value();
sb.append("select * from ").append(tableName).append(" where 1=1");
Field[] fields = clazz.getDeclaredFields();
for(Field field:fields) {
exists = field.isAnnotationPresent(Column.class);
if(!exists)
continue;
Column column = field.getAnnotation(Column.class);
String fieldName = field.getName();
String methodName = "get"+fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
Object fieldValue=null;
try {
Method method = clazz.getMethod(methodName);
fieldValue = method.invoke(user);
} catch (Exception e) {
e.printStackTrace();
}
if(fieldValue==null || (fieldValue instanceof Integer && (Integer)fieldValue==0)){
continue;
}
sb.append(" and ").append(fieldName);
if(fieldValue instanceof String) {
if(((String)fieldValue).contains(",")) {
String[] values = ((String)fieldValue).split(",");
sb.append(" in (");
for(String value: values) {
sb.append("'").append(value).append("'").append(",");
}
sb.deleteCharAt(sb.length()-1);
sb.append(")");
}else {
sb.append("=").append("'").append(fieldValue).append("'");
}
}else {
sb.append("=").append(fieldValue);
}
}
return sb.toString();
}
(4) 测试输出结果:
select * from user where 1=1 and id=10
select * from user where 1=1 and username='lucy'
select * from user where 1=1 and email in ('liu@sina.com','zh@163.com','777@qq.com')