第二十八章:SpringBoot使用AutoConfiguration自定义Starter

在我们学习SpringBoot时都已经了解到starterSpringBoot的核心组成部分,SpringBoot为我们提供了尽可能完善的封装,提供了一系列的自动化配置的starter插件,我们在使用spring-boot-starter-web时只需要在pom.xml配置文件内添加依赖就可以了,我们之前传统方式则是需要添加很多相关SpringMVC配置文件。而spring-boot-starter-web为我们提供了几乎所有的默认配置,很好的降低了使用框架时的复杂度。 因此在使用xx.starter时你就不用考虑该怎么配置,即便是有一些必要的配置在application.properties配置文件内对应配置就可以了,那好,为什么我在application.properties配置对应属性后xx.starter就可以获取到并作出处理呢?下面我们带着这个疑问来编写我们自定义的starter让我们深入了解SpringBoot

本章目标

自定义starter并且通过spring-boot-autoconfigure完成自动化配置。

SpringBoot 企业级核心技术学习专题

专题

专题名称

专题描述

001

Spring Boot 核心技术

讲解SpringBoot一些企业级层面的核心组件

002

Spring Boot 核心技术章节源码

Spring Boot 核心技术简书每一篇文章码云对应源码

003

Spring Cloud 核心技术

对Spring Cloud核心技术全面讲解

004

Spring Cloud 核心技术章节源码

Spring Cloud 核心技术简书每一篇文章对应源码

005

QueryDSL 核心技术

全面讲解QueryDSL核心技术以及基于SpringBoot整合SpringDataJPA

006

SpringDataJPA 核心技术

全面讲解SpringDataJPA核心技术

构建项目

创建starter项目我们并不需要创建SpringBoot项目,我们创建一个Maven项目就可以满足我们的需求,创建项目完成后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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yuqiyu</groupId>
    <artifactId>chapter28</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>1.5.4.RELEASE</version>
        </dependency>
    </dependencies>
</project>

我们这个starter并不做其他复杂逻辑的编写,所以这里的依赖只是添加了spring-boot-autoconfigure,实战开发时可以添加任意依赖到项目中。

配置映射参数实体

我们在文章开头埋下了一个疑问,starter是如何读取application.properties或者application.yml配置文件内需要的配置参数的呢?那么接下来我们就看看如何可以获取自定义的配置信息。 SpringBoot在处理这种事情上早就已经考虑到了,所以提供了一个注解@ConfigurationProperties,该注解可以完成将application.properties配置文件内的有规则的配置参数映射到实体内的field内,不过需要提供setter方法,自定义配置参数实体代码如下所示:

package com.yuqiyu.chapter28;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * 配置文件实体映射
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/22
 * Time:22:51
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@ConfigurationProperties(prefix = "hello")
public class HelloProperties
{
    //消息内容
    private String msg = "HengYu";
    //是否显示消息内容
    private boolean show = true;

    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public boolean isShow() {
        return show;
    }
    public void setShow(boolean show) {
        this.show = show;
    }
}

在上面代码中,@ConfigurationProperties注解内我们使用到了属性preffix,该属性配置了读取参数的前缀,根据上面的实体属性对应配置文件内的配置则是hello.msghello.show,当然我们提供了默认值,配置文件内不进行配置时则是使用默认值。

编写自定义业务

我们为自定义starter提供一个Service,并且提供一个名为sayHello的方法用于返回我们配置的msg内容。代码如下所示:

package com.yuqiyu.chapter28;

/**
 * 自定义业务实现
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/22
 * Time:22:54
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
public class HelloService
{
    //消息内容
    private String msg;
    //是否显示消息内容
    private boolean show = true;

    public String sayHello()
    {
        return show ? "Hello," + msg : "Hidden";
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public void setShow(boolean show) {
        this.show = show;
    }
}

我们Service内的代码比较简单,根据属性参数进行返回格式化后的字符串。

接下来我们开始编写自动配置,这一块是starter的核心部分,配置该部分后在启动项目时才会自动加载配置,当然其中有很多细节性质的配置

实现自动化配置

自动化配置其实只是提供实体bean的验证以及初始化,我们先来看看代码:

package com.yuqiyu.chapter28;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
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;

/**
 * 自定义starter自动化配置
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/22
 * Time:22:56
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@Configuration//开启配置
@EnableConfigurationProperties(HelloProperties.class)//开启使用映射实体对象
@ConditionalOnClass(HelloService.class)//存在HelloService时初始化该配置类
@ConditionalOnProperty//存在对应配置信息时初始化该配置类
        (
                prefix = "hello",//存在配置前缀hello
                value = "enabled",//开启
                matchIfMissing = true//缺失检查
        )
public class HelloAutoConfiguration
{

    //application.properties配置文件映射前缀实体对象
    @Autowired
    private HelloProperties helloProperties;

    /**
     * 根据条件判断不存在HelloService时初始化新bean到SpringIoc
     * @return
     */
    @Bean//创建HelloService实体bean
    @ConditionalOnMissingBean(HelloService.class)//缺失HelloService实体bean时,初始化HelloService并添加到SpringIoc
    public HelloService helloService()
    {
        System.out.println(">>>The HelloService Not Found,Execute Create New Bean.");
        HelloService helloService = new HelloService();
        helloService.setMsg(helloProperties.getMsg());//设置消息内容
        helloService.setShow(helloProperties.isShow());//设置是否显示
        return helloService;
    }
}

自动化配置代码中有很多我们之前没有用到的注解配置,我们从上开始讲解

@Configuration:这个配置就不用多做解释了,我们一直在使用 @EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。

SpringBoot内置条件注解

有关@ConditionalOnXxx相关的注解这里要系统的说下,因为这个是我们配置的关键,根据名称我们可以理解为具有Xxx条件,当然它实际的意义也是如此,条件注解是一个系列,下面我们详细做出解释

@ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件 @ConditionalOnClass:当SpringIoc容器内存在指定Class的条件 @ConditionalOnExpression:基于SpEL表达式作为判断条件 @ConditionalOnJava:基于JVM版本作为判断条件 @ConditionalOnJndi:在JNDI存在时查找指定的位置 @ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件 @ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件 @ConditionalOnNotWebApplication:当前项目不是Web项目的条件 @ConditionalOnProperty:指定的属性是否有指定的值 @ConditionalOnResource:类路径是否有指定的值 @ConditionalOnSingleCandidate:当指定BeanSpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean @ConditionalOnWebApplication:当前项目是Web项目的条件

以上注解都是元注解@Conditional演变而来的,根据不用的条件对应创建以上的具体条件注解。

到目前为止我们还没有完成自动化配置starter,我们需要了解SpringBoot运作原理后才可以完成后续编码。

Starter自动化运作原理

在注解@SpringBootApplication上存在一个开启自动化配置的注解@EnableAutoConfiguration来完成自动化配置,注解源码如下所示:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能,而EnableAutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包。我们可以先来看下spring-boot-autoconfigure包内的spring.factories文件内容,如下所示:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
.....省略

可以看到配置的结构形式是Key=>Value形式,多个Value时使用,隔开,那我们在自定义starter内也可以使用这种形式来完成,我们的目的是为了完成自动化配置,所以我们这里Key则是需要使用org.springframework.boot.autoconfigure.EnableAutoConfiguration

自定义spring.factories

我们在src/main/resource目录下创建META-INF目录,并在目录内添加文件spring.factories,具体内容如下所示:

#配置自定义Starter的自动化配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yuqiyu.chapter28.HelloAutoConfiguration

都目前为止我们的自定义starter已经配置完成,下面我们需要新建一个SpringBoot项目来测试我们的自动化配置是否已经生效。

创建测试SpringBoot项目

在使用自定义starter之前需要将starterMaven Jar Install到本地,我们使用idea工具自带的maven命令完成该操作

步骤:工具右侧 -> Maven Projects -> Lifecycle -> install

创建测试项目的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yuqiyu.sample</groupId>
    <artifactId>test-spring-boot-starter-hello</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>test-spring-boot-starter-hello</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>                
        <!--自定义starter依赖-->
        <dependency>
            <groupId>com.yuqiyu</groupId>
            <artifactId>chapter28</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

我们只需要将依赖添加到pom.xml配置文件内

运行测试

在运行项目之前,我们打开application.properties配置文件开启debug模式,查看自动化配置的输出日志,配置内容如下所示:

#显示debug日志信息
debug=true

接下来我们启动项目,在控制台查找是否存在我们的HelloAutoConfiguration日志输出,控制台输出内容如下所示:

.....省略
>>>The HelloService Not Found,Execute Create New Bean.
.....省略
   HelloAutoConfiguration matched:
      - @ConditionalOnClass found required class 'com.yuqiyu.chapter28.HelloService'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
      - @ConditionalOnProperty (hello.enabled) matched (OnPropertyCondition)

   HelloAutoConfiguration#helloService matched:
      - @ConditionalOnMissingBean (types: com.yuqiyu.chapter28.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)
.....省略

在控制台可以看到我们的自定义starter的自动化配置已经生效了,并且根据@ConditionalOnMissingBean(HelloService.class)做出了条件注入HelloService实体bean到SpringIoc容器内

编写测试控制器

我们来编写一个简单的测试控制器,查看HelloService在不配置参数情况下输出格式化字符串内容,控制器代码如下所示:

package com.yuqiyu.sample.testspringbootstarterhello;

import com.yuqiyu.chapter28.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试自定义starter自动化配置HelloService
 * ========================
 * Created with IntelliJ IDEA.
 * User:恒宇少年
 * Date:2017/7/23
 * Time:11:42
 * 码云:http://git.oschina.net/jnyqy
 * ========================
 */
@RestController
public class HelloController
{
    //注入自定义starter内逻辑
    @Autowired
    HelloService helloService;

    /**
     * 测试访问地址/hello
     * @return 格式化字符串
     */
    @RequestMapping(value = "/hello")
    public String sayHello()
    {
        return helloService.sayHello();
    }
}

接下来我们重启下项目,访问地址http://127.0.0.1:8080/hello,界面输出内容如下所示:

Hello,HengYu

界面输出的内容是我们默认值,接下来我们在application.properties配置文件内对应添加hello.msghello.show配置参数,如下所示:

#配置自定义starter参数
hello.msg=HengYu Boy
hello.show=true

重启项目,再次访问地址,界面输出内容如下所示:

Hello,HengYu Boy

我们的配置生效了,到目前为止我相信大家已经明白了我们application.properties配置文件为什么可以作为统一配置入口,为什么配置后可以被对应starter所使用。

总结

以上内容是本章的全部讲解,本章主要讲解了我们如何自定义starter并且自动化配置到SpringBoot项目中,当然里面还有很多神奇的地方需要大家去深入挖掘。

本章代码已经上传到码云: SpringBoot配套源码地址:https://gitee.com/hengboy/spring-boot-chapter SpringCloud配套源码地址:https://gitee.com/hengboy/spring-cloud-chapter SpringBoot相关系列文章请访问:目录:SpringBoot学习目录 QueryDSL相关系列文章请访问:QueryDSL通用查询框架学习目录 SpringDataJPA相关系列文章请访问:目录:SpringDataJPA学习目录 感谢阅读!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏专注研发

IDEA环境下SSM整合------环境配置

一、做不出详细的概念叙述和文本设计,本文主要以实战步骤为主,少量解释为辅助,下面请大家牢记两幅图:

831
来自专栏编程直播室

Spring Boot 之 Spring Data JPA(一)1、新建工程2、配置数据库3、代码结构4、从数据到逻辑总结

2935
来自专栏IT笔记

SpringBoot开发案例之整合Dubbo消费者

有人卖就有人买,显然是亘古不变的真理,前两篇讲解了SpringBoot+Dubbo的提供者的几种暴露方式,这篇跟大家分享一下消费者如何去订阅属于自己的服务。 相...

2635
来自专栏大魏分享(微信公众号:david-share)

本地&远程访问一个EJB | 从开发角度看应用架构4

1122
来自专栏王磊的博客

Spring Boot 最佳实践(二)集成Jsp与生产环境部署

提起Java不得不说的一个开发场景就是Web开发,也是Java最热门的开发场景之一,说到Web开发绕不开的一个技术就是JSP,因为目前市面上仍有很多的公司在使用...

1255
来自专栏PhpZendo

是否需要使用依赖注入容器?

本文是依赖注入(Depeendency Injection)系列教程的第 2 篇文章,本系列教程主要讲解如何使用 PHP 实现一个轻量级服务容器,教程包括:

542
来自专栏用户2442861的专栏

ApplicationContext 作用

现在让我们在 HelloSpring 项目下创建实际的源文件。首先,我们需要创建一个名为 com.tutorialspoint 的包。为了做到这个,在 pac...

631
来自专栏纯洁的微笑

Spring Boot 2.0 版的开源项目云收藏来了!

713
来自专栏云计算

使用 Spring Boot 2.0,Eureka 和 Spring Cloud 的微服务快速指南

原文地址:https://dzone.com/articles/quick-guide-to-microservices-with-spring-boot-20...

9401
来自专栏一个会写诗的程序员的博客

Springboot使用velocity模板引擎第五章 使用velocity模板引擎

最爽的Web组合开发就是Intellij IDEA + Maven + Spring Boot + Scala + Velocity + Boostrap + ...

1023

扫码关注云+社区