今天在看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
)启动时,会执行以下反射相关的步骤,创建UserService
和UserDao
对象:
步骤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
对象
- 对于
userDao
,class
属性的值是"com.example.UserDao"
; - 对于
userService
,class
属性的值是"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将创建的userDao
和userService
对象存入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是通用框架,需要处理任意用户定义的类(如
UserService
、OrderService
、ProductDao
),无法在框架代码中硬编码new UserService()
(因为框架开发者不知道用户会定义哪些类)。 - 反射的动态性(
Class.forName()
加载任意类,newInstance()
创建任意对象)满足了这一需求,让Spring能处理无限多的用户类。