starter 有什么用?

SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并启动相应的默认配置。

starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。

SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。

SpringBoot提供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。

所有这些依赖模块都遵循着约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。

为什么需要

在我们的日常开发工作中,经常会有一些独立于业务之外的配置模块,我们经常将其放到一个特定的包下,然后如果另一个工程需要复用这块功能的时候,需要将代码硬拷贝到另一个工程,重新集成一遍,麻烦至极。

如果我们将这些可独立于业务代码之外的功配置模块封装成一个个starter,复用的时候只需要将其在pom中引用依赖即可,SpringBoot为我们完成自动装配,简直不要太爽。

可以改进的个人项目

命名规则

SpringBoot提供的starter以spring-boot-starter-xxx的方式命名的。

官方建议自定义的starter使用 xxx-spring-boot-starter 命名规则。

以区分SpringBoot生态提供的starter。

代码编写

maven 依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
</parent>

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

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

或者采用依赖的方式:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.3.2.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>


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

还有自己特殊的额外依赖,专门用于功能的:

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>auto-log-spring</artifactId>
    <version>${auto-log.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
        </exclusion>
    </exclusions>
</dependency>

这里是把一些依赖包排除了,可忽略这部分引入。

代码编写

  • AutoLogProperties.java

这里主要通过 @ConfigurationProperties(prefix = "auto-log") 获取一些我们需要的配置信息。

比如 jdbc 连接信息等等。

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

@ConfigurationProperties(prefix = "auto-log")
public class AutoLogProperties {

    private String enable;

    private String level;

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getEnable() {
        return enable;
    }

    public void setEnable(String enable) {
        this.enable = enable;
    }

    @Override
    public String toString() {
        return "AutoLogProperties{" +
                "enable='" + enable + '\'' +
                ", level='" + level + '\'' +
                '}';
    }
}
  • AutoLogAutoConfig.java

这里比较有趣的就是 @ConditionalXXX 注解了,你可以根据自己的需要,决定什么时候注解生效。

import com.github.houbb.auto.log.spring.annotation.EnableAutoLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
@Configuration
//@ConditionalOnProperty(
//        prefix = "auto-log",
//        name = "enable",
//        havingValue = "true"
//)
@ConditionalOnClass(EnableAutoLog.class)
@EnableConfigurationProperties(AutoLogProperties.class)
@EnableAutoLog
public class AutoLogAutoConfig {

    @Autowired
    private AutoLogProperties properties;

    public void showInfo() {
        System.out.println(properties);
    }

}

其实这个和 spring 的自动扫包已经很接近了,只不过这里做了更多的特性增强。

配置文件

spring-boot 的所有自动配置,需要在 resources/META-INF/spring.factories 文件下进行指定:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.github.houbb.auto.log.sb.starter.config.AutoLogAutoConfig

这个其实就是 SPI 机制。

到这里我们的 starter 就已经写完了,是不是非常的简单。

测试

引入依赖

<dependencies>
    <dependency>
        <groupId>com.github.houbb</groupId>
        <artifactId>auto-log-sb-starter</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
</dependencies>

定义服务

  • UserService.java
public interface UserService {

    String queryLog(final String id);

}
  • UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {

    @Override
    @AutoLog
    public String queryLog(String id) {
        System.out.println("查询:" + id);

        return "result" + id;
    }

}

测试代码

import com.github.houbb.auto.log.sb.starter.config.AutoLogAutoConfig;
import com.test.service.UserService;
import com.test.service.properties.MyConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

/**
 * 启动项
 * @author binbin.hou
 * @since 1.0.0
 */
@SpringBootApplication
public class StarterTestApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(StarterTestApplication.class, args);
        AutoLogAutoConfig autoLogAutoConfig = context.getBean(AutoLogAutoConfig.class);
        autoLogAutoConfig.showInfo();

        UserService userService = context.getBean(UserService.class);
        userService.queryLog("1");
    }

}
  • application.properties
auto-log.enable=true
auto-log.level=INFO

不过这里的配置信息我暂时没有使用,只是做一个验证。

日志

AutoLogProperties{enable='true', level='INFO'}
2020-07-29 23:39:50.168  INFO 8316 --- [           main] c.g.h.a.l.c.s.i.AutoLogMethodInterceptor : public java.lang.String com.test.service.impl.UserServiceImpl.queryLog(java.lang.String) param is [1].
查询:1
2020-07-29 23:39:50.168  INFO 8316 --- [           main] c.g.h.a.l.c.s.i.AutoLogMethodInterceptor : public java.lang.String com.test.service.impl.UserServiceImpl.queryLog(java.lang.String) result is result1.

说明已经获取到了配置,并且我们定义的自动日志注解也是生效的。

TODO

auto-log 对于 spring 的依赖应该排除掉。

参考资料

SpringBoot应用篇(一):自定义starter