首页 > 编程语言 >Java:自定义实现SpringBoot Starter

Java:自定义实现SpringBoot Starter

时间:2023-10-24 11:04:26浏览次数:45  
标签:Java 自定义 boot springframework org import com example Starter

(目录)

1、自定义Starter

1.1、项目结构

$ tree
.
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── demo
    │   │               ├── ReadingConfiguration.java
    │   │               ├── config
    │   │               │   └── ReadingConfig.java
    │   │               └── service
    │   │                   ├── ReadingService.java
    │   │                   └── impl
    │   │                       └── ReadingServiceImpl.java
    │   └── resources
    │       ├── META-INF
    │       │   └── spring.factories
    │       └── application.properties
    └── test
        ├── java
        │   └── com
        │       └── example
        │           └── demo
        │               └── ReadingServiceTests.java
        └── resources
            └── application.yml

1.2、代码实现

pom.xml maven依赖

<?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.7.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>thinking-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <!-- 自动配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

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

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <!-- 不会编译测试 -->
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.properties 默认值

# 注意:该文件名只能是application.properties
# 实践发现:该文件不能出现,否则业务工程的application.yml文件无法覆盖默认值
# 设置默认的值
reading.type=txt

ReadingConfig.java 配置映射

package com.example.demo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 读取application.properties中reading相关的配置
 */
@Data
@ConfigurationProperties(prefix = "reading")
public class ReadingConfig {
    // 类型
    private String type;
}

ReadingService.java 接口

package com.example.demo.service;

public interface ReadingService {
    void reading();
}

ReadingServiceImpl.java 接口实现类

package com.example.demo.service.impl;

import com.example.demo.config.ReadingConfig;
import com.example.demo.service.ReadingService;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ReadingServiceImpl implements ReadingService {
    /**
     * reading相关配置类
     */
    private ReadingConfig readingConfig;

    public ReadingServiceImpl(ReadingConfig readingConfig) {
        this.readingConfig = readingConfig;
    }

    @Override
    public void reading() {
        log.info("reading type: {}", this.readingConfig.getType());
    }
}

ReadingConfiguration.java 自动装配的核心

package com.example.demo;

import com.example.demo.config.ReadingConfig;
import com.example.demo.service.ReadingService;
import com.example.demo.service.impl.ReadingServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(ReadingConfig.class)
// 当存在reading.enable属性,且其值为true时,才初始化该Bean
@ConditionalOnProperty(name = "reading.enable", havingValue = "true")
public class ReadingConfiguration {

    @Autowired
    private ReadingConfig readingConfig;

    // 若当前IOC容器中没有Reading接口实现时,提供一个默认的Reading实现
    @Bean
    @ConditionalOnMissingBean(ReadingService.class)
    public ReadingService readingService() {
        return new ReadingServiceImpl(this.readingConfig);
    }
}

spring.factories 自动装配注册

# 提醒SpringBoot在启动时,自动装配ReadingConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.demo.ReadingConfiguration

1.3、测试

ReadingServiceTests.java

package com.example.demo;


import com.example.demo.service.ReadingService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest(classes = ReadingConfiguration.class)
class ReadingServiceTests {

    @Autowired
    private ReadingService readingService;

    @Test
    void testReadingService() {
        readingService.reading();
    }
}

application.yml

reading:
  enable: true

1.4、打包

将项目安装到本地maven仓库

mvn install

2、引用Starter

新建一个业务工程

2.1、项目结构

$ tree
.
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── demo
        │               ├── Application.java
        │               └── controller
        │                   └── IndexController.java
        └── resources
            └── application.yml

2.2、引入starter

<!-- 引入自定义starter -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>thinking-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

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.7.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- 引入自定义starter -->
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>thinking-starter</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

reading:
  type: json
  enable: true

IndexController.java

package com.example.demo.controller;

import com.example.demo.service.ReadingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @Autowired(required = false)
    private ReadingService readingService;

    @GetMapping("/")
    public String index(){
        if(this.readingService != null){
            this.readingService.reading();
        }

        return "Hello";
    }
}

Application.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

}

3、重写默认配置

修改项目配置文件application.yml

reading:  
    enable: true  
    type: json # 重写starter默认配置

4、重写默认实现

默认提供的实现

public class ReadingConfiguration {
    @Bean
    @ConditionalOnMissingBean(ReadingService.class)
    public ReadingService readingService() {
        return new ReadingServiceImpl(this.readingConfig);
    }
}

若当前IOC容器中没有Reading接口实现时,提供一个默认的Reading实现

@ConditionalOnMissingBean(ReadingService.class)

可以自行实现ReadingService

package com.example.web.service;

import com.example.demo.config.ReadingConfig;
import com.example.demo.service.ReadingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class MyReadingService implements ReadingService {

    @Autowired
    private ReadingConfig readingConfig;

    @Override
    public void reading() {
        log.info("my reading service start reading... type is {}", this.readingConfig.getType());
    }

}

5、实现@Enable注解

类似的注解

@EnableScheduling
@EnableAsync
@EnableCaching

5.1、starter项目创建注解

创建注解

package com.example.demo.annotation;


import com.example.demo.ReadingSelector;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Import作用是往SpringIOC中导入一个类,这里即导入ReadingSelector
@Import(ReadingSelector.class)
public @interface EnableReading {

}

创建 ReadingSelector类取代 ReadingConfiguration

package com.example.demo;
import com.example.demo.config.ReadingConfig;
import com.example.demo.service.ReadingService;
import com.example.demo.service.impl.ReadingServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(ReadingConfig.class)
public class ReadingSelector {

    @Autowired
    private ReadingConfig readingConfig;

    // 当SpringIOC容器中不存在Reading实现时,才往Spring中初始化ReadingService作为Reading接口的实现
    @Bean
    @ConditionalOnMissingBean(ReadingService.class)
    public ReadingService readingService() {
        return new ReadingServiceImpl(this.readingConfig);
    }

}

5.2、业务工程使用注解

修改配置文件 application.yml

reading:
  # enable: true
  type: json

启动类增加注解@EnableReading

package com.example.web;

import com.example.demo.annotation.EnableReading;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableReading
@SpringBootApplication
public class Application {

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

}

6、元数据

元数据描述后,开发者便能通过IDE提示看到此配置属性的默认值/值类型等信息

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

重新打包,会自动生成spring-configuration-metadata.json

7、参考文章

  1. 分分钟!手写一个自己的springboot starter
  2. 好好地说一次,自定义springboot-starter开发(从配置到单元测试)

项目代码:https://github.com/mouday/spring-boot-demo

标签:Java,自定义,boot,springframework,org,import,com,example,Starter
From: https://blog.51cto.com/mouday/8001601

相关文章

  • Java医院绩效考核系统源码
    一、系统总体功能作为医院用综合绩效核算系统,系统需要和his系统进行对接,按照设定周期,从his系统获取医院科室和医生、护士、其他人员工作量,对没有录入信息化系统的工作量,绩效考核系统设有手工录入功能(可以批量导入),对获取的数据系统按照设定的公式进行汇算,且设置审核机制,可以退回修......
  • Java中将Byte[] 转成封装类型
    今天做一个需求,需要将byte[]类型转换为Double和String,因此整理起来 Doublebyte[]转doublepublicDoublebyteArrayToDouble(byte[]bytes){if(bytes.length!=8){thrownewIllegalArgumentException("数据长度不符");}longlongBits=0;for(inti=0;i<8......
  • 如何使用Java Spring Boot 创建一个微服务项目 一?
    如何使用JavaSpringBoot创建一个微服务项目一?微服务现在更流行。它们可以用任何语言编写。在这篇文章中,让我们看看SpringBoot微服务。在本文中,我们看到一个基础项目currency-exchange-sample-service,它具有业务逻辑,并且可以在另一个项目 currency-conversion-sample-service ......
  • 如何使用Java Spring Boot 创建一个微服务项目 二?
    如何使用JavaSpringBoot创建一个微服务项目二?上一篇我们已经链接了如何使用JavaSpringBoot创建一个微服务项目一?这一篇我们接着实现第二部分微服务2:货币兑换样本服务这也是一个maven项目pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apac......
  • JavaScript 将对象转换为数组
    JavaScript将对象转换为数组在JavaScript中,你可以使用不同的方法将对象转换为数组,具体取决于对象的结构和你希望在数组中得到什么样的数据。以下是一些常见的方法:Object.keys()方法:这种方法将对象的键转换为数组。constobj={a:1,b:2,c:3};constarr=Object......
  • Java使用多线程异步执行批量更新操作方法
    一、核心技术Java提供了Executor框架来实现多线程任务的执行。我们可以通过创建ExecutorService对象来管理线程池,然后将任务提交给这个线程池执行。Executor框架的优点在于,它可以自动管理线程数量,以最大化利用CPU和内存资源。二、具体实现方法1、创建一个数据更新任务类,实现Run......
  • java.security.provider.getservice blocked
    bug:https://bugs.openjdk.org/browse/JDK-8206333堆栈:"Osp-Common-Business-Thread-572"Id=1723BLOCKEDatjava.security.Provider.getService(Provider.java:1035)atsun.security.jca.ProviderList.getService(ProviderList.java:332)atsun.security.jca......
  • 入门篇-其之六-Java运算符(中)
    祝所有程序员,1024节日快乐!!!......
  • 一天吃透Java并发面试八股文
    内容摘自我的学习网站:topjavaer.cn分享50道Java并发高频面试题。线程池线程池:一个管理线程的池子。为什么平时都是使用线程池创建线程,直接new一个线程不好吗?嗯,手动创建线程有两个缺点不受控风险频繁创建开销大为什么不受控?系统资源有限,每个人针对不同业务都可以手动......
  • 华为云服务器+java环境配置
     在华为云耀云服务器L实例(官网地址https://www.huaweicloud.com/product/hecs-light.html)中,我们有着部署管理系统的场景,本期教程中,我们需要开始部署管理系统,在前面教程中我们已经配置好了服务器的数据库以及基本的运行环境,现在我们需要开始部署java环境的配置,来为后期的项目......