SPRINGBOOT启动原理(基于3.x版本)(一)

简介: SPRINGBOOT启动原理(基于3.x版本)(一)

版本


版本:3.0.1


注解


我们从启动类入手,springboot的启动类上要添加 @SpringBootApplication 注解,我们先看一下这个注解的作用:


@SpringBootApplication


//用于描述类、接口(包括注解类型) 或enum声明
@Target({ElementType.TYPE})
//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Retention(RetentionPolicy.RUNTIME)
//表明这个注解应该被 javadoc工具记录
@Documented
//子类将会继承该注解
@Inherited
//相当于@Configuration
@SpringBootConfiguration
//启用 SpringBoot 的自动配置机制
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)


@EnableAutoConfiguration


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
//使用Import自动导入所有符合自动配置条件的Bean定义并加载到IOC容器
@Import(AutoConfigurationImportSelector.class)


这个注解里面比较引人注意的是这两个注解:


@AutoConfigurationPackage


@Import({AutoConfigurationImportSelector.class})


介绍


三种将类交给spring管理,也就是加入IOC容器的方式:


  • @Bean


  • @Componet/@Service


  • @Import


@Import一般作用于@Configuration定义的类上,它有三种用法


先假设我们有3个类


public class Apple {
}
public class Banana {
}
public class Cherry {
}


都通过这个类注入:


@Configuration
public class Fruits {
}


指定class数组方式


@Configuration
@Import(Apple.class)
public class Fruits {
}


ImportSelector方式(Spring Boot底层采用比较得多的方式)


public class BananaImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.test.Banana"};
    }
}
@Configuration
@Import({Apple.class,BananaImportSelector.class})
public class Fruits {
}


ImportBeanDefinitionRegistrar方式


public class CherryDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(Cherry.class);
        //这里还可以给Cherry这个类改个名字,比如Coconut
        registry.registerBeanDefinition("Coconut",rootBeanDefinition);
    }
}
@Configuration
@Import({Apple.class,BananaImportSelector.class,CherryDefinitionRegistrar.class})
public class Fruits {
}


正题


以上,也就是说,在这个地方,要先引入一些类,而且是通过第二种方式来进行的,我们看下这个类:AutoConfigurationImportSelector.class


@Override
  public String[] selectImports(AnnotationMetadata annotationMetadata) {
  //判断EnableAutoConfiguration是否开启,一般默认开启的
    if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  }
  protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  //这里又判断了一遍EnableAutoConfiguration是否开启
    if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
    }
       //这里可以理解为获取到了EnableAutoConfiguration的属性和我们传入的值。
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //这里可以理解为获取到了所有可以自动注入的类名
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    //给这些需要自动注入的类名去重,通过list-set-list的方式
    configurations = removeDuplicates(configurations);
    //把我们设置了排出的整个set返回回来
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    //验证一下要排除自动的类是否合法,不合法的话会直接抛异常
    checkExcludedClasses(configurations, exclusions);
    //这里就直接把我们想要排除的类从自动启动里都remove了
    configurations.removeAll(exclusions);
    //TODO:这块另写一篇 获取过滤器,过滤配置类
    configurations = getConfigurationClassFilter().filter(configurations);
    //TODO:这块另写一篇 获取所有的AutoConfigurationImportListener类型的监听器。然后广播AutoConfigurationImportEvent事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    //配置类名字结合和排除集合封装成AutoConfigurationEntry返回
    return new AutoConfigurationEntry(configurations, exclusions);
  }
  protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
  //拿到了EnableAutoConfiguration类的名字
    String name = getAnnotationClass().getName();
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
        + " annotated with " + ClassUtils.getShortName(name) + "?");
    return attributes;
  }
  protected final <T> List<T> removeDuplicates(List<T> list) {
    return new ArrayList<>(new LinkedHashSet<>(list));
  }
  private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
    List<String> invalidExcludes = new ArrayList<>(exclusions.size());
    for (String exclusion : exclusions) {
      if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
        invalidExcludes.add(exclusion);
      }
    }
    if (!invalidExcludes.isEmpty()) {
      handleInvalidExcludes(invalidExcludes);
    }
  }
    private ConfigurationClassFilter getConfigurationClassFilter() {
    if (this.configurationClassFilter == null) {
    //这里其实获取到了AutoConfigurationImportFilter
      List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
      for (AutoConfigurationImportFilter filter : filters) {
        invokeAwareMethods(filter);
      }
      this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
    }
    return this.configurationClassFilter;
  }
  private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
    //这块主要是获取到了AutoConfigurationImportFilter的三个实现类
      AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
      for (AutoConfigurationImportListener listener : listeners) {
        invokeAwareMethods(listener);
        listener.onAutoConfigurationImportEvent(event);
      }
    }
  }
  protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
  }
    private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
      AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
      for (AutoConfigurationImportListener listener : listeners) {
        invokeAwareMethods(listener);
        listener.onAutoConfigurationImportEvent(event);
      }
    }
  }
  protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
  }


参考


@Import注解的应用和扩展


SpringBoot之@EnableAutoConfiguration注解


SpringBootApplication-@Import(AutoConfigurationImportSelector.class)

目录
相关文章
|
24天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
28 0
|
28天前
|
Java Spring
SpringBoot自动装配的原理
在Spring Boot项目中,启动引导类通常使用`@SpringBootApplication`注解。该注解集成了`@SpringBootConfiguration`、`@ComponentScan`和`@EnableAutoConfiguration`三个注解,分别用于标记配置类、开启组件扫描和启用自动配置。
54 17
|
17天前
|
Java 容器
springboot自动配置原理
启动类@SpringbootApplication注解下,有三个关键注解 (1)@springbootConfiguration:表示启动类是一个自动配置类 (2)@CompontScan:扫描启动类所在包外的组件到容器中 (3)@EnableConfigutarion:最关键的一个注解,他拥有两个子注解,其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中,@Import会导入一个自动配置文件选择器,他会去加载META_INF目录下的spring.factories文件,这个文件中存放很大自动配置类的全类名,这些类会根据元注解的装配条件生效,生效
|
2月前
|
druid Java Maven
|
2月前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
82 4
|
3月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
Vertx高并发理论原理以及对比SpringBoot
|
2月前
|
Java Maven Spring
springboot学习一:idea社区版本创建springboot项目的三种方式(第三种为主)
这篇文章介绍了在IntelliJ IDEA社区版中创建Spring Boot项目的三种方法,特别强调了第三种方法的详细步骤。
647 0
springboot学习一:idea社区版本创建springboot项目的三种方式(第三种为主)
|
3月前
|
Java 开发者 数据格式
【Java笔记+踩坑】SpringBoot基础4——原理篇
bean的8种加载方式,自动配置原理、自定义starter开发、SpringBoot程序启动流程解析
【Java笔记+踩坑】SpringBoot基础4——原理篇
|
2月前
|
Java Maven Spring
查看springboot版本支持最高的java版本
截至最近更新,Spring Boot 3.0及以上版本支持的最高Java版本为Java 17。鉴于技术的不断演进,建议直接参考Spring Boot的官方文档获取最准确的支持信息,因为这些版本兼容性可能会随着新版本的发布而有所变化。选择与你的Spring Boot版本相匹配的Java版本,可以确保充分利用框架特性,同时保证项目的稳定性和前瞻性。
79 0
|
3月前
|
前端开发 Java Spring
【非降版本解决】高版本Spring boot Swagger 报错解决方案
【非降版本解决】高版本Spring boot Swagger 报错解决方案