自定义注解

痛定思痛。 2021-12-21 11:51 434阅读 0赞
  1. 注解是一种元数据形式,即注解是属于java的一种数据类型,和类、接口、数组、枚举类似。注解用来修饰,类、方法、变量、参数、包。注解不会对所修饰的代码产生直接的影响。
  2. 使用自定义注解的三个过程
  • 第一步,定义注解——相当于定义标记;
  • 第二步,配置注解——把标记打在需要用到的程序代码中;
  • 第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。

定义注解

注解所使用的关键字是@interface。在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。

列子:

public @interface CherryAnnotation {
public String name();
int age() default 18;
int[] array();
}

常用的元注解

  1. 有时候你会发现有些注解只能写在方法上面(比如@Override);有些却可以写在类的上面(比如@Deprecated)。那么这些定义该如何做呢?接下来就该元注解出场了!**元注解是专门修饰注解的注解**。

1.1 @Target
@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义如下:

public enum ElementType {
/** 类,接口(包括注解类型)或枚举的声明 */
TYPE,

  1. /\*\* 属性的声明 \*/
  2. FIELD,
  3. /\*\* 方法的声明 \*/
  4. METHOD,
  5. /\*\* 方法形式参数声明 \*/
  6. PARAMETER,
  7. /\*\* 构造方法的声明 \*/
  8. CONSTRUCTOR,
  9. /\*\* 局部变量声明 \*/
  10. LOCAL\_VARIABLE,
  11. /\*\* 注解类型声明 \*/
  12. ANNOTATION\_TYPE,
  13. /\*\* 包的声明 \*/
  14. PACKAGE

}

列子:

//@CherryAnnotation被限定只能使用在类、接口或方法上面
@Target(value = {ElementType.TYPE,ElementType.METHOD})
public @interface CherryAnnotation {
String name();
int age() default 18;
int[] array();
}

1.2 @Retention
@Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。
注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。同样使用了RetentionPolicy枚举类型定义了三个阶段:

public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* (注解将被编译器忽略掉)
*/
SOURCE,

  1. /\*\*
  2. \* Annotations are to be recorded in the class file by the compiler
  3. \* but need not be retained by the VM at run time. This is the default
  4. \* behavior.
  5. \* (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)
  6. \*/
  7. CLASS,
  8. /\*\*
  9. \* Annotations are to be recorded in the class file by the compiler and
  10. \* retained by the VM at run time, so they may be read reflectively.
  11. \* (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)
  12. \* @see java.lang.reflect.AnnotatedElement
  13. \*/
  14. RUNTIME

}

我们再详解一下:

如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到;
如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到;
如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME;
在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS。

1.3 @Documented
@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

1.4 @Inherited
@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。

自定义注解就完成了

例子:

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@Documented
public @interface CherryAnnotation {
String name();
int age() default 18;
int[] score();
}

接下来是应用

应用1:

package pojos;
public class Student {
@CherryAnnotation(name = “cherry-peng”,age = 23,score = {99,66,77})
public void study(int times){
for(int i = 0; i < times; i++){
System.out.println(“Good Good Study, Day Day Up!”);
}
}
}

通过反射获取注解内容

public class TestAnnotation {
public static void main(String[] args){
try {
//获取Student的Class对象
Class stuClass = Class.forName(“pojos.Student”);

  1. //说明一下,这里形参不能写成Integer.class,应写为int.class
  2. Method stuMethod = stuClass.getMethod("study",int.class);
  3. if(stuMethod.isAnnotationPresent(CherryAnnotation.class))\{
  4. System.out.println("Student类上配置了CherryAnnotation注解!");
  5. //获取该元素上指定类型的注解
  6. CherryAnnotation cherryAnnotation = stuMethod.getAnnotation(CherryAnnotation.class);
  7. System.out.println("name: " + cherryAnnotation.name() + ", age: " + cherryAnnotation.age()
  8. + ", score: " + cherryAnnotation.score()\[0\]);
  9. \}else\{
  10. System.out.println("Student类上没有配置CherryAnnotation注解!");
  11. \}
  12. \} catch (ClassNotFoundException e) \{
  13. e.printStackTrace();
  14. \} catch (NoSuchMethodException e) \{
  15. e.printStackTrace();
  16. \}
  17. \}

}

解释一下:

应用2:

Table 注解

package test;
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;

@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Table {
String value() default “”;
}

Column 注解

package test;

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;

@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
String value() default “”;
}

TestDto 对象

package test;
@Table(“tb_test”)
public class TestDto {

  1. @Deprecated
  2. private String tt;
  3. @Column("\_id")
  4. private String id;
  5. @Column("username")
  6. private String name;
  7. public TestDto(String id, String name) \{
  8. super();
  9. this.id = id;
  10. this.name = name;
  11. \}
  12. public String getId() \{
  13. return id;
  14. \}
  15. public void setId(String id) \{
  16. this.id = id;
  17. \}
  18. public String getName() \{
  19. return name;
  20. \}
  21. public void setName(String name) \{
  22. this.name = name;
  23. \}

}

测试注解

package test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
public static void main(String[] args) {
TestDto testDto = new TestDto(“123”, “34”);
TestDto testDto1 = new TestDto(“123”, “test1”);
TestDto testDto2 = new TestDto(“”, “test1,test2,test3,test4”);
String sql = assembleSqlFromObj(testDto);
String sql1 = assembleSqlFromObj(testDto1);
String sql2 = assembleSqlFromObj(testDto2);
System.out.println(sql);
System.out.println(sql1);
System.out.println(sql2);
}

  1. /\*\*
  2. \* 通过注解来组装查询条件,生成查询语句
  3. \*
  4. \* @param obj
  5. \* @return
  6. \*/
  7. public static String assembleSqlFromObj(Object obj) \{
  8. Table table = obj.getClass().getAnnotation(Table.class);
  9. StringBuffer sbSql = new StringBuffer();
  10. String tableName = table.value();
  11. sbSql.append("select \* from " + tableName + " where 1=1 ");
  12. Field\[\] fileds = obj.getClass().getDeclaredFields();
  13. for (Field f : fileds) \{
  14. String fieldName = f.getName();
  15. String methodName = "get" + fieldName.substring(0, 1).toUpperCase()
  16. + fieldName.substring(1);
  17. try \{
  18. Column column = f.getAnnotation(Column.class);
  19. if (column != null) \{
  20. Method method = obj.getClass().getMethod(methodName);
  21. String value = (String) method.invoke(obj);
  22. if (value != null && !value.equals("")) \{
  23. if (!isNum(column.value()) && !isNum(value)) \{
  24. // 判断参数是不是 in 类型参数 1,2,3
  25. if (value.contains(",")) \{
  26. sbSql.append(" and " + column.value() + " in (" + value + ") ");
  27. \} else \{
  28. sbSql.append(" and " + column.value() + " like '%" + value + "%' ");
  29. \}
  30. \} else \{
  31. sbSql.append(" and " + column.value() + "=" + value + " ");
  32. \}
  33. \}
  34. \}
  35. \} catch (Exception e) \{
  36. e.printStackTrace();
  37. \}
  38. \}
  39. return sbSql.toString();
  40. \}
  41. /\*\*
  42. \* 检查给定的值是不是 id 类型 1.检查字段名称 2.检查字段值
  43. \*
  44. \* @param target
  45. \* @return
  46. \*/
  47. public static boolean isNum(String target) \{
  48. boolean isNum = false;
  49. if (target.toLowerCase().contains("id")) \{
  50. isNum = true;
  51. \}
  52. if (target.matches("\\\\d+")) \{
  53. isNum = true;
  54. \}
  55. return isNum;
  56. \}

}

发表评论

表情:
评论列表 (有 0 条评论,434人围观)

还没有评论,来说两句吧...

相关阅读

    相关 定义注解

    1、简单介绍注解 注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明

    相关 定义注解

    什么是注解?         从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加

    相关 定义注解

             注解是一种元数据形式,即注解是属于java的一种数据类型,和类、接口、数组、枚举类似。注解用来修饰,类、方法、变量、参数、包。注解不会对所修饰的代码产生直接的