Java 注解
注解(Annotation)是 Java 5 引入的一种元数据机制,用于在代码中添加信息,这些信息可以在编译时或运行时被读取和处理。
什么是注解?
注解是一种特殊的接口,它为代码提供元数据。注解本身不会直接影响代码的执行,但可以通过工具或框架来处理这些注解,实现特定功能。
注解的作用
- 编译时检查:如
@Override帮助编译器检查方法重写 - 代码生成:如 Lombok 自动生成 getter/setter
- 运行时处理:如 Spring 的依赖注入
- 文档生成:如 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...
小结
本章我们学习了:
- 注解基础:什么是注解、注解的作用
- 内置注解:@Override、@Deprecated、@SuppressWarnings、@FunctionalInterface
- 自定义注解:定义注解、属性类型、默认值
- 元注解:@Target、@Retention、@Documented、@Inherited、@Repeatable
- 运行时注解处理:通过反射读取类、方法、字段注解
- 实际应用:ORM 框架、权限控制、依赖注入
练习
- 创建一个
@Author注解,用于标注代码作者和日期 - 创建一个
@TestCase注解,用于标记测试方法 - 实现一个简单的验证框架,使用注解验证字段值
- 实现一个简单的 AOP 框架,使用注解标记切面方法