thinking-in-java(20)注解

【1】注解介绍

1)注解是什么?

注解(元数据)是 java提供的一种对程序元素(如类,方法,变量)打标记的方法;以便运行程序或编译器可以识别这些元素进行特殊处理;最典型的用法是,框架代码启动时扫描某注解修饰的所有类并将其加载到内存,以构建系统底层框架更直白点,类,方法,变量被注解修饰后,更容易被识别以便加载

补充: 显然,开发或学习底层框架的同学肯定必不可少要学习注解;所以注解是相当重要的;


【2】java内置的三种注解 

1)@Override: 表示被标记的方法将覆盖超类中的方法;

2)@Deprecated:表示被标记的元素(类或方法)被废弃了 ; 如果代码使用该元素,则编译器会发出警告;

3)@SuppressWarnings关闭不当的编译器警告信息;


【3】4种标准元注解

1)元注解:元注解的作用是负责修饰其他注解(参与其他注解的定义或声明)。 java5.0 定义了4个标准的元注解类型; (干货——你也可以认为被注解修饰,就是被注解打标记

1.1)@Target元注解,用于指定被修饰注解可以应用到哪些元素上

如 注解可以被应用到 packages,type(类,接口,枚举,Annotation类),类型成员(方法,构造方法,成员变量,枚举值),方法参数和本地变量等;

注解定义例子1:

@Target(ElementType.METHOD) // 表示用于修饰(标记)方法
@Retention(RetentionPolicy.RUNTIME) // 表示运行时可用 
public @interface Test {} ///:~

注解应用例子1: 

public class Testable {
	public void execute() {
		System.out.println("Executing..");
	}

	@Test  // Test 注解用于修饰方法
	void testExecute() {
		execute();
	}
}

1.2)@Retention元注解,用于指定被修饰注解的生命周期

  • SOURCE:源文件有效;被编译器丢弃
  • CLASS:class文件有效;被JVM丢弃
  • RUNTIME :运行时有效(常用);VM将在运行期也保留注解,通过反射机制读取注解;

1.3)@Document  用于指定被修饰的注解可以被 javadoc 工具文档化;

1.4)@Inherited   用于指定被修饰的注解是可以被继承的

2)上面例子中修饰的注解 Test,是没有任何成员方法的。下面我们来看有成员方法的注解UseCase;·

// 下面定义了一个注解UseCase:该注解作用于方法,该注解在运行时发挥作用
// 该注解UseCase应用于什么地方
@Target(ElementType.METHOD)
// 该注解UseCase的应用级别,源代码-SOURCE, 类文件中-CLASS, 运行时-RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
	public int id();
	public String description() default "no description"; // no description 是默认值
}
/*
D:\workbench_idea\study4vw\thinkinjava\src>javap chapter20.UseCase
Compiled from "UseCase.java"
public interface chapter20.UseCase extends java.lang.annotation.Annotation {
  public abstract int id();
  public abstract java.lang.String description();
}
*/

反编译(javap)后的源码:可以看到,注解就是一个接口,该接口的父类是 Annotation 注解接口类型,当然了,接口中的方法都是抽象方法。

2.1)使用 UseCase注解;

// 荔枝-有3个方法被注解UseCase修饰为用例
public class PasswordUtils {
	// 将 UseCase 注解作用于方法
	@UseCase(id = 47, description = "validatePassword method")
	public boolean validatePassword(String password) {
		return (password.matches("\\w*\\d\\w*"));
	}

	@UseCase(id = 48) // description 默认为 no description
	public String encryptPassword(String password) {
		return new StringBuilder(password).reverse().toString();
	}

	@UseCase(id = 49, description = "checkForNewPassword method")
	public boolean checkForNewPassword(List<String> prevPasswords, String password) {
		return !prevPasswords.contains(password);
	}
}
/* 反编译简要结果如下:
D:\workbench_idea\study4vw\thinkinjava\src>javap chapter20.PasswordUtils
Compiled from "PasswordUtils.java"
public class chapter20.PasswordUtils {
  public chapter20.PasswordUtils();
  public boolean validatePassword(java.lang.String);
  public java.lang.String encryptPassword(java.lang.String);
  public boolean checkForNewPassword(java.util.List<java.lang.String>, java.lang.String);
}
*/

解析注解

/**
 * 注解解析测试用例
 */
public class AnnotationDiyTest {
    public final static <T> void  f1(Class<T> c1) {
        for (Method method : c1.getMethods()) { // 获取类定义方法
            UseCase useCase = method.getAnnotation(UseCase.class); // 获取方法上某个注解
            if (useCase != null) {
                System.out.println(useCase.id() + "-" + useCase.description()); // 解析注解
            }
        }
    }
    public static void main(String[] args) {
        f1(PasswordUtils.class);
    }
}
/*
47-validatePassword method
48-no description
49-checkForNewPassword method
*/

【4】自定义注解

UseCase注解有两个方法,方法返回类型有 int,String;此外还有其他返回类型,包括

所有基本类型;
String;
Class
enum;
Annotation;// 注解的成员方法的返回值类型还可以是注解类型
以及以上类型的数组; 

下面定义一个嵌套注解(即注解的方法返回类型是注解类型)

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
	int value() default 0;
	String name() default "";
	Constraints constraints() default @Constraints();// 嵌套注解
} // /:~
// 定义注解(并定义嵌套注解)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
	int value() default 0;
	String name() default "";
	// 嵌套注解,因为 Constraints 也是注解类型
	Constraints constraints() default @Constraints; 
} // /:~
// 荔枝-定义注解(并定义嵌套注解)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLDouble {
	double value() default 0f;
	// 嵌套注解,因为 Constraints 也是注解类型
	Constraints constraints() default @Constraints;
}
// 荔枝-定义注解用于生成一个数据库表
@Target(ElementType.TYPE) // 注解作用于哪里(注解的作用对象)
// Applies to classes only
@Retention(RetentionPolicy.RUNTIME) // 注解的作用时间(这里是运行时) 
public @interface DBTable {
	// DBTable 有一个name元素
	// 这个注解通过使用 value元素 为处理器创建数据库表提供的表名
	public String value() default "";
} // /:~
// 荔枝-为修饰 javabean域 准备的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
	boolean primaryKey() default false; // 主键
	boolean allowNull() default true; // 允许为空
	boolean unique() default false; // 唯一键
} // /:~

使用注解创建数据库表对应的javabean

// DBTable 注解作用于类
@DBTable("MEMBER")
public class Member {
	@SQLString(30) String firstName;
	@SQLString(value=50, name = "lastName") String lastName;
	@SQLInteger(name = "age", constraints = @Constraints(primaryKey=true, allowNull=false, unique=true)) 
	Integer age;
	@SQLString(value = 30 , name = "handle"
			, constraints = @Constraints(primaryKey = true))
	String handle;
	static int memberCount;

	public String getHandle() {
		return handle;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public String toString() {
		return handle;
	}

	public Integer getAge() {
		return age;
	}
} // /:~

使用注解的快捷方式:如果注解中定义了名为value元素(value方法),且在使用注解时,该元素是唯一一个需要复制的元素,那么只需要给出value元素的值即可,无需写键值对形式
@DBTable("MEMBER")-快捷方式 , @DBTable(value="MEMBER")-普通方式


【5】使用apt处理注解

1)问题, 上述代码中,都需要为注解编写处理器以解析注解; 很麻烦;

使用 apt类库方法可以生成注解处理器,apt 是使用源代码生成注解的,无法通过class文件获取类的属性;但 mirror api 可以允许程序员在源代码中查看类属性,如方法,属性;

2)注解

// 抽取注解
@Target(ElementType.TYPE) //
@Retention(RetentionPolicy.SOURCE) // 在源文件起作用 
public @interface ExtractInterface {
	public String value();
} // /:~

【6】小结

java se5仅提供了很少的注解,大多数情况下,需要自定义注解及注解处理器(特别是开发底层框架的时候,需要自定义注解);

javassist 能够用来操作字节码;mirror api 能够用来找出java源代码中的元素 (apt-mirror-api maven repo);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值