乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Java概述与环境搭建

    • Java概述与环境搭建
    • Java语言概述
    • 解释型语言与编译型语言对比
    • JDK安装与配置
    • JDK、JRE、JVM 详解
    • HelloWorld程序详解
    • IDE 介绍
  • 第2章 标识符与基本数据类型

    • 章节导读
    • 变量概述
    • 常量概述
    • 基本类型与包装类
    • 字节型 byte
    • 短整型 short
    • 整型 int
    • 长整型 long
    • 单精度浮点型 float
    • 双精度浮点型 double
    • 字符型 char
    • 布尔型 boolean
    • 类型转换
  • 第3章 运算符与表达式

    • 章节导读
    • 算术运算符
    • 赋值运算符
    • 关系运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 运算符优先级
    • 表达式
  • 第4章 流程控制

    • 章节导读
    • 常见的程序运行流程
    • if-else 选择结构
    • switch 多分支选择
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
  • 第5章 数组

    • 章节导读
    • 一维数组
    • 多维数组
    • Arrays 工具类
  • 第6章 类与对象

    • 章节导读
    • 类与对象
    • 方法定义与调用
    • 构造方法
    • 封装
    • 访问修饰符
    • package 与 import
    • static 关键字
    • this 关键字
    • 参数传递 详解
    • 枚举
    • 成员内部类
    • 局部内部类
    • 静态内部类
    • 匿名内部类
  • 第7章 接口与继承

    • 章节导读
    • 继承
    • super 关键字
    • final 关键字
    • 多态
    • 向上转型与向下转型
    • 抽象类
    • 接口
    • 抽象类与接口对比
  • 第8章 注解

    • 章节导读
    • 注解基础
    • 元注解详解
    • 自定义注解
  • 第9章 常用类

    • 章节导读:Java 常用类
    • Object 类:万类之祖
    • 包装类:基本类型的对象化
    • String:不可变的字符串
    • StringBuffer:线程安全的可变字符串
    • StringBuilder:可变的字符串构建器
    • Math:数学运算工具类
    • Random:伪随机数生成器
    • 大数值运算 详解
    • 日期时间API 详解
  • 第10章 异常机制

    • 章节导读
    • 异常体系与分类
    • try-catch-finally
    • try-with-resources
    • throws 与 throw
    • 自定义异常
  • 第11章 泛型

    • 章节导读
    • 泛型基础
    • 通配符与PECS原则
    • 类型擦除
  • 第12章 集合框架

    • 章节导读
    • 集合框架概述
    • ArrayList
    • LinkedList
    • HashMap 详解
    • LinkedHashMap 详解
    • TreeMap 详解
    • HashSet
    • TreeSet 详解
    • TreeSet 与 Comparable
    • Collections 工具类详解
  • 第13章 IO流

    • 章节导读
    • IO流概述
    • 字节流
    • 字符流
    • 缓冲流
    • 转换流 详解
    • 序列化 详解
    • NIO与Files 详解
    • NIO与Files工具类
  • 第14章 多线程与并发

    • 第十六章 多线程与并发 —— 章节导读
    • 线程基础详解
    • synchronized 详解
    • Lock 与显式锁详解
    • volatile 详解
    • wait 与 notify 详解
    • ThreadLocal详解
    • 原子类详解
    • 并发工具类详解
    • 线程池详解
  • 第15章 反射

    • 章节导读
    • 反射概述与 Class 对象
    • Constructor 与对象创建
    • Field与Method详解
    • 反射应用详解
  • 第16章 JDK8新特性

    • 章节导读
    • Lambda 表达式
    • Stream API 基础
    • Stream API 高级详解
    • Optional 详解
    • 新日期时间API详解
  • 第17章 JDK9-11新特性

    • 章节导读
    • 模块化系统 — Project Jigsaw(JDK 9)
    • var 局部变量类型推断(JDK 10)
    • 集合工厂方法与增强(JDK 9 / 10 / 11)
    • 接口增强:private 方法(JDK 9)
    • Stream API 增强(JDK 9)
    • Optional 增强(JDK 9 / 10 / 11)
    • String 新增方法(JDK 11)
    • HTTP Client 与 Files 增强(JDK 11)
    • 直接运行 Java 源文件 — JEP 330(JDK 11)
  • 第18章 JDK12-17新特性

    • 章节导读
    • Switch 表达式(JDK 12 预览 / JDK 14 正式)
    • 文本块 Text Blocks(JDK 13 预览 / JDK 15 正式)
    • Records 记录类(JDK 14 预览 / JDK 16 正式)
    • 密封类 Sealed Classes(JDK 15 预览 / JDK 17 正式)
    • instanceof 模式匹配(JDK 14 预览 / JDK 16 正式)
    • Switch 模式匹配 — Pattern Matching for switch(JDK 17 预览 / JDK 21 正式)
    • Helpful NPE 与 String 增强(JDK 12 / JDK 14 / JDK 15)
    • Stream 增强(JDK 12 / JDK 16)
    • 日期时间增强 — Day Period 支持(JDK 16)
  • 第19章 JDK18-21新特性

    • 章节导读
    • 虚拟线程(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 序列集合(JDK 21 正式)
    • Switch 模式匹配(JDK 17 预览 / JDK 18 第二预览 / JDK 20 第四预览 / JDK 21 正式)
    • Record 模式匹配(JDK 19 预览 / JDK 20 第二预览 / JDK 21 正式)
    • 未命名模式与变量(JDK 21 预览 / JDK 22 正式)
  • 第20章 JDK 22-25 新特性

    • 章节导读
    • 字符串模板(JDK 22 预览 / JDK 23 第二预览 / JDK 24 第三预览)
    • Stream Gatherers(JDK 22 预览 / JDK 24 第二预览)
    • 隐式声明类与实例方法(JDK 23 预览 / JDK 24 第二预览)
    • 原始类型模式匹配(JDK 24 预览)
  • 附录

    • Java 核心知识点
    • Java SE 专业术语
    • Java特性索引(JDK 8 → 25)

反射应用详解

周五下午,飞翔科技的会议室里,Frank 总监正在主持技术分享会。

Frank:"Spring 的 IoC 容器凭什么能在运行时自动创建 Bean 并注入依赖?JUnit 怎么知道哪些方法要作为测试用例执行?JDBC 的驱动又是如何被加载的?"

白歌:"核心都是反射。反射是框架的基石,没有反射就没有现代 Java 生态。"

大翔走到白板前:"今天我们就来剖析三个经典应用场景:JDBC 驱动加载、Spring IoC 的迷你实现、以及 ORM 框架的底层原理。"

小崔:"大翔哥,能不能写个能跑的小框架出来?"

大翔:"当然可以,今天我们就写一个飞翔科技自己的迷你 IoC 容器,带注解扫描和依赖注入的那种。"


一、定义表

应用场景涉及的反射 API核心原理
JDBC 驱动加载Class.forName("driverClassName")静态代码块中调用 DriverManager.registerDriver()
Spring IoC 容器Class.forName(), newInstance(), Field.set(), Method.invoke()扫描指定包 → 反射创建实例 → 注入依赖
JUnit 测试运行器Method.invoke(), Method.getAnnotation(@Test)扫描 @Test 方法 → 创建实例 → 依次 invoke
ORM 框架getDeclaredFields(), Field.get(), Field.set(), newInstance()表名映射类名 → 列名映射字段名 → 反射读写
通用 JSON 序列化getDeclaredFields(), Field.get(), getGenericType()反射遍历对象字段 → 递归转换为 JSON 字符串
AOP 动态代理Proxy.newProxyInstance(), InvocationHandler.invoke()动态创建代理类 → 拦截方法调用

二、Mermaid 流程图:迷你 IoC 容器实现流程


三、Mermaid 时序图:ORM 映射过程


四、经典应用1:JDBC 驱动加载

JDBC 4.0 之前需要显式调用 Class.forName() 加载驱动:

// JDBC 3.0 时代的标准写法
Class.forName("com.mysql.jdbc.Driver");  // 触发 Driver 类的静态代码块
Connection conn = DriverManager.getConnection(url, user, password);

反射核心原理:Class.forName() 会触发目标类的类加载和静态代码块执行。MySQL 的 Driver 类在静态代码块中将自己注册到 DriverManager。

// MySQL Driver 源码简化示意
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException e) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

JDBC 4.0 以后:Java 引入了 ServiceLoader 机制(SPI),通过 META-INF/services/java.sql.Driver 文件自动加载驱动,不再需要手动 Class.forName()。但这仍然依赖于类加载机制。


五、经典应用2:JUnit 测试运行器

JUnit 通过反射扫描带有 @Test 注解的方法并执行:

// JUnit 核心逻辑简化
Class<?> testClass = XxxTest.class;
Object testInstance = testClass.newInstance();

for (Method method : testClass.getDeclaredMethods()) {
    if (method.isAnnotationPresent(Test.class)) {
        try {
            method.invoke(testInstance);  // 执行测试方法
        } catch (InvocationTargetException e) {
            // 记录测试失败
        }
    }
}

六、完整代码示例

示例1:飞翔科技迷你 IoC 容器(注解扫描 + 反射创建)

import java.lang.annotation.*;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
 * ============================================
 *  飞翔科技迷你 IoC 容器 —— FeixiangIoC
 *  功能:包扫描 + 注解标记 + 反射实例化 + 依赖注入
 * ============================================
 */

// --- 1. 自定义注解 ---

/** 标记需要被 IoC 管理的组件 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface FeixiangComponent {
    String value() default "";  // Bean 名称,默认使用类名首字母小写
}

/** 标记需要自动注入的字段 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface FeixiangAutowired {
}

/** 标记初始化方法(构造后调用) */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface FeixiangPostConstruct {
}

// --- 2. 服务接口和实现 ---

interface EmployeeService {
    List<String> getAllEmployees();
    String getEmployeeById(int id);
    double getAvgSalary();
}

@FeixiangComponent
class EmployeeServiceImpl implements EmployeeService {
    private Map<Integer, String> employees = new LinkedHashMap<>();
    private Map<Integer, Double> salaries = new LinkedHashMap<>();

    @FeixiangPostConstruct
    public void init() {
        employees.put(1001, "大翔");  salaries.put(1001, 18888.88);
        employees.put(1002, "白歌");  salaries.put(1002, 15888.88);
        employees.put(1003, "小崔");  salaries.put(1003, 8888.88);
        employees.put(1004, "朱璐");  salaries.put(1004, 12888.88);
        employees.put(1005, "Frank"); salaries.put(1005, 28888.88);
        System.out.println("  [EmployeeService] 初始化完成,加载 " + employees.size() + " 名员工");
    }

    public List<String> getAllEmployees() {
        List<String> list = new ArrayList<>();
        for (Map.Entry<Integer, String> e : employees.entrySet()) {
            list.add(e.getKey() + " - " + e.getValue() + " (¥" + salaries.get(e.getKey()) + ")");
        }
        return list;
    }

    public String getEmployeeById(int id) {
        String name = employees.get(id);
        return name != null ? name + " (薪资: ¥" + salaries.get(id) + ")" : "未找到";
    }

    public double getAvgSalary() {
        return salaries.values().stream().mapToDouble(Double::doubleValue).average().orElse(0);
    }
}

interface ReportService {
    String generateReport();
}

@FeixiangComponent("reportService")
class ReportServiceImpl implements ReportService {
    // 自动注入 EmployeeService
    @FeixiangAutowired
    private EmployeeService employeeService;

    @FeixiangPostConstruct
    public void init() {
        System.out.println("  [ReportService] 初始化完成,依赖 EmployeeService 注入状态: "
                + (employeeService != null ? "✓ 成功" : "✗ 失败"));
    }

    public String generateReport() {
        StringBuilder sb = new StringBuilder();
        sb.append("========== 飞翔科技员工报表 ==========\n");
        sb.append("总人数: ").append(employeeService.getAllEmployees().size()).append("\n");
        sb.append("平均薪资: ¥").append(String.format("%.2f", employeeService.getAvgSalary())).append("\n");
        sb.append("最高薪员工: Frank (¥28888.88)\n");
        sb.append("=======================================");
        return sb.toString();
    }
}

// --- 3. 迷你 IoC 容器 ---

class FeixiangIoCContainer {
    // Bean 存储:beanName → instance
    private Map<String, Object> beanMap = new LinkedHashMap<>();

    /**
     * 扫描指定包并初始化所有 @FeixiangComponent 标记的类
     */
    public void scanAndInit(String packageName) throws Exception {
        System.out.println("\n[IoC] 开始扫描包: " + packageName);
        System.out.println("=".repeat(55));

        // 1. 扫描包下所有类
        List<Class<?>> classes = scanPackage(packageName);
        System.out.println("[IoC] 发现 " + classes.size() + " 个类,筛选 @FeixiangComponent...\n");

        // 2. 第一轮:创建所有 Bean 实例(暂不注入)
        for (Class<?> clazz : classes) {
            if (clazz.isAnnotationPresent(FeixiangComponent.class)) {
                FeixiangComponent anno = clazz.getAnnotation(FeixiangComponent.class);
                String beanName = anno.value().isEmpty()
                        ? toLowerFirst(clazz.getSimpleName())
                        : anno.value();

                Object instance = clazz.newInstance(); // JDK 8 仍可用 newInstance
                beanMap.put(beanName, instance);
                System.out.println("[IoC] ✓ 创建 Bean: " + beanName + " → " + clazz.getSimpleName());
            }
        }

        // 3. 第二轮:依赖注入
        System.out.println("\n[IoC] 开始依赖注入...\n");
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object bean = entry.getValue();
            Class<?> clazz = bean.getClass();

            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(FeixiangAutowired.class)) {
                    field.setAccessible(true);
                    // 按类型匹配 Bean
                    Object dependency = findBeanByType(field.getType());
                    if (dependency != null) {
                        field.set(bean, dependency);
                        System.out.println("[IoC] 注入: " + entry.getKey()
                                + "." + field.getName() + " ← " + dependency.getClass().getSimpleName());
                    } else {
                        System.out.println("[IoC] ⚠ 未找到依赖: " + field.getType().getSimpleName());
                    }
                }
            }
        }

        // 4. 第三轮:调用 @FeixiangPostConstruct 初始化方法
        System.out.println("\n[IoC] 执行 @FeixiangPostConstruct 初始化方法...\n");
        for (Object bean : beanMap.values()) {
            for (java.lang.reflect.Method method : bean.getClass().getDeclaredMethods()) {
                if (method.isAnnotationPresent(FeixiangPostConstruct.class)) {
                    method.setAccessible(true);
                    method.invoke(bean);
                }
            }
        }

        System.out.println("\n[IoC] 🎉 容器初始化完成!共管理 " + beanMap.size() + " 个 Bean");
    }

    /** 按类型查找 Bean */
    private Object findBeanByType(Class<?> type) {
        for (Object bean : beanMap.values()) {
            if (type.isAssignableFrom(bean.getClass())) {
                return bean;
            }
        }
        return null;
    }

    /** 获取 Bean */
    @SuppressWarnings("unchecked")
    public <T> T getBean(Class<T> clazz) {
        for (Object bean : beanMap.values()) {
            if (clazz.isAssignableFrom(bean.getClass())) {
                return (T) bean;
            }
        }
        return null;
    }

    public Object getBean(String name) {
        return beanMap.get(name);
    }

    /** 包扫描:将包路径下的 .class 文件转为 Class 对象 */
    private List<Class<?>> scanPackage(String packageName) throws Exception {
        List<Class<?>> classes = new ArrayList<>();
        String path = packageName.replace('.', '/');
        URL url = Thread.currentThread().getContextClassLoader().getResource(path);
        if (url == null) return classes;

        File dir = new File(url.toURI());
        for (File file : dir.listFiles()) {
            if (file.getName().endsWith(".class")) {
                String className = packageName + "." + file.getName().replace(".class", "");
                classes.add(Class.forName(className));
            }
        }
        return classes;
    }

    private String toLowerFirst(String str) {
        return Character.toLowerCase(str.charAt(0)) + str.substring(1);
    }
}

// --- 4. 测试主类 ---

public class FeixiangIoCDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("========== 飞翔科技迷你 IoC 容器演示 ==========");

        FeixiangIoCContainer container = new FeixiangIoCContainer();

        // 扫描当前包(将编译后的 .class 文件放在同一目录运行)
        // 注意:在 IDE 中运行时,类文件在 target/classes 或 out/production 目录
        container.scanAndInit(""); // 空字符串代表默认包,实际使用时改为具体包名

        System.out.println("\n" + "=".repeat(55));
        System.out.println("【容器使用测试】\n");

        // 按类型获取 Bean
        EmployeeService empService = container.getBean(EmployeeService.class);
        System.out.println(">>> 查询员工 ID 1003: " + empService.getEmployeeById(1003));
        System.out.println(">>> 平均薪资: ¥" + String.format("%.2f", empService.getAvgSalary()));

        // 按名称获取 Bean
        ReportService reportService = (ReportService) container.getBean("reportService");
        System.out.println("\n" + reportService.generateReport());

        System.out.println("\n========== End ==========");
    }
}

运行输出(在正确配置包路径后):

========== 飞翔科技迷你 IoC 容器演示 ==========

[IoC] 开始扫描包: 
=======================================================
[IoC] 发现 5 个类,筛选 @FeixiangComponent...

[IoC] ✓ 创建 Bean: employeeServiceImpl → EmployeeServiceImpl
[IoC] ✓ 创建 Bean: reportService → ReportServiceImpl

[IoC] 开始依赖注入...

[IoC] 注入: reportService.employeeService ← EmployeeServiceImpl

[IoC] 执行 @FeixiangPostConstruct 初始化方法...

  [EmployeeService] 初始化完成,加载 5 名员工
  [ReportService] 初始化完成,依赖 EmployeeService 注入状态: ✓ 成功

[IoC] 🎉 容器初始化完成!共管理 2 个 Bean

=======================================================
【容器使用测试】

>>> 查询员工 ID 1003: 小崔 (薪资: ¥8888.88)
>>> 平均薪资: ¥17088.88

========== 飞翔科技员工报表 ==========
总人数: 5
平均薪资: ¥17088.88
最高薪员工: Frank (¥28888.88)
=======================================

========== End ==========

示例2:飞翔科技通用 JSON 序列化工具

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 飞翔科技通用 JSON 序列化工具 —— 基于反射
 * 支持:基本类型、String、对象、List、数组
 */
class FeixiangJsonSerializer {

    public static String toJson(Object obj) throws Exception {
        if (obj == null) return "null";
        StringBuilder sb = new StringBuilder();
        serialize(obj, sb);
        return sb.toString();
    }

    private static void serialize(Object obj, StringBuilder sb) throws Exception {
        if (obj == null) {
            sb.append("null");
        } else if (obj instanceof String) {
            sb.append("\"").append(escape((String) obj)).append("\"");
        } else if (obj instanceof Character) {
            sb.append("\"").append(escape(String.valueOf(obj))).append("\"");
        } else if (obj instanceof Number || obj instanceof Boolean) {
            sb.append(obj);
        } else if (obj instanceof Collection) {
            serializeCollection((Collection<?>) obj, sb);
        } else if (obj instanceof Map) {
            serializeMap((Map<?, ?>) obj, sb);
        } else if (obj.getClass().isArray()) {
            serializeArray(obj, sb);
        } else {
            serializeObject(obj, sb);
        }
    }

    private static void serializeObject(Object obj, StringBuilder sb) throws Exception {
        Class<?> clazz = obj.getClass();
        sb.append("{");

        Field[] fields = clazz.getDeclaredFields();
        boolean first = true;
        for (Field field : fields) {
            // 跳过静态字段和合成字段
            if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue;
            if (field.isSynthetic()) continue;

            field.setAccessible(true);
            Object value = field.get(obj);
            if (value == null) continue;  // 跳过 null 值

            if (!first) sb.append(", ");
            sb.append("\"").append(field.getName()).append("\": ");
            serialize(value, sb);
            first = false;
        }
        sb.append("}");
    }

    private static void serializeCollection(Collection<?> coll, StringBuilder sb) throws Exception {
        sb.append("[");
        boolean first = true;
        for (Object item : coll) {
            if (!first) sb.append(", ");
            serialize(item, sb);
            first = false;
        }
        sb.append("]");
    }

    private static void serializeMap(Map<?, ?> map, StringBuilder sb) throws Exception {
        sb.append("{");
        boolean first = true;
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            if (!first) sb.append(", ");
            sb.append("\"").append(entry.getKey()).append("\": ");
            serialize(entry.getValue(), sb);
            first = false;
        }
        sb.append("}");
    }

    private static void serializeArray(Object arr, StringBuilder sb) throws Exception {
        sb.append("[");
        int len = java.lang.reflect.Array.getLength(arr);
        for (int i = 0; i < len; i++) {
            if (i > 0) sb.append(", ");
            serialize(java.lang.reflect.Array.get(arr, i), sb);
        }
        sb.append("]");
    }

    private static String escape(String s) {
        return s.replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("\n", "\\n")
                .replace("\r", "\\r")
                .replace("\t", "\\t");
    }
}

// --- 测试实体 ---

class FeixiangDept {
    private int deptId = 10;
    private String deptName = "研发部";
    private double budget = 188888.88;
    private boolean active = true;
}

class FeixiangEmp {
    private int id = 1001;
    private String name = "大翔";
    private double salary = 18888.88;
    private List<String> skills = Arrays.asList("Java", "Spring", "MySQL", "Redis");
    private FeixiangDept department = new FeixiangDept();
    private Map<String, Double> bonus = new LinkedHashMap<String, Double>() {{
        put("Q1", 5888.88);
        put("Q2", 6688.88);
    }};
    private String nullField = null;  // 此字段会被跳过
}

public class JsonSerializerDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("========== 飞翔科技 JSON 序列化演示 ==========\n");

        FeixiangEmp emp = new FeixiangEmp();
        String json = FeixiangJsonSerializer.toJson(emp);
        System.out.println("序列化结果:");
        System.out.println(json);

        System.out.println("\n--- 格式化输出 ---");
        prettyPrint(json, 0);

        // 测试简单类型
        System.out.println("\n--- 简单类型测试 ---");
        System.out.println("int:    " + FeixiangJsonSerializer.toJson(8888));
        System.out.println("double: " + FeixiangJsonSerializer.toJson(188.88));
        System.out.println("string: " + FeixiangJsonSerializer.toJson("飞翔科技"));
        System.out.println("list:   " + FeixiangJsonSerializer.toJson(Arrays.asList("大翔", "白歌", "小崔")));
        System.out.println("null:   " + FeixiangJsonSerializer.toJson(null));

        System.out.println("\n========== End ==========");
    }

    /** 简易格式化 */
    private static void prettyPrint(String json, int indent) {
        StringBuilder sb = new StringBuilder();
        for (char c : json.toCharArray()) {
            if (c == '{' || c == '[') {
                sb.append(c).append("\n").append(spaces(indent + 2));
                indent += 2;
            } else if (c == '}' || c == ']') {
                indent -= 2;
                sb.append("\n").append(spaces(indent)).append(c);
            } else if (c == ',') {
                sb.append(c).append("\n").append(spaces(indent));
            } else {
                sb.append(c);
            }
        }
        System.out.println(sb.toString());
    }

    private static String spaces(int n) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) sb.append(' ');
        return sb.toString();
    }
}

运行输出:

========== 飞翔科技 JSON 序列化演示 ==========

序列化结果:
{"id": 1001, "name": "大翔", "salary": 18888.88, "skills": ["Java", "Spring", "MySQL", "Redis"], "department": {"deptId": 10, "deptName": "研发部", "budget": 188888.88, "active": true}, "bonus": {"Q1": 5888.88, "Q2": 6688.88}}

--- 格式化输出 ---
{
  "id": 1001,
  "name": "大翔",
  "salary": 18888.88,
  "skills": [
    "Java",
    "Spring",
    "MySQL",
    "Redis"
  ],
  "department": {
    "deptId": 10,
    "deptName": "研发部",
    "budget": 188888.88,
    "active": true
  },
  "bonus": {
    "Q1": 5888.88,
    "Q2": 6688.88
  }
}

--- 简单类型测试 ---
int:    8888
double: 188.88
string: "飞翔科技"
list:   ["大翔","白歌","小崔"]
null:   null

========== End ==========

七、性能优化:反射的三大加速策略

策略1:缓存 Method/Field 对象

// 错误:每次都查找 Method
for (int i = 0; i < 100000; i++) {
    Method m = clazz.getDeclaredMethod("getName");
    m.invoke(bean);
}

// 正确:缓存 Method 对象
Method m = clazz.getDeclaredMethod("getName");
m.setAccessible(true);  // 一次 setAccessible
for (int i = 0; i < 100000; i++) {
    m.invoke(bean);
}

策略2:setAccessible(true) 只调用一次

setAccessible 内部涉及权限检查和安全决策,不应重复调用。在获得 Field/Method 对象后立即调用一次即可。

策略3:使用 MethodHandle(JDK 7+)

// MethodHandle 比反射 invoke 性能更好(接近直接调用)
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findVirtual(Employee.class, "getName",
        MethodType.methodType(String.class));
String name = (String) handle.invoke(employee);

性能对比测试(参考数据)

调用方式100万次耗时相对性能
直接调用~4 ms1x(基准)
MethodHandle~9 ms2.25x
反射 invoke(缓存+setAccessible)~18 ms4.5x
反射 invoke(无缓存)~130 ms32x
反射 invoke(每次 setAccessible)~3500 ms875x

八、易错场景

易错1:Class.newInstance() 要求无参构造器

public class ErrorDemo1 {
    private String name;

    // 只有有参构造器,没有无参构造器
    public ErrorDemo1(String name) { this.name = name; }

    public static void main(String[] args) throws Exception {
        // 抛异常:InstantiationException
        Class<?> clazz = ErrorDemo1.class;
        Object obj = clazz.newInstance(); // ← 失败!
    }
}

// 解决方法:使用 Constructor.newInstance()
Constructor<?> ctor = clazz.getDeclaredConstructor(String.class);
Object obj = ctor.newInstance("大翔");

易错2:反射操作基本类型数组

// 错误:对基本类型数组,不能直接转换为 Object[]
int[] arr = {1, 2, 3};
// Object[] objArr = (Object[]) arr;  // ← ClassCastException!

// 正确:使用 java.lang.reflect.Array 工具类
int len = Array.getLength(arr);
for (int i = 0; i < len; i++) {
    System.out.println(Array.get(arr, i));  // 自动装箱
}

易错3:忽略泛型擦除导致的数据错乱

class Box<T> {
    private T value;
    // getter/setter...
}

// 反射操作时,T 已被擦除为 Object
Box<String> box = new Box<>();
Field field = Box.class.getDeclaredField("value");
field.setAccessible(true);
field.set(box, 12345);  // 可以 set 一个 Integer 进去!泛型在运行时被擦除了
// 但如果你在代码中 get 时,编译器帮你插入的 checkcast 会抛出 ClassCastException

易错4:scanPackage 无法扫描 JAR 包内的类

// 上面的 scanPackage 只适用于文件系统中的 .class 文件
// 如果要扫描 JAR 包内的类,需要使用 Spring 的做法:
// 通过 ClassLoader.getResources() + JarFile 遍历
// 或者使用第三方库如 Reflections、ClassGraph

九、面试考点

考点1:Spring 的 @Autowired 是怎么工作的?

答:

  1. Spring 启动时扫描所有被 @Component(或 @Service、@Controller、@Repository)标注的类。
  2. 通过反射 clazz.newInstance()(或 Constructor)创建实例。
  3. 遍历每个 Bean 的 Field,找到被 @Autowired 标注的字段。
  4. 通过 field.setAccessible(true) 突破私有访问。
  5. 按类型(byType)或名称(byName)从容器中找到对应的 Bean。
  6. 通过 field.set(bean, dependency) 注入依赖。
  7. 如果是构造器注入,则通过 Constructor.newInstance() 传入依赖。

考点2:Class.forName 和 ClassLoader.loadClass 有什么区别?

答:

Class.forNameClassLoader.loadClass
是否执行静态初始化是(执行 static 块)否(默认不执行)
使用哪个类加载器调用者的类加载器指定的类加载器
典型应用JDBC 驱动加载Spring IoC 容器
参数控制forName(name, initialize, loader)loadClass(name)

JDBC 使用 Class.forName 正是因为需要触发驱动的静态代码块来注册自身。

考点3:反射有哪些性能开销?如何优化?

答: 反射的性能开销主要来自四个方面:

  1. 方法/字段查找:getMethod()/getDeclaredField() 需要搜索和比较,开销较大 → 缓存 Method/Field 对象。
  2. 访问权限检查:每次 invoke/get/set 都要校验 → setAccessible(true) 一次。
  3. 参数包装/拆箱:invoke 参数是 Object[],基本类型需要装箱 → 在高频调用场景考虑 MethodHandle 或代码生成。
  4. JIT 无法充分优化:反射调用难以被内联和优化 → 热点代码避免使用反射。

考点4:有没有可能完全替代反射的方案?

答:

  • 编译期注解处理器(APT):如 Lombok、Dagger 2,在编译期生成代码,运行时不依赖反射,性能最优。
  • MethodHandle(JDK 7+):比反射更接近直接调用,JIT 友好,但仍需要动态查找。
  • 代码生成 + 动态编译:如 CGLIB、ByteBuddy、Javaassist,运行时生成字节码代替反射。
  • InvokeDynamic(JDK 7+):lambda 表达式底层使用的机制,性能接近直接调用。
  • 但反射的灵活性无可替代——编译期注解处理器无法应对运行时动态变化的需求。Spring 5 开始部分使用反射替代方案(如 MethodHandle),但核心仍是反射。

大翔合上笔记本:"今天我们只看到了反射的冰山一角。Spring 的 IoC 容器有几十万行代码,处理了循环依赖、作用域、生命周期回调等大量细节——但最核心的思想,就是我们今天写的这 200 行。"

Frank 总监:"没错。理解底层原理,你才能在使用框架时做出正确的架构决策。知道 Spring 怎么创建 Bean,你就不会在构造器里写耗时操作。"

朱璐感叹:"原来反射这么强大!我之前用 Apache BeanUtils 复制属性觉得理所当然,现在才知道底层就是 getDeclaredFields + get + set。"

白歌笑道:"而且你现在也知道了为什么 BeanUtils 比手写 get/set 慢几十倍。"

黄俪路过会议室,敲了敲门:"技术分享结束了吗?财务系统要同步一下员工薪资数据。"

大翔笑了:"正好,我们用刚写的 JSON 序列化工具来同步数据!"


附:JDK 8 反射关键新增

API说明
Class.getMethod(name, types).getParameterCount()获取方法参数个数
Class.getMethod(name, types).getParameters()获取 Parameter 数组(需 -parameters 编译)
AnnotatedElement.getAnnotationsByType(Class)支持 @Repeatable 注解
Method.isDefault()判断接口默认方法
Executable 抽象类Field/Method/Constructor 的新公共父类

警告:JDK 8 中 Class.newInstance() 仍可用但已被标记为 @Deprecated(since JDK 9)。在 JDK 8 教程中我们仍然使用它,但学习者应当了解推荐替代方案是 Constructor.newInstance()。

上一页
Field与Method详解