Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot自动装配原理

SpringBoot自动装配原理

作者头像
西柚dzh
发布于 2022-06-09 09:33:56
发布于 2022-06-09 09:33:56
36000
代码可运行
举报
文章被收录于专栏:dcmickey小站dcmickey小站
运行总次数:0
代码可运行

SpringBoot自动装配原理

SpringBoot的主配置

这里先看自动装配,extends SpringBootServletInitializer这块下一步再探讨

1、主入口

@SpringBootApplication注解标注的类为SpringBoot的主配置类,SpringBoot就会运行这个类的main方法来启动SpringBoot项目 main方法运行了一个run()方法,run方法传入自己

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SpringBootApplication
public class StarrySpringBootApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(StarrySpringBootApplication.class, args);
    }

    @Override//为了打包springboot项目
    protected SpringApplicationBuilder configure(
            SpringApplicationBuilder builder) {
        return builder.sources(this.getClass());
    }
}

spring的run方法源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified source using default settings.
     * @param primarySource the primary source to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }

2、@SpringBootApplication启动注解

是一个组合注解,核心是三个Annotation组成

  • @SpringBootConfiguration(本质是一个@Configuration)
  • @EnableAutoConfiguration
  • @ComponentScan

SpringBootApplication源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Indicates a {@link Configuration configuration} class that declares one or more
 * {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
 * auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
 * annotation that is equivalent to declaring {@code @Configuration},
 * {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
 */
@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 {

    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;

}

3、先看看@SpringBootConfiguration注解

这个注解实际上就是代表了一个配置类,表名该类是一个Spring的配置类,相当于一个beans.xml文件 具体可查看网上的文献:基于注解开发初探 https://www.jianshu.com/p/81880251a700

SpringBootConfiguration源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(annotation = Configuration.class)
    boolean proxyBeanMethods() default true;

}

4、@ComponentScan

@ComponentScan的功能其实就是自动扫描并加载符合条件的组件或bean定义,最终将这些bean定义加载到容器

用法 // 1、只包含有Controller注解bean @ComponentScan(value="com.qiu",includeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),} ,useDefaultFilters = false) // 2、排除含有controller注解的bean @ComponentScan(value="com.qiu",excludeFilters = { @Filter(type=FilterType.ANNOTATION,classes={Controller.class}) })

ComponentScan源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//可以重复使用
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
        //使用包名
        @AliasFor("basePackages")
        String[] value() default {};
        @AliasFor("value")
        String[] basePackages() default {};
        //使用具体类型名称
        Class<?>[] basePackageClasses() default {};
        ....其他属性
        //指定扫描的时候只需要包含哪些组件
        Filter[] includeFilters() default {};
        //指定扫描的时候按照什么规则排除那些组件
        Filter[] excludeFilters() default {};
        boolean lazyInit() default false;
        //过滤规则类
        @Retention(RetentionPolicy.RUNTIME)
        @Target({})
        @interface Filter {
        //FilterType定义按什么过滤类型来进行过滤,
        /**
        FilterType.ANNOTATION:按照注解
        FilterType.ASSIGNABLE_TYPE:按照给定的类型;
        FilterType.ASPECTJ:使用ASPECTJ表达式
        FilterType.REGEX:使用正则指定
        FilterType.CUSTOM:使用自定义规则
        */
        FilterType type() default FilterType.ANNOTATION;
        @AliasFor("classes")
        Class<?>[] value() default {};
        @AliasFor("value")
        Class<?>[] classes() default {};
        String[] pattern() default {};
    }
}

5、@EnableAutoConfiguration

在spring中有关于@Enablexxx的注解是开启某一项功能的注解。其原理是借助@Import的帮助,将所有符合自动配置条件的bean定义加载到Ioc容器

@EnableAsync 开启异步 @EnableScheduling 表示开启spring的定时任务

@EnableAutoConfiguration==代表开启springboot的自动装配==

EnableAutoConfiguration源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    // 按类型排序不需要自动装配的类
    Class<?>[] exclude() default {};
    // 按名称排除不需要自动装配的类
    String[] excludeName() default {};
}

@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)

先来看看@AutoConfigurationPackage

这个注解是自动配置包,主要是使用的@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class

AutoConfigurationPackage源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
   String[] basePackages() default {};
   Class<?>[] basePackageClasses() default {};
}

再来看Registrar类,这是一个内部静态类

==就是通过这个方法获取扫描的包路径==,这里就需要我们做一个小小的debug来验证下,请接着往下看

首先是Registrar类源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
 * 存储注册导入过来的基本包
 * configuration.
 */
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }

   @Override
   public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(new PackageImports(metadata));
   }

}

在第10行打个断点,开始debug

如果不能调试 File->Settings-> Build, Execution, Deployment ->Debugger->Stepping 把Do not step into the classes下的java.*,javac.*取消勾选,然后保存即可

从下图可以看出metadata就是我们的springboot的主启动类,getPackageNames获取到这个类的包名,

==说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以包名一定要注意==

接着我们看@Import(AutoConfigurationImportSelector.class)

@Import注解就是给Spring容器中导入一些组件,这里传入了一个组件的选择器:AutoConfigurationImportSelector

AutoConfigurationImportSelector源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
            
    //  这个地方才可以断点,上面的selectImports设置断点是没用的
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationEntry(configurations, exclusions);
    }       
...
}

再次debug看看:

断点需要打在==getAutoConfigurationEntry方法上==,selectImports方法断点不会生效

会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件:

有了自动配置类,免去了我们手动编写配置注入功能组件等的工作。

那他是如何获取到这些配置类的呢,看看上面这个方法:

会从META-INF/spring.factories中获取资源,然后通过Properties加载资源:

看看

getSpringFactoriesLoaderFactoryClass()是啥

getBeanClassLoader()是啥

Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作。以前我们需要自己配置的东西,自动配置类都帮我们完成了

引申注解认识

@Scope设置组件的作用域

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 1、原型案例
@Scope("prototype")
@Bean()
public User user() {
    return new User();
}
// 2、单例 默认就是单例
@Scope("singleton")

@Lazy bean组件懒加载

@Lazy注解用于标识bean是否需要延迟加载,默认是true。 没加注解主要容器启动就会实例化bean 加上@Lazy注解则第一次调用的时候才会加载

@Conditional按照条件注册Bean

Conditional源码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//作用于方法和类
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
//参数是:继承于Condition的类数组
    Class<? extends Condition>[] value();
}
//condition接口,自定义的condition类需要实现该接口
public interface Condition {
/**
     * ConditionContext:判断条件能使用的上下文(环境)
     * AnnotatedTypeMetadata:注释信息
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

使用案例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1、判断是否是linux系统
public class LinuxCondition implements Condition{
/**
 * ConditionContext:判断条件能使用的上下文(环境)
 * AnnotatedTypeMetadata:注释信息
 */
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) 
{
    //1.能获取到ioc使用的Beanfactory
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    //2、获取到类加载器
    ClassLoader classLoader = context.getClassLoader();
    //3.获取到当前环境信息
    Environment environment = context.getEnvironment();
    //4.获取到bean定义的注册类信息
    BeanDefinitionRegistry registry = context.getRegistry();
    //=============这里主要是为了获取当前系统的环境变脸
    String property=environment.getProperty("os.name");
    if (property.contains("linux")) {
        return true;//放行
    }
    return false;
}

}

2、判断是否是windows系统
public class WindowsCondition implements Condition{
/**
 * ConditionContext:判断条件能使用的上下文(环境)
 * AnnotatedTypeMetadata:注释信息
 */
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
{
    Environment environment = context.getEnvironment();
    String property=environment.getProperty("os.name");
    if (property.contains("Windows")) {
        return true;//放行
    }
    return false;
}
}
3、@Conditional:按照一定的逻辑进行判断,满足条件给容器注入bean
public class ConditionalConfig {
//注入windows
@Conditional(value= {WindowsCondition.class})
@Bean
public User user1() {
    User user=new User();
    user.setUserName("bill");
    return user;
}
//注入linux
@Conditional(value= {LinuxCondition.class})
@Bean
public User user2() {
    User user=new User();
    user.setUserName("linus");
    return user;
}
4、idea中更换操作系统方法:-Dos.name=linux

介绍完了条件注解在spring中的使用,在Springboot中条件注解的分类:

Class conditions:@ConditionalOnClass和@ConditionalOnMissingClass,表示类是否在类路径下的条件注解 Bean conditions:@ConditionalOnBean和@ConditionalOnMissingBean,表示Bean是否被定义的条件注解 Property conditions:@ConditionalOnProperty,使用prefix和name属性用来表示是否有值,默认的话,只要该属性存在值,且不为false,即可匹配 Resource conditions:@ConditionalOnResource表示是否存在指定的resouce的条件注解 Web application conditions:@ConditionalOnWebApplication和@ConditionalOnNotWebApplication,当项目是web项目,或者不是web项目的条件注解 SpEL expression conditions:@ConditionalOnExpression,根据SPEL表达式执行结果作为条件


版权属于:dingzhenhua

本文链接:https://cloud.tencent.com/developer/article/2019361

转载时须注明出处及本声明

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SpringBoot自动装配的原理
自动装配是SpringBoot的核心,从Spring的起源来说,一开始是通过XML文件对Bean进行装载,后来使用JavaConfig的方式实现无配置化的解决方案,但是本质问题并没有得到解决,直到SpringBoot的问世,帮开发者省略了繁琐的基础性的工作,是开发者更加关注于逻辑本身,SpringBoot将繁琐的基础性工作交给了Starter组件和自动装配去做。
NorthS
2023/03/21
7180
SpringBoot自动装配的原理
SpringBoot自动装配原理笔记
SpringBoot通过自动装配实现了第三方框架系统对象的注入。这种实现机制和我们前面介绍的SPI(服务扩展机制)很相似。
Java鱼头
2022/11/29
8311
springboot项目启动原理_spring原理和实现机制
SpringBoot的启动类上使用@SpringBootApplication注解标识,该注解试一个组合注解,包含多个其他注解。
全栈程序员站长
2022/10/03
3350
springboot项目启动原理_spring原理和实现机制
Spring Boot - 自动配置实现原理
Spring Boot 中的配置体系是一套强大而复杂的体系,其中最基础、最核心的要数自动配置(AutoConfiguration)机制了。
小小工匠
2021/08/17
4340
Spring Boot - 自动配置实现原理
程序员必备技能之SpringBoot的自动装配原理,很详细,建议收藏!!!
  SpringBoot应该是每个Java程序猿都会使用的基础框架了,对于SpringBoot的核心内容自动装配原理的掌握就显得非常重要了。
用户4919348
2021/07/29
5800
程序员必备技能之SpringBoot的自动装配原理,很详细,建议收藏!!!
一文吃透@SpringbootApplication的前世与今生
SpringBootApplication是标注在启动类上的复合注解,是springboot启动加载IOC容器的核心实现,也是springboot能够实现自动装配的关键逻辑,无论是面试还是自我提升都是必不可少的知识点。
柏炎
2022/08/23
3670
springboot自动装配原理
使用springboot的过程中我们发现,框架层帮我们自动注册了很多能力,类似的基础配置、集成能力支撑等,我们之前有分析过springboot应用启动的时候自动扫描注册的bean要么是启动门面类路径及子路径下的信息,要么就是用户指定的路径信息,springboot所支持的部件和框架层配置肯定不在我们指定的路径下,按照这个思路应用启动时并不会把相应的配置实例化到上下文中,而我们编写的所有业务代码甚至扩展框架信息都是基于框架能力的支撑,没有这些配置和基础组件是不可能实现的,我们本篇就围绕springboot自动装配展开分析。
叔牙
2022/01/20
1.3K0
springboot自动装配原理
用了那么久的springboot,你知道它的自动装配原理吗?
在使用springboot时,很多配置我们都没有做,都是springboot在帮我们完成,这很大一部分归功于springboot自动装配,那springboot的自动装配的原理是怎么实现的呢?
索码理
2022/09/20
4260
用了那么久的springboot,你知道它的自动装配原理吗?
SpringBoot 注解原理,自动装配原理,图文并茂,万字长文!
点进@SpringBootApplication来看,发现@SpringBootApplication是一个组合注解。
搜云库技术团队
2020/06/17
1.8K0
SpringBoot 注解原理,自动装配原理,图文并茂,万字长文!
第05天 SpringBoot自动配置原理
自从有了 SpringBoot 之后,咋们就起飞了!各种零配置开箱即用,而我们之所以开发起来能够这么爽,自动配置的功劳少不了,今天我们就一起来讨论一下 SpringBoot 自动配置原理。
程序员Leo
2023/08/16
4980
第05天 SpringBoot自动配置原理
SpringBoot 自动装配原理,看不懂不存在的
🍁 作者:知识浅谈,CSDN签约讲师,CSDN原力作者,后端领域优质创作者,热爱分享,热爱创作 📌 擅长领域:后端全栈工程师、爬虫、ACM算法 面试官常问的问题: 什么是 SpringBoot 自动装配? SpringBoot 是如何实现自动装配的?如何实现按需加载? 🤞这次都给他拿下🤞 为什么 Spring Boot 使用起来这么酸爽呢? 这得益于其自动装配。 正菜来了🛴🛴🛴 🍖什么是 SpringBoot 自动装配? 我们现在提到自动装配的时候,一般会和 Spring Boot 联系在一起。但是,
知识浅谈
2022/07/12
8651
SpringBoot 自动装配原理,看不懂不存在的
SpringBoot 自动配置
看@SpringBootApplication内部源码进行分析 ,核心代码具体如下
ruochen
2021/11/25
1.4K0
SpringBoot自动装配原理解析
自动装配是Spring Boot的核心部分,也是Spring Boot功能的基础,正是由于自动装配,才将我们从Bean的繁复配置中解脱出来。那么Spring Boot中的自动装配指的是什么?我们继 续以Spring MVC为例,不使用Spring Boot 时,我们可能需要配置视图解析器,文件解析器, 请求适配器等等各种 Bean, 如果在使用数据库Redis,还需要配置数据库Redis相关Bean。
后场技术
2020/09/03
6190
SpringBoot自动装配原理解析
手撸一个自定义starter,它不香吗
springboot 中有很多的starter 。我们用起来很爽不是吗,之所以这么爽难道不是因为我们少了很多配置吗,之前我们想要使用jdbcTemplete 是不是需要在xml 文件中配置bean 后才能使用。有被xml 一堆配置支配的恐惧吗?
程序员爱酸奶
2020/05/22
1.1K0
Spring Boot是如何实现自动配置的
Spring Boot 是 Spring 旗下众多的子项目之一,其理念是约定优于配置,它通过实现了自动配置(大多数用户平时习惯设置的配置作为默认配置)的功能来为用户快速构建出标准化的应用。Spring Boot 的特点可以概述为如下几点:
Bug开发工程师
2018/07/23
1.1K0
Spring Boot是如何实现自动配置的
SpringBoot自动装配探索
SpringBoot自动装配探索 首先看SpringBoot的启动类 @SpringBootApplication public class Application { public static void main(String[] args) { // Customize the spring config location System.setProperty("spring.config.additional-location", "
晓果冻
2022/09/08
3320
SpringBoot自动装配探索
神秘又强大的@SpringBootApplication注解
大部分的配置都可以用Java类+注解来代替,而在SpringBoot项目中见的最多的莫过于@SpringBootApplication注解了,它在每个SpringBoot的启动类上都有标注。
2020labs小助手
2021/04/14
9040
SpringBoot自动装配原理解析
我们知道,在使用SpringBoot的时候,我们只需要如下方式即可直接启动一个Web程序:
Java学习录
2019/09/23
1.4K0
SpringBoot自动装配原理解析
源码学习系列之SpringBoot自动配置(篇一)
ok,本博客尝试跟一下Springboot的自动配置源码,做一下笔记记录,自动配置是Springboot的一个很关键的特性,也容易被忽略的属性,因为这个属性被包括在@SpringBootApplication注解里,所以不去跟一下源码都不知道还有这个属性,ps:本博客源码基于SpringBoot1.5.7版本
SmileNicky
2019/11/04
4770
【原创】Spring Boot终极篇《上》
目前Spring Boot有多火,相信大家都能感受到,SpringBoot仿佛现在成为一个java开发中必备的技能之一。另外一方面,其实真正只有使用过的人才知道Spring Boot的爽快,那是一种享受。但是想做一个合格的、有优秀的java开发者,Spring Boot其背后的相关原理也是不得不掌握的。所以这一篇中我们来说Spring Boot的配置。
田维常
2020/09/22
4810
【原创】Spring Boot终极篇《上》
相关推荐
SpringBoot自动装配的原理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验