SpringBoot自动装配


SpringBoot自动装配

Spring全称SpringFramework也就是Spring框架,是目前最流行的Java框架,它可以帮助我们更加快速、更加简单的构建Java项目,而Spring家族中提供了许多框架,家族中的所有框架都是基于核心框架SpringFramework的。

而现在通过Spring开发也会比较繁琐,主要体现在两个地方:

  1. 在pom.xml中依赖配置比较繁琐,在项目开发时,需要自己去找到对应的依赖,还需要找到依赖它所配套的依赖以及对应版本,否则就会出现版本冲突问题。
  2. 在使用Spring框架进行项目开发时,需要在Spring的配置文件中做大量的配置,这就造成Spring框架入门难度较大,学习成本较高。

因为这些问题,Spring官方就提出一个全新的框架SpringBoot来简化Spring开发,直接通过SpringBoot来构建项目会比Spring更加简单快速,因为SpingBoot不用手动声明Bean对象,添加pom.xml依赖。

SpringBoot框架之所以使用起来更简单更快捷,是因为SpringBoot框架底层提供了两个非常重要的功能:一个是起步依赖,一个是自动配置。

1. 起步依赖

假设我们使用传统的Spring进行开发,此时就需要手动引入web开发的一些依赖,还要注意版本的兼容问题

<!-- servlet依赖-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!-- Spring上下文 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.20</version>
</dependency>
<!-- Spring对象管理模块 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.3.20</version>
</dependency>
<!-- SpringMVC -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.17</version>
</dependency>
<!-- Mysql驱动依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

而如果我们使用了SpringBoot,就不需要像上面这么繁琐的引入依赖了。我们只需要引入一个依赖就可以了,那就是web开发的起步依赖:springboot-starter-web

<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

</dependencies>

为什么我们只需要引入一个web开发的起步依赖,web开发所需要的所有的依赖都有了呢?

就是因为Maven的依赖传递,将起步依赖所依赖的Jar包都导入了进来。比如说A依赖B,B依赖C,引入A之后C也会引入进来

2. 自动配置

SpingBoot自动配置就是当Spring容器启动后,一些配置类、bean对象就自动存入到IOC容器中,不需要我们手动去使用xml声明Bean对象。

在这里插入图片描述

这些对象在启动的时候就被存放到Spring容器中了,通过@Autowired就可以直接注入进来。

@Autowired
private ObjectMapper objectMapper;

自动配置常见方案

我们知道通过@Component修改的类会被存入Spring容器中,当需要注意的是Spring的扫描级别是类扫描,并且扫描的是被@SpringBootApplication修饰的启动类同一级目录或者子目录中的类型。当我们想要引入第三方依赖的时候,如果不在同一个目录就需要指定要扫描的类,有那么几种常见的方案:

  • 方案1:@ComponentScan 组件扫描
  • 方案2:@Import 导入(使用@Import导入的类会被Spring加载到IOC容器中)
@ComponentScan组件扫描

通过@ComponentScan注解来指定要扫描指定包下的类,需要注意的是如果使用该注解会覆盖原来的扫描路径,也就是说还要把当前启动类的路径也添加。

@SpringBootApplication
@ComponentScan({"com.test","com.example"}) //指定要扫描的包
public class DemoApplication {

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

}

通过这种方式来完成自动配置,如果在开发的时候需要引入大量的第三方依赖,就需要在启动类上配置非常多的需要扫描的包,就会比较麻烦,且大量的包扫描性能较低。

所以SpringBoot并没有采用这种方式。

@Import导入

导入形式主要有以下几种:

  1. 导入普通类
  2. 导入配置类
  3. 导入ImportSelector接口实现类
  • 使用@Import导入普通类

    @SpringBootApplication
    @Import(AppConfig.class) //导入的类会被Spring加载到IOC容器中
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HmDianPingApplication.class, args);
        }
    
    }
    
  • 使用@Import导入配置类

    配置类:

    @Configuration
    public class HeaderConfig {
        @Bean
        public HeaderParser headerParser(){
            return new HeaderParser();
        }
    
        @Bean
        public HeaderGenerator headerGenerator(){
            return new HeaderGenerator();
        }
    }
    

    启动类

    @SpringBootApplication
    @Import(HeaderConfig.class) //导入配置类
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HmDianPingApplication.class, args);
        }
    
    }
    
  • 使用@Import导入ImportSelector接口实现类:

    ImportSelector接口实现类,重写了ImportSelector的selectImports,该方法返回的String数组里的元素就是要导入到Spring容器中的类的全路径。

    public class MyImportSelector implements ImportSelector {
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            //返回值字符串数组(数组中封装了全限定名称的类)
            return new String[]{"com.example.HeaderConfig"};
        }
    }
    

    启动类:

    @SpringBootApplication
    @Import(MyImportSelector.class) //导入ImportSelector接口实现类
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HmDianPingApplication.class, args);
        }
    
    }
    

虽然使用@Import注解通过这三种方式都可以导入第三方依赖中所提供的bean或者是配置类。

如果使用这种方式完成自动配置吗,当要引入一个第三方依赖的时候,那就还要知道第三方依赖中有哪些配置类和Bean对象,这些方法其实并不友好。

而依赖中有哪些配置类和bean第三方依赖是最清楚的,所以我们不用自己指定要导入哪些bean对象和配置类了,让第三方依赖它自己来指定

第三方依赖提供注解

比较常见的方案就是第三方依赖给我们提供一个注解,这个注解一般都以@EnableXxxx开头的注解,注解中封装的就是@Import注解。

第三方依赖中提供的注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)//指定要导入哪些bean对象或配置类
public @interface EnableHeaderConfig { 
}

在使用的时候只需要在启动类上加上第三方依赖提供的@EnableXxxxx注解即可

@EnableHeaderConfig  //使用第三方依赖提供的Enable开头的注解
@SpringBootApplication
public class SpringbootWebConfig2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWebConfig2Application.class, args);
    }
}

以上四种方式都可以完成导入操作,但是第4种方式会更方便更优雅,而这种方式也是SpringBoot当中所采用的方式。

@SpringBootApplication

@SpringBootApplication是SpringBoot启动类上的核心注解,源码如下:

前四个注解是Java中提供的元注解:

  • @Target():指定注解能使用位置,这里表示在类、接口、枚举上
  • @Retention:指定注解的声明周期,这里表示可以在运行的时候通过反射访问到
  • @Documented:注解表明该自定义注解应该被包含在由Javadoc工具生成的文档中
  • @Inherited:该注解表示当前类的子类能获取到其注解
@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}
)}
)

@SpringBootConfiguration注解

该注解的源码如下,关键信息就是@Configuration,说明当前类也是一个配置类,也就是SpringBoot的启动类也是一个配置类。

@Indexed注解是用来加速应用启动的

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@ComponentScan注解

前面已经提到这个注解是用来扫描指定包下的类到SpringIOC容器中

@EnableAutoConfiguration注解

该注解也是自动配置的核心注解,这个注解底层使用的是@Import注解,导入了AutoConfigurationImportSelector这个类

在这里插入图片描述

AutoConfigurationImportSelector这个类实现了DeferredImportSelector这个接口,而DeferredImportSelector这个接口拓展的就是ImportSelector接口

在这里插入图片描述

ImportSelector接口中的selectImports方法返回的**String[]**数组里面存放的就是需要导入SpringIOC容器中的类的全路径名。

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata importingClassMetadata);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}

AutoConfigurationImportSelector这个类中就重写了selectImports方法,源码如下:

在这里插入图片描述

selectImports()方法底层调用getAutoConfigurationEntry()方法,获取可自动配置的配置类信息集合

在这里插入图片描述

getAutoConfigurationEntry()``方法通过调用getCandidateConfigurations(annotationMetadata, attributes)方法获取在配置文件中配置的所有自动配置类的集合,源码如下:

在这里插入图片描述

getCandidateConfigurations方法的功能:

获取META-INF/spring.factories以及

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

文件中配置类的集合

这两个文件位于org.springframework.boot.autoconfigure包下

在这里插入图片描述

那么这些配置文件这么多类都会导入到Spring的IOC容器中吗?

并不是。 在声明bean对象时,上面有加一个以@Conditional开头的注解,这种注解的作用就是按照条件进行装配,只有满足条件之后,才会将bean注册到Spring的IOC容器中

需要注意的是在SpringBoot在2.7以后,新增的org.springframework.boot.autoconfigure.AutoConfiguration.imports这个配置文件,且在SpringBoot3之后只使用该文件

@Conditional

我们在跟踪SpringBoot自动配置的源码的时候,在自动配置类声明bean的时候,除了在方法上加了一个@Bean注解以外,还会经常用到一个注解,就是以Conditional开头的这一类的注解。以Conditional开头的这些注解都是条件装配的注解。下面我们就来介绍下条件装配注解。

@Conditional注解:

  • 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。
  • 位置:方法、类
  • @Conditional本身是一个父注解,派生出大量的子注解:
    • @ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。
    • @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。
    • @ConditionalOnProperty:判断Spring的配置文件中有对应属性和值,才注册bean到IOC容器。

所以@EnableAutoConfiguration这个注解才是自动配置的核心

  • 它封装了一个@Import注解,Import注解里面指定了一个ImportSelector接口的实现类。
  • 在这个实现类中,重写了ImportSelector接口中的selectImports()方法。
  • 而selectImports()方法中会去读取两份配置文件,并将配置文件中定义的配置类做为selectImports()方法的返回值返回,返回值代表的就是需要将哪些类交给Spring的IOC容器进行管理。
  • 并且这两份配置文件的类并不一定会加载到IOC容器中,通过@Conditional开头的注解来让SpringBoot进行条件装配。

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的三毛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值