Spring Boot自定义starter

164 阅读3分钟

前言

        我们经常封装自己写的框架代码,用于重复使用,通过jar依赖,代码触发来实现框架的插件的能力。在Spring-boot的框架中经常用到xxx-starter实现mybatis,mongodb,kafka等第三方中间件的服务,仅需在application等properties配置文件中写少量的配置即可实现。下面我来自行实现一个简单的starter。

        starter的3要素

                      1)配置加载器

                      2)spring bean

                      3)配置文件与spring bean配置管理器

1. 定义starter project

分别定义3个目录,用于存放3要素。

pom依赖如下:加上lombok,更方便

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
    </dependencies>

2. properties处理

SpringBoot在处理自定义application.properties或者application.yml配置文件内需要的配置参数的时候,提供了注解@ConfigurationProperties,将application.properties配置文件内的按照注解方式的配置参数映射到javabean的field内,并注入Spring的Bean容器中。

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

@Data
@ConfigurationProperties(value = "org.ee")
public class HelloProperties {
    private String starterDemo;
    private boolean demoEnable;
}

这个配置注解用于配置我们定义的规则配置注入这个bean,value就是prefix默认方式

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
    @AliasFor("prefix")
    String value() default "";

    @AliasFor("value")
    String prefix() default "";

    boolean ignoreInvalidFields() default false;

    boolean ignoreUnknownFields() default true;
}

3. 自定义starter do something

我们自定义starter是为了对spring boot框架集成服务或者中间件的client端或者server端等,比如我们自定义service。此部分服务功能在spring boot启动的时候自动加载。

@Data
public class HelloService {

    private String starterDemo;
    private boolean demoEnable;

    public String sayHello() {
        if (demoEnable) {
            return "hello:\t" + starterDemo + ",\ti do something";
        } else {
            return "i do nothing";
        }
    }
}

4. 管理配置bean与服务实现,自动化配置

//配置bean
@Configuration
//开启使用映射实体类,用于对自定义配置bean的创建,field的绑定
@EnableConfigurationProperties(HelloProperties.class)
//当存在HelloService类时初始化该配置类
@ConditionalOnClass(HelloService.class)
//application.properties或者application.yml文件存在对应配置信息时初始化该配置类
@ConditionalOnProperty
        (
                prefix = "org.ee",//存在配置前缀org.ee
                value = "enabled",//开启
                matchIfMissing = true//缺失检查
        )
public class HelloAutoConfiguration {

    @Autowired
    private HelloProperties helloProperties;

    @Bean
    //当缺失HelloService实体bean时,才初始化HelloService到spring ioc容器
    //用于自定义bean覆盖次bean实现进一步自定义能力
    @ConditionalOnMissingBean
    public HelloService buildDefaultHelloService(){
        HelloService helloService = new HelloService();
        helloService.setStarterDemo(helloProperties.getStarterDemo());
        helloService.setDemoEnable(helloProperties.isDemoEnable());
        return helloService;
    }
}

@Configuration:配置bean,等同于@Component

@EnableConfigurationProperties:开启使用配置属性,value是我们配置实体参数ClassType,将配置实体javabean作为配置来源。

其他SpringBoot内置@Conditional注解

@ConditionalOnBean:当Spring Ioc容器内存在指定Bean的时候生效
@ConditionalOnClass:当Spring Ioc容器内存在指定Class的时候生效
@ConditionalOnExpression:匹配SpEL表达式
@ConditionalOnJava:匹配JVM版本
@ConditionalOnJndi:存在JNDI时
@ConditionalOnMissingBean:当Spring Ioc容器内不存在指定Bean
@ConditionalOnMissingClass:当Spring Ioc容器内不存在指定Class
@ConditionalOnNotWebApplication:当项目不是Web项目
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnSingleCandidate:当指定BeanSpring Ioc容器内只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件

 5. 自动加载的本质EnableAutoConfiguration

配置spring.factories

         我们在src/main/resource目录下创建META-INF目录,然后创建文件spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ee.plugin.starter.config.HelloAutoConfiguration

配置后,spring boot的EnableAutoConfiguration注解在SpringBootApplication注解上,会自动加载我们写的HelloAutoConfiguration配置bean。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

6. starter使用

要使用我们的starter,需要mvn install 装载nexus库

在新project项目中依赖

<dependencies>
        <dependency>
            <groupId>com.feng.demo</groupId>
            <artifactId>feng-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>

    </dependencies>

新建application.properties文件

编写测试controller

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String sayHello(){
        return helloService.sayHello();
    }
}

启动,访问localhost:8080/hello

说明自定义starter生效。

 

总结

         spring boot和spring MVC本质没有太大的区别,但是自动化配置大大减少了程序的配置复杂度,方便开发专注于业务的开发能力。