Java开发系列-注解

概述

在 JDK5 之后提供了一个新特性,和类、接口同级。定义时使用的关键字是@interface。注解主要有三个作用,分别是编译检查、替代配置文件、定义注解 (元注解)、分析代码 (用到反射)。注解的本质就是接口,可通过反编译注解的字节码文件。

Java 中的 3 个常用注解

@Override

@Override注解主要是用于编译检查,子类重写父类的方法,重写的方法上面有该注解,一旦我们修改重写方法名就会报错。当我们删除@Override,再修改就不会报错了,这样编译器会认为这个方法是开发者自定义的方法

@Override
public String toString() {
	return super.toString();
}

@SuppressWarnings

@SuppressWarnings注解作用是用于消除警告。比如我们定义一个变量,当变量未使用时,编译器会提示警告信息。对于这些警告如果你不想被提示,可以在方法名添加该注解。注解属性有很多取值,一般我们赋值 "all",就意味着消除所有⚠️。

@SuppressWarnings("all")
public static void main(String[] args) {
	int a = 10;
}

@Deprecated

@Deprecated注解适用于提示方法不建议使用,可能改方法有 bug 或者有新的方法替代了。如果我们调用该注解表明的方法会有中划线提示。

如果我们写一个框架对外提供的接口想要告诉调用者该方法已经过期。也可以使用该注解声明。

@Deprecated
public static void test(){

}


自定义注解


注解的定义与基本使用

自定义一个注解跟类、接口格式一样,只是修饰的关键字是@interface。概述中说过注解的本质就是接口,那么跟接口一样,接口中可以有常量和抽象方法。抽象方法在注解中就称之为注解属性

public @interface MyAnnotation {
<span class="hljs-comment">// 定义一个注解属性age</span>
<span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">age</span><span class="hljs-params">()</span>;
<span class="hljs-keyword">public</span> String[] names();

}

在 MyAnnotation 注解定义了一个 test 属性,该注解属性类型为 int。 注意:注解属性类型只支持基本数据类型、Class、String、Annotation、Enum枚举

定义了 MyAnnotation 注解后,我们就可以在其它类中使用注解。

package com.coderhong.annotation;

// 给注解属性 test 赋值 10
@MyAnnotation(age =10, names="{jake, rose}")
public class MyAnnotationExample {

}

一旦注解中声明了属性,使用注解是必须对所有属性赋值,否者报错。 当注解只有一个属性,且该属性名为 value,在给注解属性赋值是可以省略属性名。

public @interface MyAnnotation {
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">value</span><span class="hljs-params">()</span>;

}

// 省略属性名
@MyAnnotation("rose")
public class MyAnnotationExample {

}


元注解

上面的自定义注解我们将注解作用在类上,其实它还可以作用在接口、方法、字段等上面。那么这个通过什么机制去控制呢,这就需要了解元注解。元注解就是作用在注解上的注解。我们常见的元注解@Retention@Target

@Retention

定义注解保留到什么阶段

@Retention 取值 注解保留到什么阶段
RetentionPolicy.SOURCE 只在代码中保留,在字节码文件中就删除了
RetentionPolicy.CLASS 只在代码和字节码文件中都保留
RetentionPolicy.CLASS 只在代码和字节码文件中都保留
RetentionPolicy.RUNTIME 所有阶段都保留

@Retention 使用示例:

//@Retention(RetentionPolicy.RUNTIME)
//@Retention(RetentionPolicy.SOURCE)
//@Retention(RetentionPolicy.CLASS)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">value</span><span class="hljs-params">()</span>;

}


@Target

@Target 规定注解作用在什么上面。

@Target 取值 作用目标
ElementType.TYPE 作用在类、接口等上面
ElementType.METHOD 作用在方法上面
ElementType.FIELD 作用在字段上面
//@Target(ElementType.TYPE)
//@Target(ElementType.METHOD)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">value</span><span class="hljs-params">()</span>;

}


注解的使用示例


通过注解模拟 JUint 实现

自定义一个注解 MyAnnotation 跟两个 Java 类 Test、MainClass。 通过执行 MainClass 类中的 main 方法调用 Test 类中所有被 MyAnnotation 修饰的方法。
这里主要运用到的技术就是注解 + 反射。通过反射获取 Test 类中所有方法,遍历方法数组拿到每一个 Method 对象,通过 isAnnotationPresent(); 判断方法是否被某个注解修饰。

Boolean flag = method.isAnnotationPresent(MyAnnotation.class);

然后调用。

这里主要看下 MainClass 类中的实现

// 执行 main 执行 TestClass 中有 MyAnnotation 注解声明的方法
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
<span class="hljs-comment">// 获取TestClass的字节码文件</span>
<span class="hljs-type">Class</span> <span class="hljs-variable">clazz</span> <span class="hljs-operator">=</span> TestClass.class;

<span class="hljs-comment">// 获取所有方法</span>
Method[] methods = clazz.getMethods();

<span class="hljs-comment">// 遍历所有方法</span>
<span class="hljs-keyword">for</span> (Method method : methods) {
	<span class="hljs-comment">// 判断方法时候有指定注解</span>
	<span class="hljs-type">Boolean</span> <span class="hljs-variable">flag</span> <span class="hljs-operator">=</span> method.isAnnotationPresent(MyAnnotation.class);
	<span class="hljs-comment">// 判断是否包含MyAnnotation注解 如果包含就执行方法</span>
	<span class="hljs-keyword">if</span>(flag){
		method.invoke(clazz.newInstance());
	}
}

}


注解替代 JDBC 的配置文件

定义一个注解JDBCAnnotation,声明属性对应了 JDBC 获取连接所需的参数信息。在工具类JDBCUtils的 getConnection 方法声明注解并对注解属性进行赋值。在调用 getConnection 方法获取连接时,通过映射技术获取 getConnection 的 Method,载通过 Method 的 getAnnotation 获取该方法的注解,从而获取注解属性值。

JDBCAnnotation

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface JDBCAnnotation {

<span class="hljs-comment">// default 给注解属性设置默认值</span>
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">DriverClass</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-string">"com.mysql.jdbc.Driver"</span>;
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">url</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/myDB"</span>;
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">user</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-string">"root"</span>;
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">password</span><span class="hljs-params">()</span>;

}

JDBCUtils

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JDBCUtils {

<span class="hljs-meta">@JDBCAnnotation(password="123456", user="root")</span>
 <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Connection <span class="hljs-title function_">getConnection</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> NoSuchMethodException, SecurityException, ClassNotFoundException, SQLException{
	
	<span class="hljs-type">Class</span> <span class="hljs-variable">clazz</span> <span class="hljs-operator">=</span> JDBCUtils.class;
	
	<span class="hljs-comment">// 获取getConnection对应的Method</span>
	<span class="hljs-type">Method</span> <span class="hljs-variable">method</span> <span class="hljs-operator">=</span> clazz.getMethod(<span class="hljs-string">"getConnection"</span>);
	
	<span class="hljs-comment">// 判断是否包含@JDBCAnnotation注解</span>
	<span class="hljs-keyword">if</span>(method.isAnnotationPresent(JDBCAnnotation.class)){
		<span class="hljs-comment">// 通过Method获取注解</span>
		<span class="hljs-type">JDBCAnnotation</span> <span class="hljs-variable">annotation</span> <span class="hljs-operator">=</span> method.getAnnotation(JDBCAnnotation.class);
		
		<span class="hljs-comment">// 通过注解获取属性value</span>
		<span class="hljs-type">String</span> <span class="hljs-variable">driverClass</span> <span class="hljs-operator">=</span> annotation.DriverClass();
		<span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> annotation.url();
		<span class="hljs-type">String</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> annotation.user();
		<span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> annotation.password();
		
		<span class="hljs-comment">// 注册驱动</span>
		Class.forName(driverClass);
		
		<span class="hljs-comment">// 获取连接</span>
		<span class="hljs-type">Connection</span> <span class="hljs-variable">connection</span> <span class="hljs-operator">=</span> DriverManager.getConnection(url, user, password);
		 <span class="hljs-keyword">return</span> connection;
	}
	 
	<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
	
}

}