前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试 – 如何编写一个SpringBoot-Starter?

面试 – 如何编写一个SpringBoot-Starter?

作者头像
收心
修改2023-01-19 09:42:00
3220
修改2023-01-19 09:42:00
举报
文章被收录于专栏:Java实战博客

刚毕业第一批面试的时候,被问过如何手写MVC框架,但是感觉面试官在扯淡,我刚毕业的CRUD,你非要写尼玛MVC框架?面试第二家公司的时候遇到:如何手写SpringBoot-Starter?我感觉一样扯淡,我有必要写Boot-Starter么?但为了丰富技术点,当天晚上就研究一下,如何手写Boot-Starter?

Gitee项目地址:https://gitee.com/li_kun_zang/Spring-Boot-Starter 强烈推荐查看本文章讲解的内容,欢迎指错!

SpringBoot-Starter原理

说手写SpringBoot Starter 本质就是了解SpringBoot是如何启动的,以及Bean是如何自动配置的!

深入分析

SpringBoot 启动靠的是一个注解@SpringBootApplication,我们可以查看其源码:发现还有3个注解

代码语言:javascript
复制
@SpringBootConfiguration // 触发自动配置和组件扫描
@EnableAutoConfiguration // 启用Spring Application Context的自动配置,尝试猜测和配置您可能需要的bean。
@ComponentScan // 配置用于@Configuration类的组件扫描指令。

我们来分析一下上面3个注解:

  • @EnableAutoConfiguration 启用SpringBoot 的自动配置机制
  • @SpringBootConfiguration = @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类
  • @ComponentScan:扫描被@Component 注解的 bean(如:@Service,@Controller),注解默认会扫描启动类所在的包路径下所有的类 ,可以自定义不扫描某些 bean。

@EnableAutoConfiguration 是实现自动装配的重要注解,其实现了:@Import(AutoConfigurationImportSelector.class)

什么意思的,猜猜看,看名字:自动、配置、导入、选择器,那就是自动配置的,我们继续点AutoConfigurationImportSelector进去看实现,

代码语言:javascript
复制
	/**
	 * 返回应该考虑的自动配置类名。Return the auto-configuration class names that should be considered. By default
	 * this method will load candidates using {@link SpringFactoriesLoader} with
	 * {@link #getSpringFactoriesLoaderFactoryClass()}.
	 * @param metadata the source metadata
	 * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
	 * attributes}
	 * @return a list of candidate configurations
	 */
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}

啥都不看,就看提示:没有自动配置字节码被发现与:META-INF/spring.factories文件夹下。如果你使用了自定义包,确保META-INF/spring.factories文件是正确的。

到此,本文讲述了我们手写自己的Starter必须创建自己的spring.factories。以实现Bean的加载。

1、先了解 @EnableConfigurationProperties是干嘛的:使用 @ConfigurationProperties 注解的类生效。

@ConfigurationProperties又是干嘛的:将其配置文件的内容注入到对象中

我们直接上案例

代码语言:javascript
复制
# 一个配置文件
user.zhangsan.name=zhangsan
user.zhangsan.age=10
user.zhangsan.hobbits=??,??

一个实体

代码语言:javascript
复制
@Data
@ConfigurationProperties("user.zhangsan") // 读取配置文件的内容
public class User {
    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

    /**
     * 爱好
     */
    private String[] hobbits;
}

添加一个配置类

代码语言:javascript
复制
@Configuration
@EnableConfigurationProperties({User.class})
public class UserConfigration {
}

看起来没啥问题吧,但是想过一个问题么,User对象是IOC容器管理的对象么?很显然不是。我们要使用User对象,就必须将其弄到IOC容器中。我们要不在User类上添加@Component或@Configuration(当然用@Controller、@Service、@Mapper就不合适了)。要么就使用@EnableConfigurationProperties({类.class}) 实现加载到IOC容器中。

到这里跟我们手写Starter关系不大,我们更加去关注如何实现自动装配的。

接下来我们就需要去了解@Import实现了3中实现Bean注册的方式

方式一:直接@Import({User.class})

代码语言:javascript
复制
@Import({User.class}) // 方式一:注册Bean

方式二:实现ImportSelector接口,同时需要在配置类中,@Import({DefaultImportSelector.class})

代码语言:javascript
复制
/**
 * @Description :注入方式二:实现ImportSelector接口重写selectImports方法
 */
public class DefaultImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //return new String[0];
        return new String[]{"com.zanglikun.entity.User"}; // 指定IOC容器加载此Bean
    }
}

方式三:实现ImportBeanDefinitionRegistrar方法。自定义配置同时需要在配置类中,@Import({DefaultImportBeanDefinitionRegistrar.class})

代码语言:javascript
复制
public class DefaultImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(User.class); // 配置Bean
        registry.registerBeanDefinition("userInstance",rootBeanDefinition); // 注册Bean
    }
}

如果你想你的starter被依赖项目的配置文件自动提示,请参考:https://cloud.tencent.com/developer/article/2162433

想了解spring.factories的作用么?

他的作用就是在IOC容器加载过程中,扫描项目依赖所有jar中META-INF文件夹下的spring.factories。然后初始化Bean。

文件内容长这样

代码语言:javascript
复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zanglikun.config.UserConfigration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zanglikun.config.XXX
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.zanglikun.config.XXXX

注意,统一前缀是org.springframework.boot.autoconfigure.EnableAutoConfiguration,后面加上你的要加载的全限定类名。这句话的作用就是 相当于在类上,加入@Configuration。

有啥用?我们每个单体SpringBoot项目都是有包扫描的吧?这spring.factories玩意就是给包扫描用的。

更具体一点,我们项目是com.alibaba。但是我们引入一个starter,他是org.xxx开头的。我们包扫描只会扫描启动类包以下的Bean对象。很明显,我们引入的starter不在扫描的范围,你@Auworied Starter的对象能成功嘛?肯定不行。如果我们配置了spring.factories。他就会扫描jar所有的此文件,然后再去加载Bean对象!这就是SpringBoot不用我们额外添加@ComponentScan的原因!

原则上,我们上文分析了所有手写BootStarter的内容了。我们闭眼想一下。

我们首先弄一个SpringBoot的依赖。

创建一个配置类(创建多个也无所谓)。

ps:如果你想弄一些复杂的Bean初始化,通过自己的配置类@Import这些配置。

resources文件夹下创建一个META-INF文件夹再创建一个spring.factories文件,指向我们的配置类。

打成jar包

另一个项目去依赖它。然后启动测试!

说白了,本篇文章只是简单说明了从应用的角度讲述了手写SpringBoot Starter。深度原理SPI压根没设计。等待我翻源码的时候,在补充,丰富本文章。

项目地址:https://gitee.com/li_kun_zang/Spring-Boot-Starter

特殊说明: 以上文章,均是我实际操作,写出来的笔记资料,不会盗用别人文章!烦请各位,请勿直接盗用!转载记得标注来源!

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SpringBoot-Starter原理
    • 深入分析
      • 我们来分析一下上面3个注解:
    • 接下来我们就需要去了解@Import实现了3中实现Bean注册的方式
      • 想了解spring.factories的作用么?
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档