如何实现自定义SpringBoot的Starter组件
作者:令狐前生 发布时间:2023-06-28 14:49:04
一、前言
想要自定义starter组件,首先要了解springboot是如何加载starter的,也就是springboot的自动装配机制原理。
1.1、starter加载原理
springboot通过一个@SpringBootApplication注解启动项目,springboot在项目启动的时候,会将项目中所有声明为Bean对象(注解、xml)的实例信息全部加载到ioc容器当中。 除此之外也会将所有依赖到的starter里的bean信息加载到ioc容器中,从而做到所谓的零配置,开箱即用。
1.1.1、加载starter
首先通过通过注解@SpringBootApplication找到@EnableAutoConfiguration注解进行加载starter。
再通过注解@EnableAutoConfiguration * 解@import找到AutoConfigurationImportSelector类加载器实现。
这个AutoConfigurationImportSelector类会去其引用的依赖jar包下,找到一个”spring.factories”文件,一般spring.factories文件里都会声明该依赖所提供的核心功能bean配置信息。文件一般在依赖jar包的META-INF文件夹下面。
以spring-boot版本2.7.7为例,加载spring.factories的代码在:
AutoConfigurationImportSelector.java->selectImports(AnnotationMetadata annotationMetadata)->getAutoConfigurationEntry(annotationMetadata)->getCandidateConfigurations(annotationMetadata, attributes)->SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())->loadSpringFactories(classLoaderToUse):
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
举例如:spring-boot-autoconfig的spring.factories.
二、自定义starter
上面了解了springboot加载starter原理,其实就是加载依赖jar包下的spring.factories文件。所以我们要自定义starter,就需要在项目中建立一个META-INF的文件夹,然后在该文件夹下面建一个spring.factories文件,文件里将你需要提供出去的bean实例信息配置好就行。
2.1、代码
2.1.1、新建springboot项目。
简单演示所以需求配置任务依赖。如springboot构建很慢,或者打包的时候下载依赖很慢,可在pom文件中添加如下仓库配置,可以加快构建速度。
<repositories>
<repository>
<id>alimaven</id>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<url>https://maven.aliyun.com/repository/public</url>
</pluginRepository>
</pluginRepositories>
注意:spring官方规定自定义组件的命名:
SpringBoot官方命名方式
格式:spring-boot-starter-{模块名}
举例:spring-boot-starter-web
自定义命名方式
格式:{模块名}-spring-boot-starter
举例:mystarter-spring-boot-starter
2.1.2、项目构建完成后,在resources文件夹下面新建META-INF文件夹,并新建spring.factories文件。
2.1.3、因为我们是作为插件来使用,所以我们不需要启动类,删除启动类。并新建几个类:
一个接口AnimalService:
package com.example.demospringbootstarter.service;
/**
* @Project: demo-spring-boot-starter
* @Description:
* @Author: chengjiangbo
* @Date: 2023/2/7 15:12
*/
public interface AnimalService {
String say();
}
两个接口实现类CatService和DogService:
package com.example.demospringbootstarter.service;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @Project: demo-spring-boot-starter
* @Description:
* @Author: chengjiangbo
* @Date: 2023/2/7 14:49
*/
@Service
public class CatService implements AnimalService{
public static String name = "cat";
@Override
public String say() {
return "喵喵";
}
}
package com.example.demospringbootstarter.service;
import org.springframework.stereotype.Service;
/**
* @Project: demo-spring-boot-starter
* @Description:
* @Author: chengjiangbo
* @Date: 2023/2/7 14:49
*/
@Service
public class DogService implements AnimalService{
public static String name = "dog";
@Override
public String say() {
return "汪汪";
}
}
再建一个配置AnimalProperties类,方便注入属性值:
package com.example.demospringbootstarter.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Project: demo-spring-boot-starter
* @Description:
* @Author: chengjiangbo
* @Date: 2023/2/7 15:37
*/
@Data
@ConfigurationProperties(prefix = "animal")
public class AnimalProperties {
private String name;
}
最后新建一个核心自动装备配置类:
package com.example.demospringbootstarter.config;
import com.example.demospringbootstarter.service.AnimalService;
import com.example.demospringbootstarter.service.CatService;
import com.example.demospringbootstarter.service.DogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Project: demo-spring-boot-starter
* @Description:
* @Author: chengjiangbo
* @Date: 2023/2/7 14:48
*/
@Configuration
@EnableConfigurationProperties(AnimalProperties.class)
public class AnimalAutoConfig {
@Autowired
private AnimalProperties animalProperties;
@Bean
public AnimalService demoService(){
switch (animalProperties.getName()){
case "cat":
return new CatService();
case "dog":
return new DogService();
default:
return null;
}
}
}
META-INF/spring.factories的内容为:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demospringbootstarter.config.AnimalAutoConfig
以上步骤都好后,使用maven命令打包:
mvn clean install -Dmaven.test.skip=true
或者使用idea的LIfecycle点击对应操作(注意不是plugin下的命令操作)。
pom.xml内容为:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.4-SNAPSHOT</version>
<name>demo-spring-boot-starter</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<repositories>
<repository>
<id>alimaven</id>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>alimaven</id>
<url>https://maven.aliyun.com/repository/public</url>
</pluginRepository>
</pluginRepositories>
</project>
三、组件集成依赖测试
3.1、新启另一个项目中,引入刚刚打包的pom依赖
<dependency>
<groupId>com.example</groupId>
<artifactId>demo-spring-boot-starter</artifactId>
<version>0.0.4-SNAPSHOT</version>
</dependency>
3.2、新建一个controller,里面注入上面提供的AnimalService类并调用其方法
package com.cjb.mavendemo.controllers;
import com.example.demospringbootstarter.service.AnimalService;
import com.example.inputoutputlogspringbootstarter.config.PrintResponseTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Project: maven-demo
* @Description:
* @Author: chengjiangbo
* @Date: 2023/2/7 10:26
*/
@RestController
@RequestMapping(value = "/test")
public class TestController {
@Autowired
private AnimalService animalService;
@PrintResponseTime
@GetMapping("/call")
public String call(){
return animalService.say();
}
}
3.3、application.properties内容配置参数"animal.name"值
3.4、最后通过项目启动类启动项目(项目启动类就一个@SpringBootApplicaiton注解)
package com.cjb.mavendemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MavenDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MavenDemoApplication.class, args);
}
}
3.5、接口测试
调用http接口测试:
修改"animal.name"值为"cat",再次调用http接口访问:
四、源码地址,参考资料
组件代码:https://download.csdn.net/download/u010132847/87426046
集成自定义组件代码:https://download.csdn.net/download/u010132847/87426048
来源:https://blog.csdn.net/u010132847/article/details/128920976
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- 引言一个Java Gradle项目会涉及到资源的访问. 一般情况下会将当前项目所需的资源文件全部放置于resources文件夹下, 无论是m
- 创建maven父子工程时遇到一个问题,当子工程的名称前缀和父工程的名称一样时,子工程会出现一系列的问题。比如我的父工程名称是microser
- 我们经常会有用到,当A 用户在北京登录 ,然后A用户在天津再登录 ,要踢出北京登录的状态。如果用户在北京重新登录,那么又要踢出天津的用户,这
- SpringBoot读取外置logback配置文件springboot项目可以读取外置配置文件,避免了修改配置文件需要重新打包部署的问题。部
- Json 是一个用于 Java 对象 和 Json 文本 相互转换的工具类。安装下载源码g
- 本文实例为大家分享了Unity使用鼠标旋转物体效果的具体代码,供大家参考,具体内容如下了解完基础知识后,然我们来做个小程序练习一下1.在Ma
- 工厂模式Spring中bean的创建,默认是框架利用反射new出来的bean实例。有时候也会有一些复杂的情况。假设有一个飞机,属性如下,现在
- 想要制作一个简易的登录界面非常容易,总体上来说就是UI布局、给定id、新建跳转的页面、以及输入账号密码的获取与判断,那么接下来就开始制作吧!
- 介绍装饰模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,装饰模式相比生成子类更为灵活,这样可以给某个对象
- java.lang.StackOverflowError出现的原因严重: Exception initializing page conte
- 在app中图片的轮播显示可以说是非常常见的实现效果了,其实现原理不过是利用ViewPager,然后利用handler每隔一定的时间将View
- 本文实例讲述了Java获取凌晨时间戳的方法。分享给大家供大家参考,具体如下:这两天有一个需求是查询用户匹配的推荐信息,包含一个有效时间段,以
- Java设计模式访问者模式模式概念访问者模式表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的
- 项目需求最近项目中有一个需求就是让Java代码去代替人工操作,自动生成PPT,具体就是查询数据库数据,然后根据模板文件(PPT),将数据库数
- 线程的同步是保证多线程安全访问竞争资源的一种手段。线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同
- 一、前言随着互联网项目前后端分离方式的流行,前端与后端交给不同的人员开发,项目沟通成本也随之提高。主要表现在WebAPI接口的沟通,Swag
- 摘要:介绍使用Java Stream流排序器Comparator对List集合进行多字段排序的方法,包括复杂实体对象多字段升降序排序方法。综
- 单例:Singleton,是指仅仅被实例化一次的类。饿汉单例设计模式一、饿汉设计模式public class SingletonHungry
- 集合定义集合,集合是java中提供的一种容器,可以用来存储多个数据。特点:数组的长度是固定的。集合的长度是可变的。集合中存储的元素必须是引用
- 本文实例为大家分享了java实现单词小游戏的具体代码,供大家参考,具体内容如下介绍公司最近有一个竞技场项目,里面有一个单词小游戏。游戏大概就