0
点赞
收藏
分享

微信扫一扫

Java自定义注解

才德的女子 2022-01-25 阅读 78

目录

概念

注解:也被称作元数据,为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。(实际意义上来说,注解仅仅是一种特殊的注释而已,如果没有解析它的代码,它可能啥作用也没有)
标记注解:内部没有定义元素的注解。
单值注解 : 内部只定义一个元素,名字为value,使用时直接传值,不需要指定元素名。

语法

定义注解

自定义注解格式如下,其中@interface是用来声明一个注解,使用@interface自定义注解时将自动继承java.lang.annotation.Annotation接口,并由编译程序自动完成其他细节,通过反编译可以查看到。元注解及注解元素内容详见下文。

元注解
修饰符 @interface 注解名 {   
    注解元素的声明1 
	注解元素的声明2 
	...  
}

自定义一个MyAnnotation注解,并声明一个名为value的注解元素:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
	String value() default "test";
}

MyAnnocation注解反编译结果:

Compiled from "MyAnnocation.java"
public interface com.bbu.annotation.MyAnnotation extends java.lang.annotation.Annotation {
  public abstract java.lang.String value();
}

通过反编译结果可以看出,注解本质上就是一个接口,该接口默认继承Annotation接口。

元注解

Java提供了四种元注解,主要用于程序员自定义注解时使用。

@Target:表示该注解可以用于什么地方

  • ElementType.TYPE:仅用于类、接口、枚举
  • ElementType.FIELD:仅用于属性
  • ElementType.METHOD:仅用于方法(非构造方法)
  • ElementType.CONSTRUCTOR:仅用于构造方法
  • ElementType.PARAMETER:仅用于方法的参数
  • ElementType.LOCAL_VARIABLE:仅用于局部变量
  • ElementType.ANNOTATION_TYPE:仅用于注解
  • ElementType.PACKAGE:仅用于包

@Retention:表示注解的生命周期(编译,.class,运行)

  • RetentionPolicy.SOURCE:注解将被编译器丢弃,仅源文件(.java)阶段有效;
  • RetentionPolicy.CLASS:注解在class文件中可用,但会被VM丢弃(源文件(.java)阶段以及字节码文件阶段(.class)有效);
  • RetentionPolicy.RUNTIME:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息(源文件、字节码以及运行时都有效)

@Documented:将此注解包含在Javadoc中。通过javadoc命令生成API文档时,注解也会生成到文档中;默认(不写@Documented时)不会把注解生成到文档中。

@Inherited:允许子类继承父类中的注解。

Java 1.8新增了一种元注解:
@Repeatable:标识注解是可重复使用的;在同一个地方使用相同的注解会报错,有了此元注解注解的注解,就可以在同一个地方使用相同的注解。

注解元素

  • 所有基本类型(四类八种:byte、short、int、long、float、double、char、boolean)
  • String
  • Class
  • enum
  • Annotation
  • 上述所有类型对应的数组

除以上类型外,如果使用到了其他类型,那么编译器就会报错。注意,也不允许使用任何包装类型,不过由于自动打包的存在,这算不是什么限制。

public enum Status {// 枚举
	SUCCESS,
	FAILURE
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
	int id() default -1;// 基本类型
	String value() default "";// String
	Class<?> clz() default String.class;// Class
	Status enu() default Status.SUCCESS;// Enum
	String[] values();// 数组
	// Integer id() default -1;// 报错(Invalid type Integer for the annotation attribute MyAnnocation.ids; only primitive type, String, Class, annotation, enumeration are permitted or 1-dimensional arrays thereof)
}

注解也可以做为元素的类型,也就是说注解可以嵌套:

package com.bbu.annocation;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE, ElementType.FIELD})
@Inherited
public @interface Alias {
	// 注解做为元素的类型
	MyAnnotation annotation() default @MyAnnotation(values = { "" });
}

注解元素设定规则:
  第一,只能用public或默认(default)这两个访问权修饰;
  第二,如果只有一个参数成员,最好把参数名称设为"value",这样的话在注解中写值的时候可以省略value;
  第三,首先,注解元素不能有不确定的值。也就是说,元素必须要么具有默认值,要么在使用注解时提供元素值。其次,对于非基本类型的元素,无论是在源代码中声明,或是在注解接口中定义默认值时,都不能以null作为值,这个约束使得处理器很难表现一个元素的存在或缺失状态,因为每个注解的声明中,所有的元素都存在,并且都具有相应的值,为了绕开这个约束,只能定义一些特殊的值,例如空字符串或负数,以此表示某个元素不存在。

Java内置注解

Java SE5开始,内置了三种标准注解,定义在java.lang中:

  • @Override:表示当前的方法将覆盖父类中的方法;若方法签名对不上被覆盖的方法,编译器就会发出错误提示。
  • @Deprecated:如果某个元素被该注解修饰,程序员使用了该元素,编译器会发出警告信息。
  • @SuppressWarnings:关闭不当的编译器警告信息。
    使用方式:
    @SuppressWarnings("")
    @SuppressWarnings({"", “”})
    @SuppressWarnings(value={"", “”})

Jdk1.8后增加了一个新的注解:

  • @FunctionalInterface:用于声明一个接口为函数式接口

四中Java内置注解源码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

可以看出@Override、@Deprecated和@FunctionalInterface三个注解中没有注解元素,是一个标记注解;@SuppressWarnings中的注解元素是一个String数组,比较常见的值有rawtypes(使用泛型时忽略没有指定相应的类型)、unchecked(抑制没有进行类型检查操作的警告)及all(抑制所有警告)等。

用法

注解与反射

在Class中定义了反射操作注解常用的方法:

  • public < A extends Annotation> A getAnnotation(Class< A> annotationClass):返回指定的注解
  • public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判定当前元素是否被指定注解修饰
  • public Annotation[] getAnnotations():返回所有的注解
  • public < A extends Annotation> A getDeclaredAnnotation(Class< A> annotationClass):返回本元素的指定注解
  • public Annotation[] getDeclaredAnnotations():返回本元素的所有注解,不包含父类继承而来的

通过以上方法得到注解Annotation实例后,我么就可以通过注解元素方法直接获取对应注解中设置的值,如@MyAnnocation(“user_id”)注解,我们可以通过getAnnotation方法获取annotation实例,然后annotation.value()获取"user_id"这个值。

反射解析注解生成SQL案例

首先自定义一个名为MyAnnotation的注解,内部设置一个注解元素value,通过@Retention元注解设置为运行时有效,这样才能使用反射动态获取注解实例,通过@Target元注解设置类、接口、枚举及属性可以使用。

package com.bbu.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解MyAnnotation
 * @author code_now
 *
 */
@Retention(RetentionPolicy.RUNTIME)// 设置为运行时也有效
@Target({ElementType.TYPE, ElementType.FIELD})// 设置类、接口、枚举及属性可以使用
public @interface MyAnnotation {
	String value() default "";// 注解元素value
}

自定义一个User类,将对应数据库中表名及列名通过@MyAnnocation注解对应起来。

package com.bbu.model;

import com.bbu.annocation.MyAnnotation;

/**
 * 自定义实体类 
 * @author code_now
 *
 */
@MyAnnocation("t_user")
public class User {

	@MyAnnocation("user_id")
	private Integer userId;
	
	@MyAnnocation("user_name")
	private String userName;
	
	@MyAnnocation("age")
	private Integer age;
	
	@MyAnnocation("home_address")
	private String homeAddress;
	
	private String desc;

	public Integer getUserId() {
		return userId;
	}

	public void setUserId(Integer userId) {
		this.userId = userId;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getHomeAddress() {
		return homeAddress;
	}

	public void setHomeAddress(String homeAddress) {
		this.homeAddress = homeAddress;
	}

	public String getDesc() {
		return desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	@Override
	public String toString() {
		return "User [userId=" + userId + ", userName=" + userName + ", age="
				+ age + ", homeAddress=" + homeAddress + ", desc=" + desc + "]";
	}
	
}

通过反射获取类及属性上对应的表名和列名,然后拼出最终的查询SQL。

package com.bbu.test;

import java.lang.reflect.Field;
import com.bbu.annocation.MyAnnotation;

/**
 * 解析注解通过反射生成SQL语句 
 * @author code_now
 *
 */
public class GenerateSQL {

	public static void main(String[] args) throws Exception {

        Class<?> clz = Class.forName("com.bbu.model.User");// 获取User类的Class对象
        StringBuffer sb = new StringBuffer();// 用于拼接sql
        sb.append("select ");
        String temp1 = "";
        Field[] declaredFields = clz.getDeclaredFields();// 获取User类中所有属性
        for (Field field : declaredFields) {
        	field.setAccessible(true);
			MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);// 获取属性上对应的注解实例
			if(annotation != null) {
				String value = annotation.value();
				sb.append(temp1+value);
				temp1 = ",";
			}
		}
        
        sb.append(" from ");
        MyAnnotation annotation = clz.getAnnotation(MyAnnotation.class);// 获取类上对应的注解实例
        if(annotation != null) {
        	String value = annotation.value();
        	sb.append(value);
        }
        
        System.out.println(sb.toString());
    }

}

最终生成的SQL:

select user_id,user_name,age,home_address from t_user

参考

Java编程思想(第4版)

举报

相关推荐

0 条评论