Spring 依赖注入可以从“把另一个对象交给当前对象”理解,但不能把容器全部简化成 HashMap 加反射。对象定义、依赖关系、作用域与生命周期都在参与其中。
value 是值,ref 是另一个 Bean
下面用两个公开静态嵌套类说明。Java 文件放在 example 包中:
package example;
public class ReflectDemo {
public static class Engine {}
public static class Car {
private String name;
private Engine engine;
public Car() {}
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public void setEngine(Engine engine) { this.engine = engine; }
public Engine getEngine() { return engine; }
}
}
相应 XML:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="engine" class="example.ReflectDemo$Engine"/>
<bean id="car" class="example.ReflectDemo$Car">
<property name="name" value="demo"/>
<property name="engine" ref="engine"/>
</bean>
</beans>
name 注入文本值,engine 引用同名 Bean;setter 命名需符合属性约定。ref 不是传一个字符串“engine”给字段,而是由容器解析依赖。注入还可以通过构造器等方式完成,不限于 setter。Spring 5 IoC 依赖
取得 Class 与创建对象是两步
以下片段位于声明 throws ReflectiveOperationException 的方法内,所引用的类已在类路径中:
Class<?> type = Class.forName("example.ReflectDemo$Car");
Object instance = type.getDeclaredConstructor().newInstance();
java.lang.reflect.Method setter = type.getMethod("setName", String.class);
setter.invoke(instance, "reflection-demo");
java.lang.reflect.Method getter = type.getMethod("getName");
System.out.println(getter.invoke(instance));
for (java.lang.reflect.Field field : type.getDeclaredFields()) {
System.out.println("声明字段:" + field.getName());
}
for (java.lang.reflect.Method method : type.getMethods()) {
System.out.println("公开方法:" + method.getName());
}
Class.forName 返回类型对应的 Class,并可能触发初始化,不直接返回 Car 实例。构造器、字段和方法查找是独立步骤;getDeclaredFields 包括当前类声明的非公开字段,不含继承字段;getMethods 包括公开的继承方法,不等于所有可见性的方法。Java 8 Class API
查方法要匹配参数类型,例如 int.class 与 Integer.class 不是同一个签名。反射调用还可能包裹目标方法异常,不能把所有失败都归结为类名错误。
容器管理的不只是创建
容器按 Bean 定义组织依赖、属性填充和初始化,必要时还会经过后处理器或生成代理。业务代码中的引用不是供人读写的物理地址,把两个对象画成地址数字仅能作为比喻。
Spring 的 singleton 是每容器、每 Bean 定义的作用域,不是整个 JVM 某个类只能存在一个实例。不同容器或同类的不同定义可以有不同对象,也不能据此要求 ApplicationContext 必须写成全局单例。Spring 5 作用域
示例解释配置与反射的关系,不用没有源码依据的 DOM4J 说法、固定创建顺序或“HashMap 就是全部实现”代替框架机制。