찾다
Javajava지도 시간SpringBoot 시작 원리는 무엇입니까?

入口

版本: 2.1.8.RELEASE

启动代码:

@SpringBootApplcation
public static void main(String[] args) {
    SpringApplication.run(BlogAdminApplication.class, args);
    System.out.println("======== admin start success... ==========");
}

这里传入了两个参数,BlogAdminApplication当前类和args参数

我们点击进入run方法查看

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}

这里是将我们写的启动类传入到了Class[]数组中,这步就是个单纯的参数转换。

探讨primarySource参数

那么问题是primarySource能接受的类型是啥样的,是不是什么类都可以接受,带着这个疑问,我们做一个测试,把这个参数给换成一个别的类呢,ManagerController类是一个我写的接口类

@SpringBootApplication
public class BlogProjectApplication {
   public static void main(String[] args) {
       SpringApplication.run(ManagerController.class, args);
       System.out.println("======== admin start success... ==========");
   }
}

控制台打印

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

提示不能启动服务,提示缺少了ServletWebServerFactory bean 点进这个类看下

@FunctionalInterface
public interface ServletWebServerFactory {
    WebServer getWebServer(ServletContextInitializer... initializers);
}

他被FunctionalInterface标注了,是一个函数式接口,只有一个getWebServer方法,用来获取webServer的 看下他的实现类,

SpringBoot 시작 원리는 무엇입니까?

这不就是提示我们缺少启动的服务容器么,说的直白点,我的理解就是他缺少可以运行的容器,我们知道,没有使用springboot项目之前,我们的项目都是跑在tomcat容器上的,当然也有使用Jetty容器的。再者,我们知道SpringBoot是对tomcat进行了内置。而SpringBoot不仅仅是只有内置了tomcat,而且还内置了好多的东西,比如我们经常使用的mq、redis等等一系列的东西,这个我们可以在spring.factories配置文件中看到,这个文件位于如下位置

SpringBoot 시작 원리는 무엇입니까?

大概内容有下,篇幅有限,就不一一列举了。

省略。。。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
省略。。。

那么回过头来,我们再看下这个问题,这些个类是如何被加载进来的,我们知道SpingBoot有个注解是开启自动注解的@EnableAutoConfiguration,他就干这个事情的。他能够激活SpringBoot内建和自定义组件的自动装配特性。

那么,知道了这些,我们把这个之前修改后的类给改造一下,加上注解@EnableAutoConfiguration,看下执行效果。

@RestController
@RequestMapping("project/manager")
@EnableAutoConfiguration
public class ManagerController extends AbstractController {

运行如下

SpringBoot 시작 원리는 무엇입니까?

从打印信息就能知道,服务器有了,只不过下面报错,提示找不到bean,那这不就简单了么,他是不是就是没有扫描到我们的包么,这里就其实可以在配置扫描包的注解继续测试,我就懒的不测试了,直接去看@SpringBootApplication注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
//
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

@SpringBootApplication这个注解其实就是一个组合注解,里面包含了

  • 元注解:用来标注他是一个注解的,jdk自带的

  • @SpringBootConfiguration:集成自@Configuration,表示是一个配置类

  • @EnableAutoConfiguration:激活SpringBoot内建和自定义组件的自动装配特性

  • @ComponentScan:扫描注解,添加了排除参数,指定排除了一些类

通过这里的实验,我们可以得出结论:

primarySource参数能接收的类是一个配置类,同时要把符合扫描规则的类装配到spring容器中,并且对SpringBoot内置的一些类进行自动扫描到,而这里的@SpringBootApplication注解就是把这些特性都整合到了一起,作为了一个引导类而已。那么说白了,primarySource他接受的其实就是一个配置类。

关于注解详细知识的话,这里就聊这么多了,后面再详细聊。

args参数

args是Java命令行参数,我们在DOS中执行Java程序的时候使用“java 文件名 args参数”。args这个数组可以接收到这些参数。这个是个基础常识了。

以下我们将继续跟踪源码进行分析

我们继续追run()方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

这个方法干了两个事情:

1、new SpringApplication()来创建对象

2、通过创建后的对象,调用对象里面的run()方法

以下我们将从这两个地方进行分析,本篇就先研究第一个

创建对象

我们先看下他是怎么创建对象的,创建了哪些对象,

public SpringApplication(Class<?>... primarySources) {
    this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //资源加载器
   this.resourceLoader = resourceLoader;
   //断言
   Assert.notNull(primarySources, "PrimarySources must not be null");
   //对primarySources进行存储到LinkedHashSet
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   //1、推断web应用类别
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   //2、加载Spring应用上下文初始化
   setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
   //3、加载Spring应用事件监听器
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //4、推断应用引导类
   this.mainApplicationClass = deduceMainApplicationClass();
}

下面研究下主要流程部分

1、推断web应用类别

推断web应用类型属于SpringBoot应用web类型的初始化过程。而该类型也可在SpringApplication构造后,run方法执行之前,通过setWebApplicationType(WebApplicationType webApplicationType)方法进行调整。

在推断Web应用类型的过程中,由于当前Spring应用上下文尚未准备(可在代码执行顺序中看到),所以实现采用的是检查检查当前ClassLoader下基准Class的存在性判断。

上源码

this.webApplicationType = WebApplicationType.deduceFromClasspath();
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
      "org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
//当DispatcherHandler存在,且DispatcherServlet、ServletContainer两个不存在时;换言之,SpringBoot仅依赖WebFlux存在时,此时的应用类型为REACTIVE
   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
      return WebApplicationType.REACTIVE;
   }
   //当Servlet和ConfigurableWebApplicationContext均不存在时,当前应用为非Web应用,即WebApplicationType.NONE,因为这些API均是Spring Web MVC必须的依赖
   for (String className : SERVLET_INDICATOR_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) {
         return WebApplicationType.NONE;
      }
   }
   //当WebFlux和Spring Web MVC同时存在时,Web应用类型同样是Servlet Web,即WebApplicationType.SERVLET
   return WebApplicationType.SERVLET;
}

2、加载Spring应用上下文初始化

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

此过程包含两个动作,依次为getSpringFactoriesInstances(ApplicationContextInitializer.class)和setInitializers方法。 先看第一个过程

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
   return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
   //获取类加载器
   ClassLoader classLoader = getClassLoader();
   // Use names and ensure unique to protect against duplicates
   //加载了META-INF/spring.factories资源中配置的ApplicationContextInitializer实现类名单。
   Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
   //初始化
   List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
   AnnotationAwareOrderComparator.sort(instances);
   return instances;
}

此处使用了Spring工厂加载机制方法SpringFactoriesLoader.loadFactoryNames(type, classLoader)。加载了META-INF/spring.factories资源中配置的ApplicationContextInitializer实现类名单。

SpringBoot 시작 원리는 무엇입니까?

加载完成后使用createSpringFactoriesInstances方法对其进行初始化。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
      ClassLoader classLoader, Object[] args, Set<String> names) {
   List<T> instances = new ArrayList<>(names.size());
   for (String name : names) {
      try {
      //从类加载器中获取指定类
         Class<?> instanceClass = ClassUtils.forName(name, classLoader);
         //判断instanceClass是不是type的子类
         Assert.isAssignable(type, instanceClass);
         //根据以上获取的类名创建类的实例
         Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
         //排序
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
         instances.add(instance);
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
      }
   }
   return instances;
}

3、加载Spring应用事件监听器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

此过程与2、加载上下文初始化基本类似。 只不过初始化的对象类型变成了ApplicationListener.class,setListeners方法也只是赋值而已

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
   this.listeners = new ArrayList<>();
   this.listeners.addAll(listeners);
}

SpringBoot 시작 원리는 무엇입니까?

4、推断应用引导类

this.mainApplicationClass = deduceMainApplicationClass(); 
private Class<?> deduceMainApplicationClass() {
  try {
     StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
     for (StackTraceElement stackTraceElement : stackTrace) {
        if ("main".equals(stackTraceElement.getMethodName())) {
           return Class.forName(stackTraceElement.getClassName());
        }
     }
  }
  catch (ClassNotFoundException ex) {
     // Swallow and continue
  }
  return null;
}

这里使用到了new RuntimeException().getStackTrace()来获取堆栈信息,找到调用执行的main方法,从而确定他的类。
这里有个疑问:他不是传了primarySources数组,里面包含了类名么,怎么还用堆栈的方式去获取,此外,这里的堆栈获取也只能获取一个调用的主main方法,他为啥还要传一个Class数组呢?

具体咋获取的,可以追下源码,一直跟踪他的父类Throwable,找到如下代码

/**
 * Fills in the execution stack trace. This method records within this
 * {@code Throwable} object information about the current state of
 * the stack frames for the current thread.
 *
 * <p>If the stack trace of this {@code Throwable} {@linkplain
 * Throwable#Throwable(String, Throwable, boolean, boolean) is not
 * writable}, calling this method has no effect.
 *
 * @return  a reference to this {@code Throwable} instance.
 * @see     java.lang.Throwable#printStackTrace()
 */
public synchronized Throwable fillInStackTrace() {
    if (stackTrace != null ||
        backtrace != null /* Out of protocol state */ ) {
        fillInStackTrace(0);
        stackTrace = UNASSIGNED_STACK;
    }
    return this;
}
private native Throwable fillInStackTrace(int dummy);

这里最后调用了native本地方法,去爬取线程堆栈信息,为运行时栈做一份快照。

SpringBoot 시작 원리는 무엇입니까?

通过这个图片,可以看到整个方法的调用链,从下往上看哦

위 내용은 SpringBoot 시작 원리는 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
이 기사는 亿速云에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제
고급 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 또는 Gradle을 어떻게 사용합니까?고급 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 또는 Gradle을 어떻게 사용합니까?Mar 17, 2025 pm 05:46 PM

이 기사에서는 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 및 Gradle을 사용하여 접근 방식과 최적화 전략을 비교합니다.

적절한 버전 및 종속성 관리로 Custom Java 라이브러리 (JAR Files)를 작성하고 사용하려면 어떻게해야합니까?적절한 버전 및 종속성 관리로 Custom Java 라이브러리 (JAR Files)를 작성하고 사용하려면 어떻게해야합니까?Mar 17, 2025 pm 05:45 PM

이 기사에서는 Maven 및 Gradle과 같은 도구를 사용하여 적절한 버전 및 종속성 관리로 사용자 정의 Java 라이브러리 (JAR Files)를 작성하고 사용하는 것에 대해 설명합니다.

카페인 또는 구아바 캐시와 같은 라이브러리를 사용하여 자바 애플리케이션에서 다단계 캐싱을 구현하려면 어떻게해야합니까?카페인 또는 구아바 캐시와 같은 라이브러리를 사용하여 자바 애플리케이션에서 다단계 캐싱을 구현하려면 어떻게해야합니까?Mar 17, 2025 pm 05:44 PM

이 기사는 카페인 및 구아바 캐시를 사용하여 자바에서 다단계 캐싱을 구현하여 응용 프로그램 성능을 향상시키는 것에 대해 설명합니다. 구성 및 퇴거 정책 관리 Best Pra와 함께 설정, 통합 및 성능 이점을 다룹니다.

캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA (Java Persistence API)를 어떻게 사용하려면 어떻게해야합니까?캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA (Java Persistence API)를 어떻게 사용하려면 어떻게해야합니까?Mar 17, 2025 pm 05:43 PM

이 기사는 캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA를 사용하는 것에 대해 설명합니다. 잠재적 인 함정을 강조하면서 성능을 최적화하기위한 설정, 엔티티 매핑 및 모범 사례를 다룹니다. [159 문자]

Java의 클래스로드 메커니즘은 다른 클래스 로더 및 대표 모델을 포함하여 어떻게 작동합니까?Java의 클래스로드 메커니즘은 다른 클래스 로더 및 대표 모델을 포함하여 어떻게 작동합니까?Mar 17, 2025 pm 05:35 PM

Java의 클래스 로딩에는 부트 스트랩, 확장 및 응용 프로그램 클래스 로더가있는 계층 적 시스템을 사용하여 클래스로드, 링크 및 초기화 클래스가 포함됩니다. 학부모 위임 모델은 핵심 클래스가 먼저로드되어 사용자 정의 클래스 LOA에 영향을 미치도록합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

DVWA

DVWA

DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

맨티스BT

맨티스BT

Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

안전한 시험 브라우저

안전한 시험 브라우저

안전한 시험 브라우저는 온라인 시험을 안전하게 치르기 위한 보안 브라우저 환경입니다. 이 소프트웨어는 모든 컴퓨터를 안전한 워크스테이션으로 바꿔줍니다. 이는 모든 유틸리티에 대한 액세스를 제어하고 학생들이 승인되지 않은 리소스를 사용하는 것을 방지합니다.