Java注解浅谈

川长思鸟来 2022-09-24 04:29 357阅读 0赞
  1. Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。注解的功能类似于代码中的注释,所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用,用来简化程序中的配置。开发过程中,我们也时常在应用代码中会看到诸如@Override@Deprecated这样的注解。

Java SE5内置了三种标准注解:

  1. @Override,表示当前的方法定义将覆盖超类中的方法。
  2. @Deprecated,使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。
  3. @SuppressWarnings,关闭不当编译器警告信息。

上面这三个注解多少我们都会在写代码的时候遇到。Java还提供了4中注解,专门负责新注解的创建。即元注解。

一.元注解。Java5.0定义的4种元注解:

  1.   1.@Target,
  2.   2.@Retention,
  3.   3.@Documented,
  4.   4.@Inherited

下面,分别看看这4种注解的详细说明。

1.1 @Target。(注解用于什么地方)

@Target说明了Annotation所修饰的对象范围,注解用于什么地方。如果不明确指出,该注解可以放在任何地方。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

  1. CONSTRUCTOR:构造器的声明
  2. FIELD:域声明(包括enum实例)
  3. LOCAL_VARIABLE:局部变量声明
  4. METHOD:方法声明
  5. PACKAGE:包声明
  6. PARAMETER:参数声明
  7. TYPE:类、接口(包括注解类型)或enum声明

1.2 @Retention。(什么时候使用该注解。定义该注解的生命周期)

@Retention定义了该Annotation被保留的时间长短。表示需要在什么级别保存该注解信息。

作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

取值(RetentionPoicy)有:

  1. SOURCE 注解将被编译器丢弃(在源文件中有效,即源文件保留)
  2. CLASS 注解在class文件中可用,但会被VM丢弃(在class文件中有效,即class保留)
  3. RUNTIMEVM将在运行期间保留注解,因此可以通过反射机制读取注解的信息(在运行时有效,即运行时保留)

1.3 @Documented。(将注解包含在Javadoc中)
@Documented一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。因此可以被javadoc此类的工具文档化。@Documented是一个标记注解,没有成员。有关标记注解,下文有说明。

1.4 @Inherited。(是否允许子类继承该注解)

@Inherited阐述了某个被标注的类型是被继承的。@Inherited是一个标记注解,没有成员。有关标记注解,下文有说明。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

二. 自定义注解。

  1. 使用@interface自定义注解。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、ClassStringenum)。可以通过default来声明参数的默认值。

定义注解格式:

  1. public @interface 注解名 {定义体}

注解参数的可支持数据类型:

  1.     1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  2.     2.String类型
  3.     3.Class类型
  4.     4.enum类型
  5.     5.Annotation类型
  6.     6.以上所有类型的数组

Annotation类型里面的参数该怎么设定:
  第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;   
  第二,参数成员数据类型如上所述;  
  第三,如果只有一个参数成员,最好把参数名称设为”value”。

实例代码:

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Table {
  4. }

ps: 1. 没有元素的注解称为标记注解,上面的@Table就是一个标记注解。

  1. 2. 如果只有一个参数成员,最好把参数名称设为"value",例如,
  2. @Target({ElementType.TYPE})
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface Table {
  5. String value();
  6. }

在使用注解时,可以不写属性名和等于号,直接写值即可,例如可以这么写@Table(“user”)。

自定义完注解后,最核心的东西便是要实现注解解析!注解解析主要是通过反射机制实现的。有关反射机制,可以看这篇文章,Java反射浅谈。此处不再详说。

三. 注解解析。

  1. 首先,看看JDK提供的有关注解相关的API方法,

3.1 isAnnotationPresent(Class<? extends Annotation> annotationClass),

如果指定的注解位于此元素上,则返回‘true’,否则返回‘false’。

3.2 T getAnnotation(Class annotationClass),

如果存在此注释,则返回此元素的指定类型的注释,否则为空。

3.3 Annotation[] getAnnotations(),

返回此元素上的所有注释。如果这个元素上没有注释,则返回值是一个长度为0的数组。此方法的调用方可以自由修改返回的数组;它将对返回给其他调用方的数组没有影响。

3.4 Annotation[] getDeclaredAnnotations(),

返回直接在该元素上的注释。此方法忽略了继承的注释。如果在这个元素上没有直接存在的注释,返回值是一个长度为0的数组。此方法的调用方可以自由修改返回的数组;它将对返回给其他调用方的数组没有影响。

下文,结合具体的场景,来实现注解以及注解解析。

从原理上讲,注解解析器就是通过反射机制获取被检查方法上的注解信息,然后根据注解元素的值进行特定的处理。

四. 注解实战。

  1. 打算使用注解生成SQL语句。

4.1 自定义注解。

(1) 自定义一个Table(表)注解,作用域是类,在运行时有效,只有一个属性,所有命名为‘value’,

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Table {
  4. String value();
  5. }

(2) 自定义一个Column(列)注解,作用域是类,在运行时有效,只有一个属性,所有命名为‘value’,

  1. @Target({ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Column {
  4. String value();
  5. }

4.2 定义一个实体类,使用注解,具体代码如下,

  1. @Table("user")
  2. public class User {
  3. @Column("id")
  4. private int id;
  5. @Column("user_name")
  6. private String userName;
  7. @Column("nick_name")
  8. private String nickName;
  9. @Column("age")
  10. private int age;
  11. @Column("city")
  12. private String city;
  13. @Column("email")
  14. private String email;
  15. @Column("mobile")
  16. private String mobile;
  17. public int getId() {
  18. return id;
  19. }
  20. public void setId(int id) {
  21. this.id = id;
  22. }
  23. public String getUserName() {
  24. return userName;
  25. }
  26. public void setUserName(String userName) {
  27. this.userName = userName;
  28. }
  29. public String getNickName() {
  30. return nickName;
  31. }
  32. public void setNickName(String nickName) {
  33. this.nickName = nickName;
  34. }
  35. public int getAge() {
  36. return age;
  37. }
  38. public void setAge(int age) {
  39. this.age = age;
  40. }
  41. public String getCity() {
  42. return city;
  43. }
  44. public void setCity(String city) {
  45. this.city = city;
  46. }
  47. public String getEmail() {
  48. return email;
  49. }
  50. public void setEmail(String email) {
  51. this.email = email;
  52. }
  53. public String getMobile() {
  54. return mobile;
  55. }
  56. public void setMobile(String mobile) {
  57. this.mobile = mobile;
  58. }
  59. }

其中‘@Table(“user”)’表示数据库中表名,而类似‘@Column(“id”) private int id;’表示 该字段(属性)在表中对应的列名。
注解部分已经实现了,接下来就需要实现注解解析,具体代码如下,

4.3 注解解析,具体代码如下,

  1. /**
  2. * 生成sql
  3. * @param f
  4. * @return
  5. */
  6. private static String query(Object f) {
  7. StringBuilder builder=new StringBuilder();
  8. //1 获取class
  9. Class c=f.getClass();
  10. //是否有Table注解
  11. boolean exists=c.isAnnotationPresent(Table.class);
  12. if(!exists){
  13. return null;
  14. }
  15. //2 获取table的名字
  16. Table t=(Table)c.getAnnotation(Table.class);
  17. String tableName=t.value();
  18. builder.append("select * from ").append(tableName).append(" where 1=1");
  19. //3 遍历所有字段
  20. Field[] fArray=c.getDeclaredFields();
  21. for(Field field:fArray)
  22. {
  23. //4 处理每个字段对应的sql
  24. // //是否有Column注解
  25. boolean fExists=field.isAnnotationPresent(Column.class);
  26. if(!fExists){
  27. continue;
  28. }
  29. // 4.1 拿到字段名
  30. Column column=field.getAnnotation(Column.class);
  31. String columnName=column.value();
  32. //4.2 拿到字段值
  33. String fieldName=field.getName();
  34. String getMethodName="get"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1);
  35. Method getMethod;
  36. Object fieldValue = null;
  37. try {
  38. getMethod = c.getMethod(getMethodName);
  39. fieldValue=(Object) getMethod.invoke(f);
  40. } catch (Exception e) {
  41. // TODO Auto-generated catch block
  42. e.printStackTrace();
  43. }
  44. //如果值为null或者值为Integer类型并且等于0,则不生成sql
  45. if(fieldValue==null||(fieldValue instanceof Integer && (Integer)fieldValue==0)){
  46. continue;
  47. }
  48. // 4.3 组装sql
  49. builder.append(" and ").append(fieldName);
  50. //
  51. if(fieldValue instanceof String){
  52. //生成模糊查询的sql
  53. if(((String) fieldValue).contains(",")){
  54. String[] values=((String) fieldValue).split(",");
  55. builder.append(" in(");
  56. for (String v:values) {
  57. builder.append("'").append(v).append("'").append(",");
  58. }
  59. builder.deleteCharAt(builder.length()-1);
  60. builder.append(")");
  61. }else{
  62. builder.append("=").append("'").append(fieldValue).append("'");
  63. }
  64. }else if(fieldValue instanceof Integer){
  65. builder.append("=").append(fieldValue);
  66. }
  67. }
  68. return builder.toString();
  69. }

代码都有注释,不多说了。下面,我们测试一下这个注解解析器生成的sql,

(1) 测试代码1,

  1. public static void main(String[] args) {
  2. User f1=new User();
  3. f1.setId(10);
  4. User f2=new User();
  5. f2.setUserName("xingxing");
  6. User f3=new User();
  7. f3.setEmail("123456789@163.com");
  8. String sql1=query(f1);
  9. String sql2=query(f2);
  10. String sql3=query(f3);
  11. System.out.println(sql1);
  12. System.out.println(sql2);
  13. System.out.println(sql3);
  14. }

运行结果截图如下,

Center

(2) 测试代码2

  1. public static void main(String[] args) {
  2. User f1=new User();
  3. f1.setEmail("123456789@163.com,wangwei@sina.com,zhuhe@qq.com");
  4. String sql1=query(f1);
  5. System.out.println(sql1);
  6. }

运行结果截图如下,

Center 1

通过注解生成的sql与我们想要的sql基本一致。

五、总结。

通过本篇文章,我们对注解有一个新的认识!如果你觉得很炫,那么赶紧动手吧!

发表评论

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

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

相关阅读

    相关 java

    java与c语言 java是一门面向对象编程的强类型语言,一切东西都要抽象为对象,具有封装、继承、多态三个特性。这门语言常用于开发网站,与c语言不同,c语言是面向

    相关 Java反射

        JAVA反射机制(Reflection)是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对J象,都能够调用它的任意一个方法;这种动态获取的

    相关 Java注解

           自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分。注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。注解使得Java源代码

    相关 java

         Java是面向对象的程序设计语言,同普通的面向对象一样,java也同样具有三大特征:封装,继承和多态。此处就不对其做过多赘述了。只提一点:Java不支持多重继承。

    相关 Spring注解

    Spring目前的趋势是使用注解结合Java代码而不是配置来定义行为、属性、功能、规则和扩展点,因此梳理注解也是梳理Spring功能点的很好的方式,全面的梳理可以补足我们知识点