Spring Boot - 自动装配中的不可忽视的@Import

简介: Spring Boot - 自动装配中的不可忽视的@Import

20210522080452432.png

Pre

Spring Boot - 自动配置实现原理


四种处理方式


在使用 Spring Boot 时,@Import 也是一个非常常见的注解,可以用来动态创建 Bean。


在 @Import 注解的属性中可以设置需要引入的类名,例如 @AutoConfigurationPackage 注解上的 @Import(AutoConfigurationPackages.Registrar.class)。根据该类的不同类型,Spring 容器针对 @Import 注解有以下四种处理方式:


如果该类实现了 ImportSelector 接口,Spring 容器就会实例化该类,并且调用其 selectImports 方法;

如果该类实现了 DeferredImportSelector 接口,则 Spring 容器也会实例化该类并调用其 selectImports方法。DeferredImportSelector 继承了 ImportSelector,区别在于 DeferredImportSelector 实例的 selectImports 方法调用时机晚于 ImportSelector 的实例,要等到 @Configuration 注解中相关的业务全部都处理完了才会调用;

如果该类实现了 ImportBeanDefinitionRegistrar 接口,Spring 容器就会实例化该类,并且调用其 registerBeanDefinitions 方法;

如果该类没有实现上述三种接口中的任何一个,Spring 容器就会直接实例化该类。

我们来搞一搞@Import吧


从@SpringBootApplication注解说起


我们都知道 启动一个SpringBoot应用 无须各种的配置文件,无须各种繁杂的pom依赖,一个main方法,就能run起来了。

与其他框架整合也相当方便,使用EnableXXXXX注解就可以完成整合

那SpringBoot是如何实现自动配置的????


@SpringBootApplication组合注解说明


@SpringBootApplication: Spring Boot应用标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot需要运行这个类的main方法来启动SpringBoot应用


20210522081317763.png


@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited

注解说明:


@Target(ElementType.TYPE) 设置当前注解可以标记在哪

@Retention(RetentionPolicy.RUNTIME) 当注解标注的类编译以什么方式保留。 RetentionPolicy.RUNTIME 会被jvm加载

@Documented java doc 会生成注解信息

@Inherited 是否会被继承

更详细的请参考我以前写的一篇博文: Java-Java5.0注解解读


@SpringBootConfiguration

Spring Boot的配置类 , 标注在某个类上,表示这是一个Spring Boot的配置类

@EnableAutoConfiguration

开启自动配置功能 , @EnableAutoConfiguration告诉SpringBoot开启自动配置,会自动去加载自动配置类


@ComponentScan

相当于在spring.xml 配置中<context:comonent-scan> 但是并没有指定basepackage,如果没有指定spring底层会自动扫描当前配置类所有在的包


@EnableAutoConfiguration

SpringBootApplication注解中最重要的一个注解就是 @EnableAutoConfiguration.


20210522082828804.png


@AutoConfigurationPackage

将当前配置类所在包保存在BasePackages的Bean中。供Spring内部使用

20210522083309216.png


使用了@Import注解 保存扫描路径, 注册

那看下 org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar


2021052208381868.png


@Import(AutoConfigurationImportSelector.class)

关键点!

可以看到,在@EnableAutoConfiguration注解内也使用到了@Import注解来完成导入配置的功能


那都用它,我们来搞搞@Import吧


@Import源码



20210522084050623.png


@Import表示要导入的一个或多个@Configuration

我们来看下value方法源码中的注释: Configuration,ImportSelector,ImportBeanDefinitionRegistrar 或者是一个普通的组件

那分别来看下如何使用吧

20210522101638434.png


POM 核心 有个context就够了

  <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
    </dependencies>


@Import 普通组件

@Import({ 要导入的组件 } )

【模拟第三方框架提供的】

package com.artisan.configuration;
import com.artisan.beans.Artisan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author 小工匠
 * @version 1.0
 * @description:  自定义的配置类  类比第三方的配置类
 * @date 2021/5/22 8:50
 * @mark: show me the code , change the world
 */
@Configuration
public class ArtisanConfig {
    @Bean
    public Artisan artisan() {
        Artisan artisan = new Artisan();
        artisan.setName("小工匠");
        artisan.setAge(18);
        return artisan;
    }
}


【模拟Spring自己的】

/**
 * 系统当前加载的配置类
 */
@Configuration
@Import({ArtisanConfig.class})
// @Import(ArtisanImportSelector.class)
//@Import(ArtisanRegistrar.class)
//@EnableArtisan
public class AppConfig {
}


将AppConfig.java添加注解 @Import({ArtisanConfig.class}) , 将第三方的配置类导入到Bean容器中 , 本质上就是导入 一个Configuration配置类组件

【测试类】

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        Artisan artisan =  ctx.getBean(Artisan.class);
        System.out.println(artisan);
    }
}


【测试结果】

20210522101911180.png


@Import 实现了ImportSelector接口的组件 (类的全限定类名)

【ImportSelector接口 返回全限定名】

package com.artisan.impt;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
/**
 * @author 小工匠
 * @version 1.0
 * @description: TODO
 * @date 2021/5/22 9:25
 * @mark: show me the code , change the world
 */
public class ArtisanImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.artisan.beans.Artisan"};
    }
}


【AppConfig - @Import(ArtisanImportSelector.class)】

/**
 * 系统当前加载的配置类
 */
@Configuration
//@Import({ArtisanConfig.class})
 @Import(ArtisanImportSelector.class)
//@Import(ArtisanRegistrar.class)
//@EnableArtisan
public class AppConfig {
}


测试

20210522102304357.png


ArtisanSelector返回的类的全限定类名,即为导入到容器中的组件全类名


@Import 实现了ImportBeanDefinitionRegistrar接口的组件

【ImportBeanDefinitionRegistrar 接口】

package com.artisan.impt;
import com.artisan.beans.Artisan;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
/**
 * @author 小工匠
 * @version 1.0
 * @description: TODO
 * @date 2021/5/22 9:28
 * @mark: show me the code , change the world
 */
public class ArtisanRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Artisan.class);
        builder.setScope(BeanDefinition.SCOPE_SINGLETON);
        builder.addPropertyValue("name", "小工匠Registrar");
        builder.addPropertyValue("age", "18");
        registry.registerBeanDefinition("artisan", builder.getBeanDefinition());
    }
}

【AppConfig - @Import(ArtisanRegistrar.class) 】

/**
 * 系统当前加载的配置类
 */
@Configuration
//@Import({ArtisanConfig.class})
// @Import(ArtisanImportSelector.class)
@Import(ArtisanRegistrar.class)
//@EnableArtisan
public class AppConfig {
}


【测试】

20210522102505211.png


ImportBeanDefinitionRegistrar类似于ImportSelector用法,只不过这种用法能自定义化注册,往容器内注入一个BeanDefinition,然后BeanDeiniton在容器内转为一个实例bean。


最佳实践 @EnableXXX

【自定义注解 @EnableArtisan】

package com.artisan.annotation;
import com.artisan.configuration.ArtisanConfig;
import com.artisan.impt.ArtisanImportSelector;
import com.artisan.impt.ArtisanRegistrar;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Import({ArtisanConfig.class})   @Import({ArtisanImportSelector.class})  都可以
@Import({ArtisanRegistrar.class})
public @interface EnableArtisan {
}


将AppConfig.java添加@EnableArtisan


2021052210263917.png

【测试结果】

20210522102655224.png


源码

https://github.com/yangshangwei/boot2/tree/master/spring_maven

相关文章
|
4天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
1月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
38 2
|
2月前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
6月前
|
XML druid Java
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
Spring5系列学习文章分享---第二篇(IOC的bean管理factory+Bean作用域与生命周期+自动装配+基于注解管理+外部属性管理之druid)
74 0
|
2月前
|
Java 微服务 Spring
springBoot:@Enable&@import&事件监听 (六)
本文介绍了如何在Spring Boot项目中实现跨项目获取Bean类的方法,包括创建两个项目、在pom文件中导入依赖、定义Bean和配置类、封装注解@EnableStudent以简化导入过程。同时,详细讲解了@Import的四种用法,并展示了如何通过实现ApplicationRunner、CommandLineRunner接口及配置spring.factories文件来执行程序启动时的监听逻辑。
|
2月前
|
Java 数据库连接 mybatis
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
该文档详细介绍了如何在Springboot Web项目中整合Mybatis,包括添加依赖、使用`@MapperScan`注解配置包扫描路径等步骤。若未使用`@MapperScan`,系统会自动扫描加了`@Mapper`注解的接口;若使用了`@MapperScan`,则按指定路径扫描。文档还深入分析了相关源码,解释了不同情况下的扫描逻辑与优先级,帮助理解Mybatis在Springboot项目中的自动配置机制。
177 0
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
|
4月前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
WXM
|
4月前
|
存储 缓存 Java
|
5月前
|
开发框架 Java 开发者
Spring Boot中的自动装配原理
Spring Boot中的自动装配原理
|
6月前
|
Java 应用服务中间件 Spring
解析Spring Boot自动装配的原理与机制
解析Spring Boot自动装配的原理与机制
130 4