SpringBoot 源码理解

-- 阅读之前先掌握这个

  • 常见的ApplicationContext: FileSystemXmlApplicationContext,ClassPathXmlApplicationContext,AnnoationConfigApplicationContext,ConfigurableApplicationContext,GenericApplicationContext(很多)
  • 常见的Scanner:ClassPathBeanDefinationScanner,AnnotationBeanDefinitionScanner
java 复制代码
// 创建容器
GenericApplicationContext applicationContext = new GenericApplicationContext();

// 确定bean类型
AbstractBeanDefinition beanDefinition =
        BeanDefinitionBuilder.genericBeanDefinition(类).setScope(域).getBeanDefinition();
// 注册bean
applicationContext.registerBeanDefinition();
//启动IOC
applicationContext.refresh();
// ....
// 实现注解的各种方式
//....
applicationContext.close();
  • AOP
java 复制代码
// 设置proxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
// 设置目标对象
proxyFactory.setTarget();
// 强制使用cglib
proxyFactory.setProxyTargetClass();
// 设置jdk动态代理
proxyFactory.setInterfaces();
// 设置通知
proxyFactory.addAdvice()
// proxyFactory.addAdvisor()配合DefaultPointcutAdvisor()

资源加载

java 复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 加载各种个样的配置文件
    this.resourceLoader = resourceLoader;
    // 断言工具
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 加载类的(给IOC的)
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 判断web类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList<>(
          getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 从ApplicationContext初始化前读取的(springBoot特有的)
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 从监听器读取,横跨整个ApplicationContext生命周期(SpringBoot特有的方法)
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //主类推断(通过这个和primarySource就可以得出当前是不是主类加载)
    this.mainApplicationClass = deduceMainApplicationClass();
}

web应用判断

Java 复制代码
static WebApplicationType deduceFromClasspath() {
//  使用 classUtils 判断是不是WebFlux的类型
    if ( ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && 
        !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)  &&
        !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        // 返回一个Reactive类型项目
       return WebApplicationType.REACTIVE;
    }
    // 判断是不是非Web类型
    for (String className : SERVLET_INDICATOR_CLASSES) {
       if (!ClassUtils.isPresent(className, null)) {
          return WebApplicationType.NONE;
       }
    }
    // 以上都不是即Servlet项目
    return WebApplicationType.SERVLET;
}

spring.factories文件(SpringBoot的SPI原理)

  • getSpringFactoriesInstances和createSpringFactoriesInstances这两个实际上就是创建和获取Spring.factories本质没有什么可讲的。
java 复制代码
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
       classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
       return result;
    }

    result = new HashMap<>();
    try {
       Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
       while (urls.hasMoreElements()) {
          URL url = urls.nextElement();
          UrlResource resource = new UrlResource(url);
          Properties properties = PropertiesLoaderUtils.loadProperties(resource);
          for (Map.Entry<?, ?> entry : properties.entrySet()) {
             String factoryTypeName = ((String) entry.getKey()).trim();
             String[] factoryImplementationNames =
                   StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
             for (String factoryImplementationName : factoryImplementationNames) {
                result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                      .add(factoryImplementationName.trim());
             }
          }
       }

       // Replace all lists with unmodifiable lists containing unique elements
       result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
             .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
       cache.put(classLoader, result);
    }
    catch (IOException ex) {
       throw new IllegalArgumentException("Unable to load factories from location [" +
             FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

run方法核心原理

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    // 统计启动时间
    long startTime = System.nanoTime();
    //创建DefaultBootstrapContext
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    // 创建ApplicationContext编译对象
    ConfigurableApplicationContext context = null;
    //
    configureHeadlessProperty();
    // 创建SpringApplicationRunListeners监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 开始监听
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
      // 传入命令行参数
       ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       // 准备环境
       ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
       configureIgnoreBeanInfo(environment);
       // 控制台打印欢迎信息
       Banner printedBanner = printBanner(environment);
       // 赋值ConfigurableApplicationContext运行对象
       context = createApplicationContext();
       context.setApplicationStartup(this.applicationStartup);
       // 准备上下文
       prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
       // 刷新上下文,启动SpringIOC
       refreshContext(context);
       // 回调操作
       afterRefresh(context, applicationArguments);
       // 计算耗时时间
       Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
       if (this.logStartupInfo) {
       //输出耗时日志
          new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
       }
       // 监听完毕
       listeners.started(context, timeTakenToStartup);
       // 查看CommandLineRunner和applicationRunner方法
       callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
    // 处理启动异常
       handleRunFailure(context, ex, listeners);
       throw new IllegalStateException(ex);
    }
    try {
    // 计算耗时时间
       Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
       // 通知监听器,应用已经准备好
       listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
    // 处理异常
       handleRunFailure(context, ex, null);
       throw new IllegalStateException(ex);
    }
    // 返回已经创建好的上下文
    return context;
}
相关推荐
小蒜学长40 分钟前
基于Java的公司采购系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·公司采购系统
小坏讲微服务1 小时前
Spring Boot 4 新特性全解析:从上手到生产实战
java·spring boot·后端·架构·springboot4
huaweichenai3 小时前
spring boot 实现file文件上传
java·spring boot·后端
Zelman4 小时前
测试层级与测试类型
后端·面试·测试
韩振方4 小时前
容器已经能运行了,为什么 Kubernetes 还要用 Pod?
后端
会编程的吕洞宾4 小时前
AgentScope Java 实战:给 AI Agent 加上权限管控
后端
Thneonl6 小时前
全集群钟差 300 毫秒会发生什么:证书悄悄过期,日志倒流
运维·后端
金銀銅鐵6 小时前
[Java] 借助GUI展示class文件的版本号
后端·python·ai编程
马剑威(威哥爱编程)6 小时前
【AI全栈后端12-12】Spring Boot 3.x 到 4.x 迁移实操:Jakarta 11、Jackson 3 与 AI 2.0
java·开发语言·spring boot·后端