跳到主要内容

Java 注解

注解(Annotation)是 Java 5 引入的一种元数据机制,用于在代码中添加信息,这些信息可以在编译时或运行时被读取和处理。

什么是注解?

注解是一种特殊的接口,它为代码提供元数据。注解本身不会直接影响代码的执行,但可以通过工具或框架来处理这些注解,实现特定功能。

注解的作用

  1. 编译时检查:如 @Override 帮助编译器检查方法重写
  2. 代码生成:如 Lombok 自动生成 getter/setter
  3. 运行时处理:如 Spring 的依赖注入
  4. 文档生成:如 Javadoc 注解

内置注解

Java 提供了一些常用的内置注解:

@Override

标识方法重写父类方法:

public class Animal {
public void speak() {
System.out.println("动物发出声音");
}
}

public class Dog extends Animal {
@Override // 编译器检查是否正确重写
public void speak() {
System.out.println("汪汪");
}

// @Override // 编译错误:不是重写方法
// public void run() { }
}

@Deprecated

标识已过时的元素:

public class OldClass {
@Deprecated(since = "2.0", forRemoval = true)
public void oldMethod() {
// 使用此方法会收到编译警告
}

/**
* @deprecated 使用 newMethod() 代替
*/
@Deprecated
public void legacyMethod() {
oldMethod();
}

public void newMethod() {
// 新方法
}
}

// 使用时会收到警告
OldClass obj = new OldClass();
obj.oldMethod(); // 警告:已过时

@SuppressWarnings

抑制编译器警告:

public class Demo {
// 抑制未检查警告
@SuppressWarnings("unchecked")
public void method() {
List list = new ArrayList(); // 原始类型,通常会有警告
list.add("hello");
}

// 抑制多种警告
@SuppressWarnings({"unchecked", "deprecation"})
public void anotherMethod() {
// ...
}
}

常用警告类型:

参数说明
unchecked未检查的类型转换
deprecation使用已过时的 API
rawtypes使用原始类型
unused未使用的代码
null空指针分析
serial缺少 serialVersionUID

@FunctionalInterface

标识函数式接口(只有一个抽象方法):

@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);

// 可以有默认方法
default void print() {
System.out.println("Calculator");
}

// 可以有静态方法
static Calculator add() {
return (a, b) -> a + b;
}

// 不能有第二个抽象方法
// int anotherMethod(); // 编译错误
}

// 使用 Lambda 表达式
Calculator calc = (a, b) -> a * b;

自定义注解

定义注解

// 基本注解定义
public @interface MyAnnotation {
String value();
int count() default 0;
}

// 使用
@MyAnnotation(value = "test", count = 5)
public class MyClass { }

// value 是特殊属性,如果只有 value 需要指定,可以省略属性名
@MyAnnotation("test")
public class AnotherClass { }

注解的属性类型

public @interface AllTypes {
// 基本类型
int intValue();
double doubleValue();
boolean booleanValue();

// String
String stringValue();

// Class
Class<?> classValue();

// 枚举
Day enumValue();

// 注解
MyAnnotation annotationValue();

// 以上类型的一维数组
String[] stringArray();
int[] intArray();

// 不能使用其他类型,如包装类或 Object
// Integer wrong(); // 编译错误
}

注解的默认值

public @interface Defaults {
// 必须有默认值,或者使用时必须指定
String required();

// 有默认值
String optional() default "默认值";

// 数组默认值
String[] tags() default {};

// 枚举默认值
Day day() default Day.MONDAY;
}

元注解

元注解是用于修饰注解的注解,定义注解的行为。

@Target

指定注解可以应用的目标:

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

// 可以用在类、接口、枚举上
@Target(ElementType.TYPE)
public @interface ClassAnnotation { }

// 可以用在方法上
@Target(ElementType.METHOD)
public @interface MethodAnnotation { }

// 可以用在字段上
@Target(ElementType.FIELD)
public @interface FieldAnnotation { }

// 可以用在多个目标上
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface MultiAnnotation { }

// Java 8+ 新增:可以用在类型参数上
@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParamAnnotation { }

// Java 8+ 新增:可以用在类型使用上
@Target(ElementType.TYPE_USE)
public @interface TypeUseAnnotation { }

ElementType 取值:

说明
TYPE类、接口、枚举
FIELD字段
METHOD方法
PARAMETER参数
CONSTRUCTOR构造方法
LOCAL_VARIABLE局部变量
ANNOTATION_TYPE注解类型
PACKAGE
TYPE_PARAMETER类型参数(Java 8+)
TYPE_USE类型使用(Java 8+)

@Retention

指定注解的保留策略:

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

// 源码级别:只在源码中存在,编译后消失
// 用于编译时处理,如 @Override
@Retention(RetentionPolicy.SOURCE)
public @interface SourceAnnotation { }

// 类级别:编译后保留在 class 文件中,但 JVM 忽略
// 默认值
@Retention(RetentionPolicy.CLASS)
public @interface ClassAnnotation { }

// 运行时级别:保留到运行时,可以通过反射读取
// 用于运行时处理,如 Spring 注解
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnnotation { }

@Documented

指定注解是否包含在 Javadoc 中:

import java.lang.annotation.Documented;

@Documented
public @interface ApiDoc {
String value();
}

// 使用后,Javadoc 会包含此注解信息
@ApiDoc("这是一个 API 方法")
public void apiMethod() { }

@Inherited

指定注解是否被子类继承:

import java.lang.annotation.Inherited;

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

@InheritedAnnotation("父类注解")
public class Parent { }

// 子类会继承父类的 @InheritedAnnotation
public class Child extends Parent { }

// 通过反射检查
InheritedAnnotation annotation = Child.class.getAnnotation(InheritedAnnotation.class);
System.out.println(annotation.value()); // "父类注解"

@Repeatable

Java 8 引入,允许同一注解多次使用:

import java.lang.annotation.Repeatable;

// 容器注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedules {
Schedule[] value();
}

// 可重复的注解
@Repeatable(Schedules.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedule {
String day();
int hour() default 0;
}

// 使用方式1:单独使用
@Schedule(day = "周一")
@Schedule(day = "周三")
@Schedule(day = "周五", hour = 14)
public class Task { }

// 使用方式2:使用容器
@Schedules({
@Schedule(day = "周一"),
@Schedule(day = "周三"),
@Schedule(day = "周五", hour = 14)
})
public class AnotherTask { }

运行时注解处理

通过反射读取运行时注解:

读取类注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
String name();
}

@Table(name = "users")
public class User {
// ...
}

// 读取类注解
Class<User> clazz = User.class;
if (clazz.isAnnotationPresent(Table.class)) {
Table table = clazz.getAnnotation(Table.class);
System.out.println("表名: " + table.name()); // 表名: users
}

// 获取所有注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType().getName());
}

读取方法注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
String value() default "";
boolean enable() default true;
}

public class Service {
@Log("执行用户查询")
public void queryUser() { }

@Log(value = "保存用户", enable = false)
public void saveUser() { }
}

// 读取方法注解
Class<Service> clazz = Service.class;
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Log.class)) {
Log log = method.getAnnotation(Log.class);
System.out.println("方法: " + method.getName());
System.out.println("描述: " + log.value());
System.out.println("启用: " + log.enable());
}
}

读取字段注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String name();
String type() default "VARCHAR";
int length() default 255;
}

public class User {
@Column(name = "id", type = "INT")
private int id;

@Column(name = "username", length = 50)
private String username;

@Column(name = "email")
private String email;
}

// 读取字段注解
Class<User> clazz = User.class;
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
System.out.println("字段: " + field.getName());
System.out.println("列名: " + column.name());
System.out.println("类型: " + column.type());
System.out.println("长度: " + column.length());
}
}

实际应用示例

1. 简单的 ORM 框架

// 表名注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Entity {
String table() default "";
}

// 列注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Id {
String column() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String name() default "";
String type() default "VARCHAR";
}

// 主键注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface PrimaryKey { }

// 实体类
@Entity(table = "t_user")
public class User {
@PrimaryKey
@Column(name = "id", type = "INT")
private int id;

@Column(name = "username")
private String username;

@Column(name = "password")
private String password;

// getter/setter...
}

// ORM 工具类
public class SimpleORM {
public static String getTableName(Class<?> clazz) {
Entity entity = clazz.getAnnotation(Entity.class);
return entity != null && !entity.table().isEmpty()
? entity.table()
: clazz.getSimpleName().toLowerCase();
}

public static String getInsertSQL(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
StringBuilder sql = new StringBuilder("INSERT INTO ");
sql.append(getTableName(clazz)).append(" (");

StringBuilder values = new StringBuilder(" VALUES (");
boolean first = true;

for (Field field : clazz.getDeclaredFields()) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
if (!first) {
sql.append(", ");
values.append(", ");
}
String columnName = column.name().isEmpty()
? field.getName()
: column.name();
sql.append(columnName);

field.setAccessible(true);
Object value = field.get(obj);
if (value instanceof String) {
values.append("'").append(value).append("'");
} else {
values.append(value);
}
first = false;
}
}

sql.append(")").append(values).append(")");
return sql.toString();
}
}

// 使用
User user = new User();
user.setId(1);
user.setUsername("张三");
user.setPassword("123456");

String sql = SimpleORM.getInsertSQL(user);
System.out.println(sql);
// INSERT INTO t_user (id, username, password) VALUES (1, '张三', '123456')

2. 方法权限控制

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequireRole {
String[] value();
}

public class UserController {
@RequireRole("ADMIN")
public void deleteUser(int userId) {
System.out.println("删除用户: " + userId);
}

@RequireRole({"ADMIN", "MANAGER"})
public void updateUser(int userId) {
System.out.println("更新用户: " + userId);
}

public void getUser(int userId) {
System.out.println("获取用户: " + userId);
}
}

// 权限检查器
public class PermissionChecker {
public static void checkPermission(Object obj, String methodName, String userRole)
throws Exception {
Class<?> clazz = obj.getClass();
Method method = clazz.getMethod(methodName, int.class);

if (method.isAnnotationPresent(RequireRole.class)) {
RequireRole required = method.getAnnotation(RequireRole.class);
boolean hasPermission = false;
for (String role : required.value()) {
if (role.equals(userRole)) {
hasPermission = true;
break;
}
}
if (!hasPermission) {
throw new SecurityException("没有权限执行此操作");
}
}
}
}

// 使用
UserController controller = new UserController();
PermissionChecker.checkPermission(controller, "deleteUser", "ADMIN"); // OK
PermissionChecker.checkPermission(controller, "updateUser", "MANAGER"); // OK
// PermissionChecker.checkPermission(controller, "deleteUser", "USER"); // 抛出异常

3. 依赖注入

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject { }

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

@Component
public class UserService {
public void serve() {
System.out.println("UserService serving...");
}
}

@Component
public class OrderService {
@Inject
private UserService userService;

public void process() {
userService.serve();
System.out.println("OrderService processing...");
}
}

// 简单的依赖注入容器
public class SimpleContainer {
private static Map<Class<?>, Object> instances = new HashMap<>();

public static <T> T getInstance(Class<T> clazz) throws Exception {
if (instances.containsKey(clazz)) {
return clazz.cast(instances.get(clazz));
}

T instance = clazz.getDeclaredConstructor().newInstance();
instances.put(clazz, instance);

// 注入依赖
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
Object dependency = getInstance(field.getType());
field.set(instance, dependency);
}
}

return instance;
}
}

// 使用
OrderService orderService = SimpleContainer.getInstance(OrderService.class);
orderService.process();
// UserService serving...
// OrderService processing...

小结

本章我们学习了:

  1. 注解基础:什么是注解、注解的作用
  2. 内置注解:@Override、@Deprecated、@SuppressWarnings、@FunctionalInterface
  3. 自定义注解:定义注解、属性类型、默认值
  4. 元注解:@Target、@Retention、@Documented、@Inherited、@Repeatable
  5. 运行时注解处理:通过反射读取类、方法、字段注解
  6. 实际应用:ORM 框架、权限控制、依赖注入

练习

  1. 创建一个 @Author 注解,用于标注代码作者和日期
  2. 创建一个 @TestCase 注解,用于标记测试方法
  3. 实现一个简单的验证框架,使用注解验证字段值
  4. 实现一个简单的 AOP 框架,使用注解标记切面方法