SpringBoot: Custom Starter

 

It is not easy to write, please indicate when reprinting (http://shihlei.iteye.com/blog/2407689)!

I. Overview

SpringBoot personal feeling features:

1) A collection of many libraries (various Starters), which is convenient to quickly build application systems.

2) Automatically configure spring (through the AutoConfiguration mechanism), simplify the configuration, and also facilitate the expansion of new Starters.

3) Embedded web container without WAR deployment.

 

This article customizes a helloworld-starter to reveal the definition process of starter and the automatic configuration method of Spring AutoConfiguration.

 

Note:

SpringBoot AutoConfiguration mechanism: 

    When SpringBoot starts, it scans the META-INF/spring.factories files in all Jars in the classpath, reads the Configuration specified by org.springframework.boot.autoconfigure.EnableAutoConfiguration, automatically creates beans according to the Conditional conditions on the Configuration, and injects them into the container.

 

Spring's auto-configured Starter and auto-configuration capabilities are provided by the following packages.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>1.5.9.RELEASE</version>
</dependency>

 

For the use of Spring framework @Configuration, please refer to the blog "Spring: @Configuration Use"

 

Two Demo project planning

(1) spring-boot-helloworld-starter project - custom Starter

Realize the automatic configuration of HelloWorldTemplate, and decide how to format the information according to whether there is a FastJson package in the classpath

 

 

(2) spring-boot-web project - calling Starter

Rely on the spring-boot-helloworld-starter project HelloWorldTemplate, and return the formatted information to the client (the information includes the information in helloworld.author in application.yml, "Hello World", and the incoming object information of the call)

 

 

Three spring-boot-helloworld-starter projects

(1) Engineering:

1) Parent pom: 

Among them: spring-boot-dependencies provides the dependency declaration of spring boot

 

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>x.demo.springboot.starter</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>module/spring-boot-helloworld-starter</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
        <spring.boot.version>1.5.9.RELEASE</spring.boot.version>
        <fastjson.version>1.2.28</fastjson.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- spring boot 基础依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

    

 

2)module: 

其中FastJson 使用,<optional>true</optional> ,指定客户端可以选择依赖该jar,用于实现如果classpath 中没有fastjson的jar,则不使用JSON FastJsonOutputFormater

 

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-boot-starter</artifactId>
        <groupId>x.demo.springboot.starter</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-boot-helloworld-starter</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <!-- 指定客户端可以选择依赖该jar,用于实现如果classpath 中没有fastjson的jar,则不使用JSON FastJsonOutputFormater-->
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

 

 

(2)主要服务类:HelloWorldTemplate

实现组装配置文件中的作者信息、HelloWorld、用户传入的对象信息

 

package x.demo.springboot.starter.hw;

import x.demo.springboot.starter.hw.autoconfiguration.HelloWorldProperties;
import x.demo.springboot.starter.hw.outputformater.OutputFormater;

/**
 * 配置文件中的作者信息
 * HelloWorld
 * 对象信息
 */
public class HelloWorldTemplate {

    private HelloWorldProperties helloWorldProperties;

    private OutputFormater outputFormater;

    public HelloWorldTemplate(HelloWorldProperties helloWorldProperties, OutputFormater outputFormater) {
        this.helloWorldProperties = helloWorldProperties;
        this.outputFormater = outputFormater;
    }

    /**
     * 打印作者信息
     * 打印对象信息
     *
     * @param obj 打印的对象
     */
    public <T> String generate(T obj) {
        StringBuilder message = new StringBuilder();
        message.append("author: ").append(outputFormater.format(helloWorldProperties.getAuthor())).append("\n");
        message.append("say: Hello World!").append("\n");
        message.append("object: ").append(outputFormater.format(obj)).append("\n");
        return message.toString();
    }
}

 

 

(3)HelloWorldProperties:映射application.yaml文件中的 helloworld.authors信息:

以通过@ConfigurationProperties(prefix = HelloWorldProperties.HELLOWORLD_PREFIX) 标识是属性映射类,同时指定了在主配置文件中的前缀。需要通过@EnableConfigurationProperties(HelloWorldProperties.class)开启。

 

package x.demo.springboot.starter.hw.autoconfiguration;

import java.util.Map;

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

/**
 * 用于springboot 从配置文件 (application.yml) 中读取 helloworld配置
 */
@ConfigurationProperties(prefix = HelloWorldProperties.HELLOWORLD_PREFIX)
public class HelloWorldProperties {

    public static final String HELLOWORLD_PREFIX = "helloworld";

    private Map<String, Object> author;

    public Map<String, Object> getAuthor() {
        return author;
    }

    public void setAuthor(Map<String, Object> author) {
        this.author = author;
    }
}

 

 

(4)OutputFormater:输出格式化:

1)接口

 

package x.demo.springboot.starter.hw.outputformater;

/**
 * 格式化工具
 */
public interface OutputFormater {

    /**
     * 格式
     * @param obj 对象
     * @return 格式字符串
     */
    <T> String format(T obj);
}

 

 

2)toString()实现

 

package x.demo.springboot.starter.hw.outputformater;

import java.util.Objects;

/**
 * 使用对象toString() 格式化
 */
public class ToStringOutputFormater implements OutputFormater {
    /**
     * 格式
     *
     * @param obj 对象
     * @return 格式字符串
     */
    public <T> String format(T obj) {
        return "ToStringOutputFormater:" +Objects.toString(obj);
    }
}

 

 

3)json 实现

 

package x.demo.springboot.starter.hw.outputformater;

import com.alibaba.fastjson.JSON;

/**
 * 使用fastjson格式化
 */
public class FastJsonOutputFormater implements OutputFormater {
    /**
     * 格式
     *
     * @param obj 对象
     * @return 格式字符串
     */
    public <T> String format(T obj) {
        return "FastJsonOutputFormater: " + JSON.toJSONString(obj);
    }
}

 

 

(5)自动配置类,用于向容器注册相应的对象:

关于Spring框架@Configuration使用,可参见博客《Spring:@Configuration 使用》

 

1)OutputFormaterAutoConfiguration:配置OutputFormater

 

这里通过@Conditional注解指定注册条件,主要用了:

 

@ConditionalOnMissingClass : classpath 中没有com.alibaba.fastjson.JSON 实例化,创建ToStringOutputFormater

@ConditionalOnClass(name = "com.alibaba.fastjson.JSON") classpath 中有com.alibaba.fastjson.JSON 实例化 FastJsonOutputFormater

package x.demo.springboot.starter.hw.autoconfiguration;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import x.demo.springboot.starter.hw.outputformater.FastJsonOutputFormater;
import x.demo.springboot.starter.hw.outputformater.OutputFormater;
import x.demo.springboot.starter.hw.outputformater.ToStringOutputFormater;

@Configuration
public class OutputFormaterAutoConfiguration {

    /**
     * @ConditionalOnMissingClass : classpath 中没有com.alibaba.fastjson.JSON 实例化,创建ToStringOutputFormater
     */
    @ConditionalOnMissingClass("com.alibaba.fastjson.JSON")
    @Bean
    public OutputFormater toStringOutputFormater() {
        return new ToStringOutputFormater();
    }

    /**
     * @ConditionalOnClass(name = "com.alibaba.fastjson.JSON") classpath 中有com.alibaba.fastjson.JSON 实例化 FastJsonOutputFormater
     */
    @ConditionalOnClass(name = "com.alibaba.fastjson.JSON")
    @Bean
    public OutputFormater fastJsonOutputFormater() {
        return new FastJsonOutputFormater();
    }
}

 

 

2)HelloWorldAutoConfiguration:配置HelloWorldTemplate

package x.demo.springboot.starter.hw.autoconfiguration;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import x.demo.springboot.starter.hw.HelloWorldTemplate;
import x.demo.springboot.starter.hw.outputformater.OutputFormater;


@EnableConfigurationProperties(HelloWorldProperties.class)
@Import(OutputFormaterAutoConfiguration.class)
@Configuration
public class HelloWorldAutoConfiguration {

    @Bean
    public HelloWorldTemplate helloWorldTemplate(HelloWorldProperties helloWorldProperties, OutputFormater outputFormater) {
        return new HelloWorldTemplate(helloWorldProperties, outputFormater);
    }
}

  

(6)注册自动配置类:META-INF/spring.factories

 

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
x.demo.springboot.starter.hw.autoconfiguration.HelloWorldAutoConfiguration

 

四 spring-boot-web 项目

(1)工程:

1)父 pom : 继承了spring-boot-starter-parent

 

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>x.demo.springboot.starter</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>module/spring-boot-helloworld-starter</module>
    </modules>

    <properties>
        <java.version>1.8</java.version>
        <spring.boot.version>1.5.9.RELEASE</spring.boot.version>
        <fastjson.version>1.2.28</fastjson.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- spring boot 基础依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

 

2)module: 

依赖下spring-boot-helloworld-starter,这里不依赖FastJson的包,则自定义Starter中实例化的是没 ToStringOutputFormater

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>x.demo.springboot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>spring-boot-web</artifactId>
    <packaging>jar</packaging>

    <name>spring-boot-web</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>x.demo.springboot.starter</groupId>
            <artifactId>spring-boot-helloworld-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

 

(2)配置文件:application.yml

# 应用配置
server:
  port: 8080

# helloworld starter 配置
helloworld.author:
  name: foo
  roles: admin, developer
  email: [email protected]

 

(3)controller 调用 HelloWorldTemplate

package x.demo.springboot.web.controller;


import javax.annotation.Resource;

import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import x.demo.springboot.starter.hw.HelloWorldTemplate;

@RestController
@RequestMapping("/helloworld")
public class HelloWorldContorller {

    @Resource
    HelloWorldTemplate helloWorldTemplate;

    @GetMapping("/say")
    public String say() {
        String message = helloWorldTemplate.generate("------------");
        return StringUtils.replace(message,"\n","<br/>");
    }
}

 

(4)springboot启动类:

注:@SpringBootApplication 同时添加重要的两个MetaAnnotation @EnableAutoConfiguration,@ComponentScan,启动了AutoConfiguration和包扫描。

 

由于:

web包:x.demo.springboot.web

starter包:x.demo.springboot.starter.hw

 

包不同,所以Starter不是包扫描注入的,是通过读取META-INF/spring.factories注入的

 

package x.demo.springboot.web;

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

@SpringBootApplication
public class SpringBootWeb {

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

 

五 结果

1)常规结果

 

 

2)添加FastJson依赖的结果

web module pom:

 

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>x.demo.springboot</groupId>
        <artifactId>spring-boot</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>spring-boot-web</artifactId>
    <packaging>jar</packaging>

    <name>spring-boot-web</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>x.demo.springboot.starter</groupId>
            <artifactId>spring-boot-helloworld-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>
</project>

 



  

六 涉及注解说明

1) 配置

@Configuration,@Import,@Bean:《Spring:@Configuration 使用》

 

2) 条件组装

@ConditionalOnBean:Spring容器中存在指定实例时

@ConditionalOnClass:类加载器中存在指定类时

@ConditionalOnExpression:指定表达式成立时

@ConditionalOnMissingBean:Spring容器总缺少指定实例时

@ConditionalOnMissingClass:类加载器中不存在指定类时

@ConditionalOnNotWebApplication:非Web应用时

@ConditionalOnResource:存在指定资源文件时

@ConditionalOnWebApplication:Web应用时

 

3)读取属性

@EnableConfigurationProperties:开始扫描处理@ConfigurationProperties注解的Bean

@ConfigurationProperties:绑定外部配置

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326179038&siteId=291194637