DEV Community

Liu yu
Liu yu

Posted on

ApplicationContext反射调用链

背景是,GPT4o给我提了一个问题:ApplicationContext是如何创建Bean的?

测试代码

package com.wiring;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

@Component
class SimpleBean {

    public SimpleBean() {

        System.out.println("1. SimpleBean 构造函数被调用");
    }

    @PostConstruct
    public void init() {

        System.out.println("2. SimpleBean 初始化方法被调用");
    }

    @PreDestroy
    public void destroy() {

        System.out.println("3. SimpleBean 销毁方法被调用");
    }
}


public class Main {
    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext("com.wiring");

        //反射构造
        SimpleBean bean = context.getBean(SimpleBean.class);

        context.close();
    }
}
Enter fullscreen mode Exit fullscreen mode

现在主要想研究这句话

SimpleBean bean = context.getBean(SimpleBean.class);
Enter fullscreen mode Exit fullscreen mode

详细解释 Spring 创建 Bean 实例的完整调用链:

1. getBean() - Spring 入口

// AbstractBeanFactory
public Object getBean(String name) throws BeansException {
    // 获取 bean,如果不存在则创建
    return doGetBean(name, null, null, false);
}
Enter fullscreen mode Exit fullscreen mode

2. createBeanInstance() - 开始创建实例

// AbstractAutowireCapableBeanFactory
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 通过反射获取构造函数并创建实例
    Constructor<?> constructorToUse = getConstructorToUse(beanName, mbd);
    return instantiateBean(beanName, mbd);
}
Enter fullscreen mode Exit fullscreen mode

3. Constructor.newInstance() - Java反射API

@CallerSensitive
@ForceInline
public T newInstance(Object... initargs) {
    // 获取调用者并执行实例化
    Class<?> caller = override ? null : Reflection.getCallerClass();
    return newInstanceWithCaller(initargs, !override, caller);
}
Enter fullscreen mode Exit fullscreen mode

4. newInstanceWithCaller() - 权限检查和访问器获取

private T newInstanceWithCaller(Object[] args, boolean checkAccess, Class<?> caller) {
    // 检查访问权限
    if (checkAccess) checkAccess(caller, clazz, clazz, modifiers);

    // 获取构造函数访问器
    ConstructorAccessor ca = constructorAccessor;
    if (ca == null) {
        ca = acquireConstructorAccessor();
    }

    // 创建实例
    return (T) ca.newInstance(args);
}
Enter fullscreen mode Exit fullscreen mode

5. ConstructorAccessor.newInstance() - 实例创建准备

// DirectConstructorHandleAccessor
public Object newInstance(Object[] args) throws InstantiationException {
    // 检查参数
    if (args != null && args.length != paramCount) {
        throw new IllegalArgumentException("参数个数不匹配");
    }
    // 调用实际的构造函数
    return invokeImpl(args);
}
Enter fullscreen mode Exit fullscreen mode

6. invokeImpl() - 最终的实例创建

@Hidden
@ForceInline
Object invokeImpl(Object[] args) throws Throwable {
    return switch (paramCount) {
        case 0 -> target.invokeExact();         // 无参构造
        case 1 -> target.invokeExact(args[0]);  // 单参数构造
        case 2 -> target.invokeExact(args[0], args[1]);
        case 3 -> target.invokeExact(args[0], args[1], args[2]);
        default -> target.invokeExact(args);
    };
}
Enter fullscreen mode Exit fullscreen mode

总结

从技术上讲,最终创建对象实例的是 invokeExact(args) 这个调用。其他所有的步骤都是为了确保这个调用能够正确、安全、并且在正确的上下文中发生。

  1. Spring 容器的管理(getBean():这一步骤是 Spring 容器的入口,它负责检查缓存、解析 Bean 定义、处理作用域和依赖注入等。这些操作确保了 Bean 的创建是在正确的上下文中进行的,例如,如果 Bean 是单例的,那么它只会被创建一次。

  2. 实例创建策略(createBeanInstance():这一步骤根据 Bean 定义来决定如何创建 Bean 实例。它会解析构造函数、处理依赖注入、处理代理逻辑等。这些操作确保了 Bean 的创建符合 Spring 的依赖注入和 AOP 功能。

  3. 反射调用(Constructor.newInstance():这一步骤是 Java 反射 API 的一部分,它负责动态调用构造函数。这一步骤确保了构造函数的调用是动态的,可以处理不同的参数和异常。

  4. 权限检查(newInstanceWithCaller():这一步骤检查访问权限,确保调用者有权限访问目标类的构造函数。这一步骤确保了代码的安全性。

  5. 参数处理(ConstructorAccessor.newInstance():这一步骤检查参数,确保传入的参数与构造函数的参数匹配。这一步骤确保了参数的正确性。

  6. 最终创建(invokeExact():这一步骤是最终的实例化操作,它调用目标类的构造函数。这一步骤是真正创建对象实例的地方。

Top comments (0)