反射与注解
反射
什么是反射?
答案: 反射(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
// 保存用户
// afterCGLIB动态代理(基于继承):
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();练习题
- 反射为什么性能低?如何优化?
- 如何通过反射调用私有方法?
- 注解的本质是什么?
- JDK动态代理和CGLIB代理的区别?