高级java每日一道面试题-2025年5月08日-基础篇[反射篇-进阶-编码]-定义一个自定义注解`@MyAnnotation`,通过反射解析某类中被该注解标记的方法。

如果有遗漏,评论区告诉我进行补充

面试官: 定义一个自定义注解@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

六、面试高频问题

  1. @Retention@Target 的作用?

    • @Retention:控制注解的生命周期(SOURCE/CLASS/RUNTIME)。
    • @Target:限定注解的作用范围(如方法、类、字段等)。
  2. 如何获取注解的属性值?

    • 通过 method.getAnnotation(MyAnnotation.class) 获取注解实例,再调用属性方法(如 annotation.value())。
  3. 反射调用方法的性能影响?

    • 反射调用比直接调用慢(需检查权限、类型转换等),但可通过 MethodHandles 或缓存优化。
  4. 注解与Spring AOP的关系?

    • Spring AOP通过解析注解(如 @Transactional)生成代理对象,在运行时拦截方法调用。

七、总结

  • 自定义注解:通过 @interface 定义,结合 @Target@Retention 控制行为。
  • 反射解析:使用 Class.getDeclaredMethods()Method.getAnnotation() 动态获取注解信息。
  • 扩展场景:结合注解可实现日志、权限校验、性能监控等框架级功能。

掌握这一技术点不仅能应对面试,还能为设计可扩展的Java框架(如自定义ORM、AOP工具)打下基础。建议结合Spring源码(如 @Controller@Autowired 的实现)进一步加深理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java我跟你拼了

您的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值