跳到主要内容Java 反射机制详解:原理、场景与实战 | 极客日志Javajava
Java 反射机制详解:原理、场景与实战
Java 反射机制允许程序在运行时动态获取类的信息并操作属性、方法及构造器。它通过 Class、Field、Method 等核心类实现,广泛应用于依赖注入、动态代理、序列化及注解处理等场景。虽然提供了极大的灵活性,但也存在性能开销和安全风险,使用时需谨慎权衡。
晚风叙旧1 浏览 1. 什么是反射
反射(Reflection)是 Java 语言的一个核心特性,它允许程序在运行时动态地获取类的信息,并操作类或对象的属性、方法和构造器。
1.1 直观理解
想象你有一个密封的盒子(类),正常情况下你只能通过盒子上的按钮(公共方法)来操作它。而反射就像是一把 X 光扫描仪,可以让你不用打开盒子就能看到里面的所有结构(私有字段、方法等),甚至可以绕过正常的操作方式直接控制内部部件。
什么时候需要用到反射呢?举个生动的例子:游戏外挂(仅用于理解访问控制)。
class GameCharacter {
private int health = 100;
private String name;
public void attack() { }
private void cheat() { health = 9999; }
}
没有反射时:
- 你只能调用
attack() 方法
- 无法修改血量
health
- 无法调用
cheat() 方法
使用反射后:
Class<?> clazz = GameCharacter.class;
Field healthField = clazz.getDeclaredField("health");
healthField.setAccessible(true);
healthField.set(character, 9999);
Method cheatMethod = clazz.getDeclaredMethod("cheat");
cheatMethod.setAccessible(true);
cheatMethod.invoke(character);
这就好比在游戏运行时突然获得了修改角色属性的能力!
1.2 作用
反射打破了编译时的限制,让代码具备了动态性。它是框架开发(如 Spring)、ORM 映射、序列化库以及测试工具的基础设施。
2. 核心类
Java 反射主要涉及以下几个核心类,它们构成了反射 API 的骨架:
- Class:表示类的元数据,是反射的入口点。
- Field:表示类的字段(成员变量)。
- Method:表示类的方法。
- Constructor:表示类的构造器。
3. 应用场景
3.1 基本反射操作
这是最基础的用法,获取 Class 对象并调用方法。
import java.lang.reflect.*;
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
Class<?> clazz1 = Class.forName("java.lang.String");
Class<?> clazz2 = String.class;
Class<?> clazz3 = "hello".getClass();
System.out.println("类名:" + clazz1.getName());
System.out.println("简单类名:" + clazz1.getSimpleName());
System.out.println("\n公共方法:");
Method[] methods = clazz1.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println("\n所有字段:");
Field[] fields = clazz1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
String str = (String) clazz1.getConstructor(String.class).newInstance("Hello Reflection");
Method lengthMethod = clazz1.getMethod("length");
int length = (int) lengthMethod.invoke(str);
System.out.println("\n字符串长度:" + length);
}
}
3.2 动态加载类
public class PluginManager {
public void loadPlugin(String className) throws Exception {
Class<?> pluginClass = Class.forName(className);
Object plugin = pluginClass.newInstance();
if (plugin instanceof Runnable) {
((Runnable) plugin).run();
}
}
public static void main(String[] args) throws Exception {
PluginManager manager = new PluginManager();
manager.loadPlugin("com.example.MyPlugin");
}
}
3.3 调用私有方法(测试时常用)
在单元测试中,有时需要验证私有方法的逻辑,或者为了模拟某些边界条件。
public class SecretClass {
private String secretMethod(String input) {
return "Secret: " + input;
}
}
public class SecretTest {
public static void main(String[] args) throws Exception {
SecretClass instance = new SecretClass();
Class<?> clazz = instance.getClass();
Method secretMethod = clazz.getDeclaredMethod("secretMethod", String.class);
secretMethod.setAccessible(true);
String result = (String) secretMethod.invoke(instance, "Hello");
System.out.println(result);
}
}
3.4 JSON/XML 序列化与反序列化
虽然生产环境通常使用 Jackson 或 Gson,但理解底层原理有助于排查问题。
3.4.1 JSON 序列化示例
public class JsonSerializer {
public static String toJson(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
StringBuilder json = new StringBuilder("{");
for (Field field : fields) {
field.setAccessible(true);
json.append("\"").append(field.getName()).append("\":")
.append("\"").append(field.get(obj)).append("\",");
}
if (json.length() > 1) {
json.deleteCharAt(json.length() - 1);
}
json.append("}");
return json.toString();
}
}
class Person {
private String name = "Alice";
private int age = 25;
}
public class Main {
public static void main(String[] args) throws Exception {
Person person = new Person();
System.out.println(JsonSerializer.toJson(person));
}
}
3.4.2 XML 序列化示例
import java.lang.reflect.*;
import java.util.*;
public class XmlSerializer {
public static String toXml(Object obj) throws Exception {
if (obj == null) return "<null/>";
Class<?> clazz = obj.getClass();
StringBuilder xml = new StringBuilder();
if (obj instanceof Number || obj instanceof Boolean || obj instanceof String) {
return "<value>" + obj.toString() + "</value>";
}
if (clazz.isArray()) {
xml.append("<array type=\"").append(clazz.getComponentType().getSimpleName()).append("\">");
int length = Array.getLength(obj);
for (int i = 0; i < length; i++) {
xml.append(toXml(Array.get(obj, i)));
}
xml.append("</array>");
return xml.toString();
}
if (obj instanceof Collection) {
Collection<?> collection = (Collection<?>) obj;
xml.append("<collection>");
for (Object item : collection) {
xml.append(toXml(item));
}
xml.append("</collection>");
return xml.toString();
}
xml.append("<").append(clazz.getSimpleName()).append(">");
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isTransient(field.getModifiers())) continue;
field.setAccessible(true);
Object value = field.get(obj);
xml.append("<").append(field.getName()).append(">")
.append(toXml(value))
.append("</").append(field.getName()).append(">");
}
xml.append("</").append(clazz.getSimpleName()).append(">");
return xml.toString();
}
public static void main(String[] args) throws Exception {
class Product {
private String id = "P1001";
private String name = "Laptop";
private double price = 999.99;
private List<String> tags = Arrays.asList("electronics", "computer");
}
Product product = new Product();
System.out.println(XmlSerializer.toXml(product));
}
}
3.5 反射实现依赖注入
依赖注入是 Spring 等框架的核心设计模式,通过反射机制动态地将依赖对象注入到目标对象中。
3.5.1 简单依赖注入实现
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class SimpleContainer {
private Map<String, Object> beans = new HashMap<>();
public void registerBean(String name, Object bean) {
beans.put(name, bean);
}
public void injectDependencies() throws Exception {
for (Object bean : beans.values()) {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(Autowired.class) != null) {
Class<?> fieldType = field.getType();
Object dependency = findDependency(fieldType);
if (dependency != null) {
field.setAccessible(true);
field.set(bean, dependency);
}
}
}
}
}
private Object findDependency(Class<?> type) {
for (Object bean : beans.values()) {
if (type.isAssignableFrom(bean.getClass())) {
return bean;
}
}
return null;
}
}
@interface Autowired {}
class ServiceA {}
class ServiceB {
@Autowired
private ServiceA serviceA;
public void showDependency() {
System.out.println("ServiceB 中的 ServiceA 依赖:" + serviceA);
}
}
public class DITest {
public static void main(String[] args) throws Exception {
SimpleContainer container = new SimpleContainer();
container.registerBean("serviceA", new ServiceA());
container.registerBean("serviceB", new ServiceB());
container.injectDependencies();
ServiceB serviceB = (ServiceB) container.beans.get("serviceB");
serviceB.showDependency();
}
}
3.5.2 依赖注入原理
- 扫描类路径,通过反射获取类的元信息。
- 解析
@Component、@Service 等注解。
- 解析构造器或字段上的
@Autowired 注解。
- 通过反射创建 Bean 实例。
- 通过反射将依赖注入到目标字段或构造器中。
3.6 反射实现动态代理
动态代理是 AOP(面向切面编程)的基础,它允许在运行时动态创建代理对象。
3.6.1 JDK 动态代理示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface UserService {
void addUser(String name);
void deleteUser(String name);
}
class UserServiceImpl implements UserService {
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
public void deleteUser(String name) {
System.out.println("删除用户:" + name);
}
}
class LoggingHandler implements InvocationHandler {
private Object target;
public LoggingHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备执行:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("执行完成:" + method.getName());
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
UserService realService = new UserServiceImpl();
UserService proxyService = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new LoggingHandler(realService)
);
proxyService.addUser("张三");
proxyService.deleteUser("李四");
}
}
3.6.2 CGLIB 动态代理示例
当目标类没有实现接口时,可以使用 CGLIB 库。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class ProductService {
public void saveProduct(String name) {
System.out.println("保存产品:" + name);
}
}
class ProductMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("CGLIB 代理前置处理");
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB 代理后置处理");
return result;
}
}
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
enhancer.setCallback(new ProductMethodInterceptor());
ProductService proxy = (ProductService) enhancer.create();
proxy.saveProduct("笔记本电脑");
}
}
3.7 反射实现注解处理
Java 反射 API 提供了关键方法来处理注解:
getAnnotation(Class<T>) - 获取指定类型的注解
getAnnotations() - 获取所有注解
isAnnotationPresent(Class<?>) - 检查是否存在指定注解
3.7.1 定义自定义注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Table {
String name();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
String name();
String type();
}
3.7.2 使用反射处理注解
import java.lang.reflect.Field;
@Table(name = "user_table")
class User {
@Column(name = "user_id", type = "bigint")
private Long id;
@Column(name = "user_name", type = "varchar(50)")
private String name;
@Column(name = "user_age", type = "int")
private Integer age;
private transient String temp;
}
public class AnnotationProcessor {
public static void main(String[] args) {
Class<User> userClass = User.class;
Table tableAnnotation = userClass.getAnnotation(Table.class);
if (tableAnnotation != null) {
System.out.println("表名:" + tableAnnotation.name());
StringBuilder sql = new StringBuilder();
sql.append("CREATE TABLE ").append(tableAnnotation.name()).append("(\n");
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
Column columnAnnotation = field.getAnnotation(Column.class);
if (columnAnnotation != null) {
sql.append(" ").append(columnAnnotation.name())
.append(" ").append(columnAnnotation.type()).append(",\n");
}
}
sql.delete(sql.length() - 2, sql.length());
sql.append(");\n");
System.out.println("生成的 SQL:\n" + sql);
}
}
}
3.7.3 进阶注解处理
import java.lang.annotation.*;
import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Permission {
String[] roles() default {};
}
class AdminService {
@Permission(roles = {"admin", "superadmin"})
public void deleteUser(String username) {
System.out.println("删除用户:" + username);
}
@Permission(roles = {"user", "admin"})
public void viewProfile(String username) {
System.out.println("查看用户资料:" + username);
}
public void publicMethod() {
System.out.println("公开方法,无需权限");
}
}
public class PermissionProcessor {
public static void main(String[] args) throws Exception {
String currentRole = "user";
AdminService service = new AdminService();
Method[] methods = service.getClass().getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Permission.class)) {
Permission permission = method.getAnnotation(Permission.class);
boolean hasPermission = false;
for (String role : permission.roles()) {
if (role.equals(currentRole)) {
hasPermission = true;
break;
}
}
if (hasPermission) {
System.out.println("允许执行:" + method.getName());
method.invoke(service, "testUser");
} else {
System.out.println("拒绝访问:" + method.getName() + ",需要角色:" + String.join(",", permission.roles()));
}
} else {
System.out.println("执行无权限限制方法:" + method.getName());
method.invoke(service);
}
}
}
}
import java.lang.annotation.*;
import java.lang.reflect.Parameter;
import java.lang.reflect.Method;
class UserService {
public void createUser(
@Validate(min = 3, max = 20) String username,
@Validate(min = 6, pattern = ".*[0-9].*") String password
) {
System.out.println("创建用户:" + username);
}
}
@interface Validate {
int min() default 0;
int max() default Integer.MAX_VALUE;
String pattern() default "";
}
public class ValidationProcessor {
public static void invokeWithValidation(Object target, Method method, Object... args) throws Exception {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Validate validate = parameters[i].getAnnotation(Validate.class);
if (validate != null) {
Object arg = args[i];
if (arg instanceof String) {
String value = (String) arg;
if (value.length() < validate.min()) {
throw new IllegalArgumentException(parameters[i].getName() + " 长度不能小于 " + validate.min());
}
if (value.length() > validate.max()) {
throw new IllegalArgumentException(parameters[i].getName() + " 长度不能大于 " + validate.max());
}
if (!validate.pattern().isEmpty() && !value.matches(validate.pattern())) {
throw new IllegalArgumentException(parameters[i].getName() + " 不符合格式要求");
}
}
}
}
method.invoke(target, args);
}
public static void main(String[] args) throws Exception {
UserService service = new UserService();
Method method = service.getClass().getMethod("createUser", String.class, String.class);
try {
invokeWithValidation(service, method, "ab", "123456");
} catch (IllegalArgumentException e) {
System.out.println("验证失败:" + e.getMessage());
}
try {
invokeWithValidation(service, method, "validUser", "simple");
} catch (IllegalArgumentException e) {
System.out.println("验证失败:" + e.getMessage());
}
invokeWithValidation(service, method, "validUser", "pass123");
}
}
import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.*;
@Controller
@interface Controller {
String value() default "";
}
@RequestMapping
@interface RequestMapping {
String path();
String method() default "GET";
}
@Controller("/user")
class UserController {
@RequestMapping(path = "/list", method = "GET")
public List<String> listUsers() {
return Arrays.asList("Alice", "Bob", "Charlie");
}
@RequestMapping(path = "/add", method = "POST")
public String addUser(String username) {
return "添加用户成功:" + username;
}
}
public class MvcSimulator {
private Map<String, Method> routeMap = new HashMap<>();
public void init() {
Class<?>[] controllers = {UserController.class};
for (Class<?> controller : controllers) {
Controller controllerAnnotation = controller.getAnnotation(Controller.class);
String basePath = controllerAnnotation.value();
for (Method method : controller.getDeclaredMethods()) {
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
String fullPath = basePath + mapping.path();
routeMap.put(mapping.method() + ":" + fullPath, method);
}
}
}
}
public Object handleRequest(String httpMethod, String path, Object... args) throws Exception {
String key = httpMethod + ":" + path;
Method method = routeMap.get(key);
if (method != null) {
return method.invoke(method.getDeclaringClass().newInstance(), args);
}
throw new RuntimeException("404 Not Found");
}
public static void main(String[] args) throws Exception {
MvcSimulator simulator = new MvcSimulator();
simulator.init();
Object result1 = simulator.handleRequest("GET", "/user/list");
System.out.println("GET /user/list => " + result1);
Object result2 = simulator.handleRequest("POST", "/user/add", "David");
System.out.println("POST /user/add => " + result2);
try {
simulator.handleRequest("GET", "/not/exist");
} catch (Exception e) {
System.out.println("GET /not/exist => " + e.getMessage());
}
}
}
3.8 代码提示与 IDE 自动补全
IDE 功能如代码提示、自动补全都利用反射获取类信息。实现这些功能主要依赖以下反射操作:
- 获取类的所有公共方法 (
getMethods())
- 获取类的所有声明方法 (
getDeclaredMethods())
- 获取方法的参数信息 (
getParameterTypes())
- 获取类的字段信息 (
getFields(), getDeclaredFields())
- 获取类的构造函数 (
getConstructors())
3.8.1 类成员自动补全
import java.lang.reflect.*;
import java.util.*;
public class CodeCompletion {
private static final Set<String> JAVA_KEYWORDS = new HashSet<>(Arrays.asList(
"public", "private", "protected", "static", "void", "class", "interface"
));
public static List<String> getClassSuggestions(Class<?> clazz, String prefix) {
List<String> suggestions = new ArrayList<>();
for (Method method : clazz.getMethods()) {
if (method.getName().startsWith(prefix)) {
suggestions.add(method.getName() + "()");
}
}
for (Field field : clazz.getFields()) {
if (field.getName().startsWith(prefix)) {
suggestions.add(field.getName());
}
}
return suggestions;
}
public static void main(String[] args) {
Class<?> stringClass = String.class;
System.out.println("String 类以'comp'开头的方法/字段:");
System.out.println(getClassSuggestions(stringClass, "comp"));
System.out.println("\nString 类以'to'开头的方法/字段:");
System.out.println(getClassSuggestions(stringClass, "to"));
}
}
3.8.2 方法参数提示
import java.lang.reflect.*;
import java.util.*;
public class MethodParameterHint {
public static Map<String, List<String>> getMethodParameterHints(Class<?> clazz) {
Map<String, List<String>> hints = new HashMap<>();
for (Method method : clazz.getMethods()) {
List<String> params = new ArrayList<>();
for (Parameter param : method.getParameters()) {
params.add(param.getType().getSimpleName() + " " + param.getName());
}
String methodKey = method.getName() + "(" + String.join(", ", params) + ")";
hints.put(methodKey, params);
}
return hints;
}
public static void printMethodHints(Class<?> clazz, String methodPrefix) {
System.out.println("方法名以'" + methodPrefix + "'开头的方法签名:");
for (Method method : clazz.getMethods()) {
if (method.getName().startsWith(methodPrefix)) {
System.out.print(method.getName() + "(");
Parameter[] params = method.getParameters();
for (int i = 0; i < params.length; i++) {
if (i > 0) System.out.print(", ");
System.out.print(params[i].getType().getSimpleName() + " " + params[i].getName());
}
System.out.println(")");
}
}
}
public static void main(String[] args) {
Class<?> listClass = List.class;
System.out.println("List 接口所有方法参数提示:");
Map<String, List<String>> hints = getMethodParameterHints(listClass);
hints.forEach((k, v) -> System.out.println(k));
System.out.println("\n---\n");
printMethodHints(listClass, "add");
}
}
4. 反射的优缺点
- 极大的灵活性,可以在运行时动态操作。
- 可以访问类的私有成员(突破封装)。
- 适合开发通用框架和工具。
- 性能开销较大(比直接调用慢)。
- 破坏封装性,可能带来安全问题。
- 代码可读性降低,调试困难。
5. 总结
记住:反射是 Java 的'元'能力,强大但应慎用。就像超能力不能随便在公共场合使用一样,在生产环境中,除非确实需要动态性,否则优先选择静态编译期的安全调用方式。合理使用反射能构建强大的框架,滥用则可能导致难以维护的代码隐患。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online