反射在Spring IOC容器中的应用——动态创建Bean

发布于:2025-08-14 ⋅ 阅读:(12) ⋅ 点赞:(0)

今天在看Java八股文时,对这里产生了一些疑惑,因为在目前做的练手项目中还没有用到过除了new以外的新建对象方式,在请教了其他前辈后对此有了新的理解,所以专门记录以用于梳理思路和复习基础。这里着重讲解反射机制实现新建对象

这里用很经典的一个框架:Spring IOC容器来举例讲解

========================================================================

Spring的**控制反转(IOC)**容器是反射最经典的应用场景。其核心逻辑是:通过反射动态加载用户定义的类,创建对象,并注入依赖

1. 场景说明

假设我们有一个UserService类,依赖UserDao类(用户定义的类):

// 用户定义的Dao类(数据访问层)
public class UserDao {
    public void save(User user) {
        System.out.println("保存用户到数据库:" + user);
    }
}

// 用户定义的Service类(业务层),依赖UserDao
public class UserService {
    private UserDao userDao; // 依赖注入的属性

    // 构造器注入(或setter注入)
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void register(User user) {
        userDao.save(user); // 调用Dao的方法
    }
}

用户通过Spring配置文件(或注解)声明Bean:

<!-- applicationContext.xml 配置文件 -->
<beans>
    <!-- 声明UserDao Bean:类名是com.example.UserDao -->
    <bean id="userDao" class="com.example.UserDao"/>

    <!-- 声明UserService Bean:依赖userDao -->
    <bean id="userService" class="com.example.UserService">
        <constructor-arg ref="userDao"/> <!-- 构造器注入userDao -->
    </bean>
</beans>

2. Spring IOC容器的工作流程(反射应用细节)

Spring容器(如ClassPathXmlApplicationContext)启动时,会执行以下反射相关的步骤,创建UserServiceUserDao对象:

步骤1:加载配置文件,解析Bean定义

Spring读取applicationContext.xml,解析出两个Bean定义:

  • userDao:类名com.example.UserDao,无依赖;
  • userService:类名com.example.UserService,依赖userDao
步骤2:通过反射加载类(Class.forName()

① 解析配置文件,获取类名

Spring的XmlBeanDefinitionReader(XML配置解析器)读取applicationContext.xml,解析每个<bean>标签的class属性:

② 加载类的Class对象

  • 对于userDaoclass属性的值是"com.example.UserDao"
  • 对于userServiceclass属性的值是"com.example.UserService"

Spring使用Class.forName()加载每个类的Class对象(运行时动态加载):

// 加载UserDao类(com.example.UserDao)
Class<?> userDaoClass = Class.forName("com.example.UserDao", false, classLoader);
// 加载UserService类(com.example.UserService)
Class<?> userServiceClass = Class.forName("com.example.UserService", false, classLoader);
步骤3:通过反射获取构造器(getConstructor()

Spring需要获取类的构造器,才能创建对象。对于UserDao(无依赖),获取无参构造器;对于UserService(依赖UserDao),获取有参构造器(参数类型为UserDao):

// 获取UserDao的无参构造器(UserDao有默认无参构造器)
Constructor<?> userDaoConstructor = userDaoClass.getConstructor();
// 获取UserService的有参构造器(参数类型为UserDao)
Constructor<?> userServiceConstructor = userServiceClass.getConstructor(UserDao.class);
步骤4:通过反射创建对象(newInstance()
  • 创建UserDao对象:用无参构造器创建:
  •   // 创建UserDao对象(无参构造器)
      UserDao userDao = (UserDao) userDaoConstructor.newInstance();

  • 创建UserService对象:用有参构造器创建,传入userDao对象(依赖注入):
  •   // 创建UserService对象(有参构造器,注入userDao)
      UserService userService = (UserService) userServiceConstructor.newInstance(userDao);

步骤5:将对象存入IOC容器

Spring将创建的userDaouserService对象存入IOC容器(如HashMap,键是bean id,值是对象):

// 模拟IOC容器(HashMap)
Map<String, Object> iocContainer = new HashMap<>();
iocContainer.put("userDao", userDao);
iocContainer.put("userService", userService);
步骤6:使用Bean(从容器中获取)

用户通过ApplicationContext从容器中获取userService对象,无需手动new

// 启动Spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取userService对象(无需new)
UserService userService = (UserService) context.getBean("userService");
// 使用userService调用方法(依赖的userDao已注入)
userService.register(new User("zhangsan", 25));

为什么用反射?

  • Spring是通用框架,需要处理任意用户定义的类(如UserServiceOrderServiceProductDao),无法在框架代码中硬编码new UserService()(因为框架开发者不知道用户会定义哪些类)。
  • 反射的动态性Class.forName()加载任意类,newInstance()创建任意对象)满足了这一需求,让Spring能处理无限多的用户类。