Java注解
本文最后更新于 2025-11-27,文章内容可能已经过时。
🌟 什么是Java注解?
Java注解(Annotation)是JDK 1.5引入的元数据机制,用于向代码添加补充信息,不影响程序执行逻辑。它就像给代码贴的"便利贴",告诉编译器、框架或工具:"这个方法很重要"、"这个类需要特殊处理"。
💡 关键区别:注释(// 或 /* */)是给程序员看的,编译器会忽略;注解是给程序(编译器、框架、工具)看的,程序可以通过代码读取注解信息,从而做出相应处理。
🔍 一、内置注解:你每天都在用
1. @Override:我重写了父类方法
class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
}
作用:告诉编译器这个方法是重写父类的方法。如果方法名写错了(比如eatt()),编译器会报错提醒。
2. @Deprecated:我过时了,别用我
class OldCalculator {
@Deprecated
public int add(int a, int b) {
return a + b;
}
public int plus(int a, int b) {
return a + b;
}
}
public class Test {
public static void main(String[] args) {
OldCalculator calc = new OldCalculator();
calc.add(1, 2); // IDE会提示:add()已过时,建议用plus()
calc.plus(1, 2); // 推荐使用
}
}
作用:标记方法/类已过时,提醒开发人员不要使用。
3. @SuppressWarnings:忽略警告
public class Test {
public static void main(String[] args) {
@SuppressWarnings("unused")
int a = 10; // 忽略"未使用变量"警告
}
}
作用:抑制特定的编译器警告,比如unchecked、deprecation等。
🧩 二、元注解:定义注解的注解
元注解是用于定义注解的注解,就像"给标签贴标签"。
| 元注解 | 作用 | 常见值 |
|---|---|---|
| @Retention | 注解保留策略 | SOURCE、CLASS、RUNTIME |
| @Target | 注解作用范围 | TYPE、METHOD、FIELD等 |
| @Inherited | 注解是否可继承 | true/false |
| @Documented | 是否包含在Javadoc中 | true/false |
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target(ElementType.METHOD) // 作用于方法
@Inherited // 可继承
@Documented // 包含在Javadoc中
public @interface MyAnnotation {
String value() default "默认值";
}
💡 重要提示:如果要通过反射读取注解,必须设置@Retention(RetentionPolicy.RUNTIME)
🛠️ 三、自定义注解:打造专属标签
1. 基本语法
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Important {
int level() default 1; // 1-3级,1最不重要,3最重要
String desc() default "未描述";
}
2. 使用自定义注解
public class UserService {
@Important(level = 3, desc = "核心业务方法")
public User getUserById(Long id) {
// 业务逻辑
return new User(id, "John Doe");
}
@Important(level = 1)
public void log(String message) {
System.out.println("日志: " + message);
}
}
3. 通过反射读取注解
public class AnnotationReader {
public static void main(String[] args) {
// 获取类上的注解
Class<UserService> clazz = UserService.class;
Important classAnnotation = clazz.getAnnotation(Important.class);
System.out.println("类上的重要性级别: " + (classAnnotation != null ? classAnnotation.level() : "无"));
// 获取方法上的注解
try {
Method method = clazz.getMethod("getUserById", Long.class);
Important methodAnnotation = method.getAnnotation(Important.class);
System.out.println("方法上的重要性级别: " + methodAnnotation.level());
System.out.println("方法描述: " + methodAnnotation.desc());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
💡 四、实际应用场景与详细示例
1. 框架配置:Spring的依赖注入
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findUserById(Long id) {
return userRepository.findById(id);
}
}
说明:@Service和@Autowired是Spring框架的注解,用于简化配置和依赖注入,无需XML配置。
2. 单元测试:JUnit的测试标记
import org.junit.Test;
import static org.junit.Assert.*;
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calc = new Calculator();
assertEquals(4, calc.add(2, 2));
}
@Test
public void testSubtract() {
Calculator calc = new Calculator();
assertEquals(2, calc.subtract(5, 3));
}
}
说明:@Test标记测试方法,测试运行器会自动执行这些方法并生成测试报告。
3. 持久化:JPA的实体映射
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username")
private String username;
@Column(name = "email")
private String email;
// getters and setters
}
说明:@Entity、@Table、@Id、@Column等注解描述数据库映射关系,框架可以自动生成SQL语句。
4. 日志记录:自定义注解 + AOP
// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
String value() default "方法执行";
}
// 使用注解
@Service
public class UserService {
@LogExecution("获取用户信息")
public User getUserById(Long id) {
// 业务逻辑
return new User(id, "John Doe");
}
}
// AOP切面
@Aspect
@Component
public class LogAspect {
@Before("@annotation(logExecution)")
public void logMethod(JoinPoint joinPoint, LogExecution logExecution) {
System.out.println("执行方法: " + joinPoint.getSignature().getName() + " - " + logExecution.value());
}
}
说明:通过自定义@LogExecution注解和AOP,实现方法执行日志记录,无需在每个方法中写日志代码。
5. 权限校验:自定义注解 + 拦截器
// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequireRole {
String[] roles() default {};
}
// 使用注解
@RestController
public class UserController {
@RequireRole(roles = {"ADMIN", "MANAGER"})
@GetMapping("/users")
public List<User> getAllUsers() {
// 业务逻辑
return userService.getAllUsers();
}
}
// 拦截器
@Component
public class RoleInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
RequireRole requireRole = method.getMethodAnnotation(RequireRole.class);
if (requireRole != null) {
// 检查用户角色
String[] roles = requireRole.roles();
// 检查当前用户是否在角色列表中
if (!isUserInRoles(roles)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Insufficient permissions");
return false;
}
}
}
return true;
}
}
说明:通过自定义@RequireRole注解和拦截器,实现基于角色的权限控制,无需在每个方法中写权限检查代码。
🌈 五、高级应用:可重复注解(Java 8+)
Java 8引入了可重复注解,允许在同一个程序元素上多次使用相同类型的注解。
// 定义可重复注解
@Repeatable(Roles.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Role {
String roleName();
}
// 定义容器注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Roles {
Role[] value();
}
// 使用
public class RoleTest {
@Role(roleName = "admin")
@Role(roleName = "user")
public void doSomething() {
// 业务逻辑
}
}
说明:Java 8之前,需要使用注解容器来实现重复注解,现在可以直接使用@Repeatable。
💎 六、总结:为什么注解这么重要?
- 减少样板代码:如Spring的@Service替代XML配置
- 提高代码可读性:通过注解直接表达设计意图
- 实现声明式编程:如AOP、权限控制等
- 框架集成:Spring、Hibernate等框架大量使用注解
- 元数据管理:为代码添加丰富的元数据信息
✨ 一句话总结:注解不是魔法,但用好它,代码会像被施了魔法一样简洁优雅。
💎Java注解完整自定义注解示例
🌟 示例目标
创建一个@LogExecution注解,用于:
- 记录方法执行时间
- 标记方法重要级别(info/warning/error)
- 在控制台输出日志
📜 完整代码示例
package com.nn3n;
import java.lang.annotation.*;
// ================== 1. 定义自定义注解 ==================
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface LogExecution {
String description() default "方法执行";
int level() default 1;
boolean enable() default true;
}
package com.nn3n;
public interface UserService {
User getUserDetails(Long userId);
User createUser(String name);
void deleteUser(Long userId);
}
package com.nn3n;
import java.util.concurrent.TimeUnit;
// ================== 3. 实现类(实现接口) ==================
public class UserServiceImpl implements UserService {
@LogExecution(description = "获取用户详情", level = 2)
@Override
public User getUserDetails(Long userId) {
System.out.println("正在获取用户详情... 参数: " + userId);
simulateDelay(150);
return new User(userId, "John Doe");
}
@LogExecution(description = "创建新用户", level = 1, enable = true)
@Override
public User createUser(String name) {
System.out.println("正在创建新用户... 参数: " + name);
simulateDelay(80);
return new User(1L, name);
}
@LogExecution(description = "删除用户", level = 3, enable = false) // 已禁用日志
@Override
public void deleteUser(Long userId) {
System.out.println("正在删除用户... 参数: " + userId);
simulateDelay(200);
}
private void simulateDelay(int milliseconds) {
try {
TimeUnit.MILLISECONDS.sleep(milliseconds);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
package com.nn3n;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// ================== 4. 实体类 ==================
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
}
package com.nn3n;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// ================== 2. 定义日志处理器(动态代理实现) ==================
public class LogInvocationHandler implements InvocationHandler {
private final Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ====== 添加调试信息 ======
Method targetMethod;
try {
targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
throw new RuntimeException("方法未找到: " + method.getName(), e);
}
System.out.println("DEBUG: 调用方法 [" + method.getName() + "]");
System.out.println("DEBUG: 方法是否有@LogExecution注解? " + targetMethod.isAnnotationPresent(LogExecution.class));
if (targetMethod.isAnnotationPresent(LogExecution.class)) {
LogExecution logAnnotation = targetMethod.getAnnotation(LogExecution.class);
System.out.println("logAnnotation.enable() : " + logAnnotation.enable());
if (!logAnnotation.enable()) {
System.out.println("DEBUG: 日志已禁用,跳过");
return method.invoke(target, args);
}
System.out.println("\n" + "=".repeat(50));
System.out.println("【日志开始】 " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now()));
System.out.println("方法: " + method.getName());
System.out.println("描述: " + logAnnotation.description());
System.out.println("级别: " + getLogLevel(logAnnotation.level()));
long startTime = System.currentTimeMillis();
try {
Object result = method.invoke(target, args);
long executionTime = System.currentTimeMillis() - startTime;
System.out.println("执行时间: " + executionTime + "ms");
System.out.println("【日志结束】" + "=".repeat(50));
return result;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
return method.invoke(target, args);
}
private String getLogLevel(int level) {
return switch (level) {
case 1 -> "INFO";
case 2 -> "WARNING";
case 3 -> "ERROR";
default -> "UNKNOWN";
};
}
}
package com.nn3n;
import java.lang.reflect.Proxy;
// ================== 5. 主程序:演示自动日志记录 ==================
public class AnnotationDemo {
public static void main(String[] args) {
System.out.println("===== 开始演示自动日志记录 =====");
// 创建原始服务对象
UserService userService = new UserServiceImpl();
// 创建代理对象(关键!自动触发日志)
UserService proxyUserService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
new LogInvocationHandler(userService)
);
// 正常调用方法(日志自动触发!)
proxyUserService.getUserDetails(1001L);
proxyUserService.createUser("Jane Smith");
proxyUserService.deleteUser(1002L); // 此方法日志已禁用
System.out.println("\n===== 演示结束 =====");
}
}
🧪 运行结果示例
===== 开始演示自动日志记录 =====
DEBUG: 调用方法 [getUserDetails]
DEBUG: 方法是否有@LogExecution注解? true
logAnnotation.enable() : true
==================================================
【日志开始】 2025-11-27 15:35:23
方法: getUserDetails
描述: 获取用户详情
级别: WARNING
正在获取用户详情... 参数: 1001
执行时间: 155ms
【日志结束】
==================================================
DEBUG: 调用方法 [createUser]
DEBUG: 方法是否有@LogExecution注解? true
logAnnotation.enable() : true
==================================================
【日志开始】 2025-11-27 15:35:23
方法: createUser
描述: 创建新用户
级别: INFO
正在创建新用户... 参数: Jane Smith
执行时间: 95ms
【日志结束】
==================================================
DEBUG: 调用方法 [deleteUser]
DEBUG: 方法是否有@LogExecution注解? true
logAnnotation.enable() : false
DEBUG: 日志已禁用,跳过
正在删除用户... 参数: 1002
===== 演示结束 =====
💡 关键点说明:
- @LogExecution注解的enable=false让deleteUser()方法不记录日志
- level=2显示为WARNING,level=1显示为INFO
- 执行时间精确到毫秒
🔍 为什么这个示例这么实用?
实际应用场景(真实项目中常见):
- 性能监控:标记关键方法,记录执行时间
- 日志分级:根据level设置不同日志级别
- 功能开关:通过enable属性动态开启/关闭日志
- 无侵入式:业务代码无需写日志逻辑,注解自动处理
与传统日志对比
// 传统方式(侵入式,代码臃肿)
public User getUserDetails(Long userId) {
long start = System.currentTimeMillis();
System.out.println("[INFO] 获取用户详情开始");
// 业务逻辑
User user = new User(userId, "John Doe");
System.out.println("[INFO] 获取用户详情结束,耗时: " + (System.currentTimeMillis() - start) + "ms");
return user;
}
// 使用注解(无侵入,代码简洁)
@LogExecution(description = "获取用户详情", level = 2)
public User getUserDetails(Long userId) {
// 业务逻辑(无日志代码!)
return new User(userId, "John Doe");
}
🛠️ 如何在Spring Boot中实现?
在Spring Boot项目中,我们通常用AOP实现类似功能,比反射更高效:
// Spring AOP切面实现
@Aspect
@Component
public class LogAspect {
@Before("@annotation(logExecution)")
public void logBefore(JoinPoint joinPoint, LogExecution logExecution) {
System.out.println("【AOP日志】" + joinPoint.getSignature().getName() + " - " + logExecution.description() + " (级别: " + logExecution.level() + ")");
}
@AfterReturning(pointcut = "@annotation(logExecution)", returning = "result")
public void logAfter(JoinPoint joinPoint, LogExecution logExecution, Object result) {
System.out.println("【AOP日志】执行完成,结果: " + result);
}
}
✨ Spring优势:AOP在方法执行前后自动触发,无需手动调用,性能更好
- 感谢你赐予我前进的力量

