个人文章均用作个人学习记录,所以里面包含了很多自言自语的话。抱歉。
注解
写在前面的话,个人对于 注解 这个概念的理解。
什么是注解?从本质上来说,注解就是一个标签,实际上可以将其认为是一种特殊的注释,如果没有任何代码解析它的话, 那么它并没有任何实际的意义和用途。(甚至不如普通的注释)
我个人认为,注解只是某些数据的载体,而并不是用于执行某个动作,所以很多时候我们自定义注解之后(一般将自定义注解的有效性设置为RUNTIME,即运行时有效),仍然需要通过 反射 来进行一些额外的操作,使得注解变得“有用”起来
感觉对于注解的内核有了一定程度的认识,原来只知道注解长这样,完全不知道内在是什么。
自定义注解
自定义注解的语法要求
下面是自定义注解的一个示例
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description{
String desc();
String author();
int age() default 18;
}
下面是对其解释:
-
@interface:如果要自定义注解,那么必须使用@interface关键字来定义注解 -
String desc();、String author()、int age() default 18;- 以上三个可不是什么所谓的成员方法,首先必须明确: 自定义的注解中没有 也不能包含所谓的方法;
- 以上三个实际上是注解的元素,他们是注解的一部分,用于存储注解的配置数据。
- 这些元素在定义时确实需要使用括号
(),但这不是方法调用的括号,而是注解元素的默认值定义。(个人认为简单理解为 成员变量可能还稍微可行) - 可以使用
default关键字给成员指定一个默认值 比如上面的int age() default 18; - ▲ 成员的类型实际上是受限的,合法的类型包括基本数据类型 、String、Class、Annotation、Enumeration ; 如果注解只有一个成员,那么成员名必须取名为value(),在使用的时候可以忽略成员名和赋值号( = )
- 注解类可以没有成员,没有成员的注解称为标识注解。
元注解
元注解,简单来说就是注解的注解。
仍然是上面的例子:
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description{
String desc();
String author();
int age() default 18;
}
-
@Target指定注解可以修饰的元素类型-
ElementType是一个枚举类型,它定义了被@Target修饰的注解可以应用的范围:ElementType.ANNOTATION_TYPE- 标记的注解可以应用于注解类型。ElementType.CONSTRUCTOR- 标记的注解可以应用于构造函数。ElementType.FIELD- 标记的注解可以应用于字段或属性。ElementType.LOCAL_VARIABLE- 标记的注解可以应用于局部变量。ElementType.METHOD- 标记的注解可以应用于方法。ElementType.PACKAGE- 标记的注解可以应用于包声明。ElementType.PARAMETER- 标记的注解可以应用于方法的参数。ElementType.TYPE- 标记的注解可以应用于类的任何元素。
-
-
@Retention指明注解的保留级别-
RetentionPolicy是一个枚举类型,它定义了被@Retention修饰的注解所支持的保留级别:RetentionPolicy.SOURCE- 标记的注解仅在源文件中有效,编译器会忽略。RetentionPolicy.CLASS- 标记的注解在 class 文件中有效,JVM 会忽略。RetentionPolicy.RUNTIME- 标记的注解在运行时有效。
-
-
@Inherited表示注解类型可以被继承(默认情况下不是这样)。- 表示自动继承注解类型。 如果注解类型声明中存在
@Inherited元注解,则注解所修饰类的所有子类都将会继承此注解。
- 表示自动继承注解类型。 如果注解类型声明中存在
-
@Documented表示无论何时使用指定的注解,都应使用 Javadoc(默认情况下,注释不包含在 Javadoc 中)
自定义注解例子
1、自定义注解@Table
package com.nylonmin.AnnotationTest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解Table
* 该注解可放在类上@Target({ElementType.TYPE})
* 并且设置为运行时有效@Retention(RetentionPolicy.RUNTIME)
* @author nylonmin
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
2、自定义注解@Column
package com.nylonmin.AnnotationTest;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解Table
* 该注解可放在成员对象上@Target({ElementType.FIELD})
* 并且设置为运行时有效@Retention(RetentionPolicy.RUNTIME)
* @author nylonmin
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value();
}
3、定义类Filter 并将注解加上去
package com.nylonmin.AnnotationTest;
@Table("user")
public class Filter {
@Column("id")
private int id;
@Column("userName")
private String userName;
@Column("nickName")
private String nickName;
@Column("age")
private int age;
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;
}
}
4、定义Test类 其中一个main方法 另一个静态方法query 将Filter类的对象中的成员变量变为sql查询语句的条件
package com.nylonmin.AnnotationTest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception {
Filter filter = new Filter();
filter.setAge(12);
filter.setUserName("张三");
String query = query(filter);
System.out.println(query);
}
/**
* 用于将对象Filter f中的成员变量转化为一个sql查询语句
* @param f 传入的Filter类的对象
* @return 返回sql语句 String类型
* @author nylonmin
*/
public static String query(Filter f) throws Exception {
//创建StringBuilder类对象result,表示最终返回的sql语句
StringBuilder result =new StringBuilder();
//Step1:获取Class
Class c = f.getClass();
//Step2: 获取注解中的table名称
boolean annotationPresent = c.isAnnotationPresent(Table.class);
if(!annotationPresent){
System.out.println("无Table注解,无法获取表名称");
return null;
}
Table table = (Table)c.getAnnotation(Table.class);
//这里挺神奇的,在末尾加上where 1=1
result.append("select * from ").append(table.value()).append(" where 1=1");
//Step3 : 获取所有的Field(成员变量) 及其上面的注解
Field[] Fields = c.getDeclaredFields();
for(Field field:Fields){
boolean fieldAnnotationPresent = field.isAnnotationPresent(Column.class);
if(!fieldAnnotationPresent){
continue;
}
Column fieldAnnotation = field.getAnnotation(Column.class);
//拿到注解的值 (实际上这里指代sql语句中的字段名称了)
String columnName = fieldAnnotation.value();
//拿到字段的值
//首先要拿到对应的get方法
//比方说我们取到的是id 那么其get方法的名称应该是 getId 即字段名第一个字母要大写
String methodName = "get"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
Method method = c.getDeclaredMethod(methodName);
//调用get方法获取对应字段的值
Object fieldValue = method.invoke(f,null);
//如果是null 或者返回值为Integer类型且值为0 跳过
if(fieldValue == null || (fieldValue instanceof Integer && ((Integer) fieldValue).intValue()==0)){
continue;
}
//如果值为String类型 则要加上 单引号
if(fieldValue instanceof String){
result.append(" and ").append(columnName).append("=").append("'").append(fieldValue).append("'");
}
//否则就直接=
else {
result.append(" and ").append(columnName).append("=").append(fieldValue);
}
}
return result.toString();
}
}