如果有遗漏,评论区告诉我进行补充
面试官: 定义一个自定义注解@MyAnnotation
,通过反射解析某类中被该注解标记的方法。
我回答:
在Java高级面试中,自定义注解与反射机制的结合是考察开发者对元编程(Metaprogramming)和框架设计理解的核心内容。以下是对自定义注解 @MyAnnotation
的完整实现与解析过程的综合讲解,结合关键细节和扩展场景,帮助你系统掌握这一技术点。
一、自定义注解定义
1. 定义 @MyAnnotation
import java.lang.annotation.*;
@Target(ElementType.METHOD) // 限定注解只能用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解信息保留到运行时
public @interface MyAnnotation {
String value() default "default"; // 默认属性,可通过注解直接赋值
int priority() default 0; // 新增属性:优先级,用于扩展场景
}
关键点
@Target
:明确注解的作用范围(如方法、类、字段等)。@Retention
:必须为RUNTIME
,否则反射无法读取。- 默认值:
value()
属性未赋值时默认为"default"
,调用时可省略参数(如@MyAnnotation
等价于@MyAnnotation(value="default")
)。
二、应用注解到目标类
2. 示例类 AnnotatedClass
public class AnnotatedClass {
@MyAnnotation(value = "Hello", priority = 1)
public void sayHello() {
System.out.println("Hello called");
}
@MyAnnotation(value = "World") // 使用默认priority
public void sayWorld() {
System.out.println("World called");
}
public void noAnnotation() {
System.out.println("No annotation here");
}
}
三、通过反射解析注解
3. 核心解析逻辑
import java.lang.reflect.Method;
public class AnnotationProcessor {
public static void main(String[] args) {
try {
// 1. 获取目标类的Class对象
Class<?> clazz = AnnotatedClass.class; // 或 Class.forName("AnnotatedClass")
// 2. 遍历所有声明的方法
for (Method method : clazz.getDeclaredMethods()) {
// 3. 检查方法是否被@MyAnnotation标记
if (method.isAnnotationPresent(MyAnnotation.class)) {
// 4. 获取注解实例并解析属性
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("Method: " + method.getName());
System.out.println(" Value: " + annotation.value());
System.out.println(" Priority: " + annotation.priority());
// 5. 调用被注解的方法(无参方法)
Object instance = clazz.getDeclaredConstructor().newInstance();
method.invoke(instance); // 调用无参方法
System.out.println();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果
Method: sayHello
Value: Hello
Priority: 1
Hello called
Method: sayWorld
Value: World
Priority: 0
World called
四、关键技术细节
1. 反射调用方法的注意事项
- 无参方法:直接通过
method.invoke(instance)
调用。 - 有参方法:需根据方法签名传递参数:
// 示例:调用有参方法 @MyAnnotation public void sayWithParam(String msg) { System.out.println("Param: " + msg); } // 调用方式 Method method = clazz.getMethod("sayWithParam", String.class); method.invoke(instance, "Test Param"); // 传递参数
2. 处理私有方法
- 若需调用私有方法,需额外设置可访问性:
Method privateMethod = clazz.getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); // 绕过访问控制 privateMethod.invoke(instance);
3. 继承方法与接口方法
getDeclaredMethods()
仅返回当前类声明的方法,不包括父类或接口方法。- 若需检查父类方法,需递归遍历继承链:
while (clazz != null) { for (Method method : clazz.getDeclaredMethods()) { // 处理方法 } clazz = clazz.getSuperclass(); // 向上遍历父类 }
五、扩展场景:结合注解实现AOP功能
1. 示例:统计方法执行时间
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
String description() default "";
}
public class ExecutionTimeLogger {
public static void log(Object target) throws Exception {
Class<?> clazz = target.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(LogExecutionTime.class)) {
LogExecutionTime annotation = method.getAnnotation(LogExecutionTime.class);
System.out.println("Monitoring: " + method.getName() +
" (" + annotation.description() + ")");
// 使用代理或反射调用,并计时
Object instance = clazz.getDeclaredConstructor().newInstance();
long start = System.currentTimeMillis();
method.invoke(instance);
long duration = System.currentTimeMillis() - start;
System.out.println(" Execution Time: " + duration + "ms");
}
}
}
}
2. 使用示例
public class Service {
@LogExecutionTime(description = "Business Logic")
public void performTask() throws InterruptedException {
Thread.sleep(500); // 模拟耗时操作
}
}
// 调用
ExecutionTimeLogger.log(new Service());
输出
Monitoring: performTask (Business Logic)
Execution Time: 500ms
六、面试高频问题
-
@Retention
和@Target
的作用?@Retention
:控制注解的生命周期(SOURCE/CLASS/RUNTIME)。@Target
:限定注解的作用范围(如方法、类、字段等)。
-
如何获取注解的属性值?
- 通过
method.getAnnotation(MyAnnotation.class)
获取注解实例,再调用属性方法(如annotation.value()
)。
- 通过
-
反射调用方法的性能影响?
- 反射调用比直接调用慢(需检查权限、类型转换等),但可通过
MethodHandles
或缓存优化。
- 反射调用比直接调用慢(需检查权限、类型转换等),但可通过
-
注解与Spring AOP的关系?
- Spring AOP通过解析注解(如
@Transactional
)生成代理对象,在运行时拦截方法调用。
- Spring AOP通过解析注解(如
七、总结
- 自定义注解:通过
@interface
定义,结合@Target
和@Retention
控制行为。 - 反射解析:使用
Class.getDeclaredMethods()
和Method.getAnnotation()
动态获取注解信息。 - 扩展场景:结合注解可实现日志、权限校验、性能监控等框架级功能。
掌握这一技术点不仅能应对面试,还能为设计可扩展的Java框架(如自定义ORM、AOP工具)打下基础。建议结合Spring源码(如 @Controller
、@Autowired
的实现)进一步加深理解。