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 就是全部实现”代替框架机制。