Skip to content

反射与注解

反射

什么是反射?

答案: 反射(Reflection)是Java在运行时动态获取类信息、调用方法、访问字段的机制。

核心类

  • Class:类的字节码对象
  • Constructor:构造方法
  • Method:方法
  • Field:字段

优点

  • 灵活性高,可以动态创建对象
  • 框架开发的基础(Spring、MyBatis等)

缺点

  • 性能较低
  • 破坏封装性
  • 代码可读性差

如何获取Class对象?

答案:

java
// 方式1:类名.class
Class<?> clazz1 = String.class;

// 方式2:对象.getClass()
String str = "hello";
Class<?> clazz2 = str.getClass();

// 方式3:Class.forName()
Class<?> clazz3 = Class.forName("java.lang.String");

// 判断是否同一个Class对象
System.out.println(clazz1 == clazz2);  // true

反射的常用操作?

答案:

1. 创建对象

java
Class<?> clazz = User.class;

// 无参构造
User user1 = (User) clazz.newInstance();  // 已过时

// 推荐方式
User user2 = (User) clazz.getDeclaredConstructor().newInstance();

// 有参构造
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
User user3 = (User) constructor.newInstance("Tom", 20);

2. 访问字段

java
Class<?> clazz = User.class;
User user = new User();

// 获取public字段
Field field1 = clazz.getField("name");

// 获取所有字段(包括private)
Field field2 = clazz.getDeclaredField("age");
field2.setAccessible(true);  // 设置可访问

// 获取字段值
Object value = field2.get(user);

// 设置字段值
field2.set(user, 20);

3. 调用方法

java
Class<?> clazz = User.class;
User user = new User();

// 获取public方法
Method method1 = clazz.getMethod("getName");

// 获取所有方法(包括private)
Method method2 = clazz.getDeclaredMethod("setAge", int.class);
method2.setAccessible(true);

// 调用方法
Object result = method1.invoke(user);
method2.invoke(user, 20);

4. 获取类信息

java
Class<?> clazz = User.class;

// 类名
String name = clazz.getName();  // 全限定名
String simpleName = clazz.getSimpleName();  // 简单名

// 父类
Class<?> superClass = clazz.getSuperclass();

// 接口
Class<?>[] interfaces = clazz.getInterfaces();

// 注解
Annotation[] annotations = clazz.getAnnotations();

// 修饰符
int modifiers = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);

反射的应用场景?

答案:

1. 框架开发

java
// Spring IoC容器
@Component
public class UserService {
}

// Spring通过反射创建对象
Class<?> clazz = Class.forName("com.example.UserService");
Object bean = clazz.newInstance();

2. 动态代理

java
// JDK动态代理
Object proxy = Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    (proxy, method, args) -> {
        System.out.println("before");
        Object result = method.invoke(target, args);
        System.out.println("after");
        return result;
    }
);

3. 工具类

java
// BeanUtils.copyProperties()
public static void copyProperties(Object source, Object target) {
    Class<?> sourceClass = source.getClass();
    Class<?> targetClass = target.getClass();

    Field[] fields = sourceClass.getDeclaredFields();
    for (Field field : fields) {
        field.setAccessible(true);
        try {
            Field targetField = targetClass.getDeclaredField(field.getName());
            targetField.setAccessible(true);
            targetField.set(target, field.get(source));
        } catch (Exception e) {
            // ignore
        }
    }
}

注解

什么是注解?

答案: 注解(Annotation)是一种元数据,用于给代码添加说明信息。

内置注解

  • @Override:重写方法
  • @Deprecated:已过时
  • @SuppressWarnings:抑制警告
  • @FunctionalInterface:函数式接口

元注解(用于定义注解):

  • @Target:注解的使用位置
  • @Retention:注解的生命周期
  • @Documented:是否生成文档
  • @Inherited:是否可继承

如何自定义注解?

答案:

java
@Target(ElementType.METHOD)  // 用于方法
@Retention(RetentionPolicy.RUNTIME)  // 运行时有效
@Documented
public @interface MyAnnotation {
    String value() default "";  // 属性
    int timeout() default 3000;
}

@Target的值

java
ElementType.TYPE           // 类、接口
ElementType.FIELD          // 字段
ElementType.METHOD         // 方法
ElementType.PARAMETER      // 参数
ElementType.CONSTRUCTOR    // 构造方法
ElementType.LOCAL_VARIABLE // 局部变量
ElementType.ANNOTATION_TYPE // 注解
ElementType.PACKAGE        // 包

@Retention的值

java
RetentionPolicy.SOURCE   // 源码阶段,编译后丢弃
RetentionPolicy.CLASS    // 编译阶段,运行时丢弃(默认)
RetentionPolicy.RUNTIME  // 运行时,可通过反射获取

如何解析注解?

答案:

java
// 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

// 使用注解
public class UserService {
    @Log("查询用户")
    public void getUser() {
        System.out.println("查询用户");
    }
}

// 解析注解
Class<?> clazz = UserService.class;
Method method = clazz.getMethod("getUser");

// 判断是否有注解
if (method.isAnnotationPresent(Log.class)) {
    // 获取注解
    Log log = method.getAnnotation(Log.class);
    System.out.println(log.value());  // 查询用户
}

注解的应用场景?

答案:

1. 配置信息

java
@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getById(id);
    }
}

2. 数据校验

java
public class User {
    @NotNull(message = "姓名不能为空")
    private String name;

    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;
}

3. AOP切面

java
@Aspect
@Component
public class LogAspect {

    @Around("@annotation(log)")
    public Object around(ProceedingJoinPoint pjp, Log log) throws Throwable {
        System.out.println("日志:" + log.value());
        return pjp.proceed();
    }
}

4. 单元测试

java
@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void testGetUser() {
        User user = userService.getById(1L);
        assertNotNull(user);
    }
}

动态代理

什么是动态代理?

答案: 动态代理是在运行时动态生成代理类,实现对目标对象的增强。

JDK动态代理(基于接口):

java
// 接口
public interface UserService {
    void save();
}

// 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("保存用户");
    }
}

// 代理
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object result = method.invoke(target, args);
            System.out.println("after");
            return result;
        }
    }
);

proxy.save();
// 输出:
// before
// 保存用户
// after

CGLIB动态代理(基于继承):

java
// 目标类(无需接口)
public class UserService {
    public void save() {
        System.out.println("保存用户");
    }
}

// 代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after");
        return result;
    }
});

UserService proxy = (UserService) enhancer.create();
proxy.save();

练习题

  1. 反射为什么性能低?如何优化?
  2. 如何通过反射调用私有方法?
  3. 注解的本质是什么?
  4. JDK动态代理和CGLIB代理的区别?

Released under the MIT License.