自定义Spring Boot Starter

一、起源

Spring时代,搭建一个 Web 应用通常需要在 pom 文件中引入多个 Web 模块相关的 Maven 依赖,如:SpringMvc、Tomcat等依赖,而 SpringBoot 则只需引入spring-boot-starter-web依赖即可。这就是SpringBoot 的 Starter特性,用来简化项目初始搭建以及开发过程,它是一个功能模块的所有 Maven 依赖集合体。
SpringBoot 用起来方便,它默认集成了 Java 的主流框架。这也是SpringBoot的一大特色,使用方便,需要什么框架或者技术,只需要引入对应的 starter 即可。
即使官方集成了很多主流框架,但SpringBoot官方也不能囊括我们所有的使用场景,往往我们需要自定义starter,来简化我们对SpringBoot的使用。

二、概述

2.1 starter示例

SpringBoot 提供了非常多的 Starter,下面列出常用的几个:

名称 功能
spring-boot-starter-web 支持 Web 开发,包括 Tomcat 和 spring-webmvc
spring-boot-starter-redis 支持 Redis 键值存储数据库,包括 spring-redis
spring-boot-starter-test 支持常规的测试依赖,包括 JUnit、Hamcrest、Mockito 以及 spring-test 模块
spring-boot-starter-aop 支持面向切面的编程即 AOP,包括 spring-aop 和 AspectJ
spring-boot-starter-data-elasticsearch 支持 ElasticSearch 搜索和分析引擎,包括 spring-data-elasticsearch
spring-boot-starter-jdbc 支持JDBC数据库
spring-boot-starter-data-jpa 支持 JPA ,包括 spring-data-jpa、spring-orm、Hibernate

这些 Starter 其实不包含 Java 代码,核心是它的 pom 文件。我们以 spring-boot-starter-web 为例,来看看该 Starter 的 pom 文件包含的内容。

先在项目中引入以下依赖:

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

然后找到引入的 spring-boot-starter-web 依赖的文件夹位置:

打开该 pom 文件进行查看:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starters</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.0.3.RELEASE</version>
    <name>Spring Boot Web Starter</name>
    
    ...
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.0.3.RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <!-- 对 json 解析的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
            <version>2.0.3.RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <!-- 提供 Tomcat 容器。通过这可以看到  ServletWeb 默认的容器是 Tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <version>2.0.3.RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <!-- hibernate 的校验框架 -->
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.0.10.Final</version>
            <scope>compile</scope>
        </dependency>

        <!-- 提供了核心 HTTP 集成,用于集成其它 web 框架的基础结构 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.7.RELEASE</version>
            <scope>compile</scope>
        </dependency>

        <!-- 提供了对 Spring MVC 的支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.7.RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

可以看到,在该 pom 文件中已经定义好了 Web 模块需要的各个组件。之后,引入的 Starter 依赖可以与 SpringBoot 的自动装配特性、外部化配置特性进行无缝衔接,来达到快速开发的目的。

可以看到这些 Starter 的名称都是以spring-boot-starter为开头,后面跟着具体的模块名,所有官方的 Starter 遵循相似的命名模式。

2.2 starter命名规范

starter的命名规则,命名规则分为两种,一种是官方的命名规则,另一种就是我们自己制作的starter命名规则。

2.2.1 官方命名规则

前缀:spring-boot-starter-
规则:spring-boot-starter-模块名
举例:spring-boot-starter-web、spring-boot-starter-jdbc

2.2.2 自定义命名规则

后缀:-spring-boot-starter
规则:模块-spring-boot-starter
举例:hello-spring-boot-starter

三、实例剖析

比如需要在Spring Boot中使用Rabbit MQ,我们只需要引入依赖以下依赖:

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

然后在application.propertiesapplication.yml中添加以下数据库配置信息:

spring:  
  rabbitmq:
    addresses: ${RABBIT_ADDRESS:192.168.0.100:5672}
    username: ${RABBIT_USERNAME:guest}
    password: ${RABBIT_PASSWORD:guest}   

然后我们就可以自动注入RabbitTemplate了。

@Service
public class MessageService {
  @Autowired
  RabbitTemplate rabbitTemplate;
}

这一切的核心,就是自动装配。

3.1 Spring Boot 自动装配原理

Spring Boot依赖spring-boot-autoconfigure,这个jar包是帮助spring boot自动装配所有需要的依赖。

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

创建一个spring boot工程,系统会自动引入这个jar包,查看jar包下的META-INF --> spring.factories文件,会发现默认自动添加了许多模块。比如我们上面讲到的RabbitAutoConfiguration。

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

# 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.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\  # 注意看这里...
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
# 其它的省略

重要的事情说三遍,以下内容为:重点,重点,重点!

Spring Boot在启动时,会解析这些配置文件。我们看到,org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration这个类被加进去了,我们以这个作为列子讲解。

展开spring-boot-autoconfigure下的org.springframework.boot.autoconfigure --> amqp,找到RabbitAutoConfiguration,打开其源代码:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ RabbitTemplate.class, Channel.class }) // 参见下边注释1
@EnableConfigurationProperties(RabbitProperties.class) // 参见下边注释2
@Import(RabbitAnnotationDrivenConfiguration.class)
public class RabbitAutoConfiguration {

        // 其它代码省略...

        @Bean
        @ConditionalOnSingleCandidate(ConnectionFactory.class)
        @ConditionalOnMissingBean(RabbitOperations.class)
        public RabbitTemplate rabbitTemplate(RabbitTemplateConfigurer configurer, ConnectionFactory connectionFactory) {
            RabbitTemplate template = new RabbitTemplate();
            configurer.configure(template, connectionFactory);
            return template;
        }

        @Bean
        @ConditionalOnSingleCandidate(ConnectionFactory.class)
        @ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
        @ConditionalOnMissingBean   // 参见下边注释3
        public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
            return new RabbitAdmin(connectionFactory);
        }

    }
}

可以看到,在这个类里边,我们@Bean注解了RabbitTemplate和AmqpAdmin让Spring管理这两个实例,这就是我们可以在自己的代码中自动注入这两个类实列的原因。

这里需要解析几个注解:

  1. @ConditionalOnClass({ RabbitTemplate.class, Channel.class })
    这个注解的意思是,如果Spring Boot应用程序中包含RabbitTemplate和Channel这两个类,RabbitAutoConfiguration配置文件才生效,否则不解析配置文件。所以当我们引入spring-boot-starter-amqp依赖的时候,这个配置就生效了,Spring Boot就会帮我们创建RabbitTemplate的实例。
  2. @EnableConfigurationProperties(RabbitProperties.class)
    这个注解就是注入RabbitProperties实例,这是一个读取配置文件的的配置类,代码如下:
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitProperties {

    private static final int DEFAULT_PORT = 5672;

    private static final int DEFAULT_PORT_SECURE = 5671;

    /**
     * RabbitMQ host. Ignored if an address is set.
     */
    private String host = "localhost";

    /**
     * RabbitMQ port. Ignored if an address is set. Default to 5672, or 5671 if SSL is
     * enabled.
     */
    private Integer port;

    /**
     * Login user to authenticate to the broker.
     */
    private String username = "guest";
  // 省略其它代码...

这个类就是从我们的yml配置文件中读取Rabbit 连接等配置信息。

  1. @ConditionalOnMissingBean
    这是放在创建Bean的方法注解,意思是如果Amqpadmin这个类存在,就不创建了。这给用户灵活的选择,可以创建自己的Ampqadmin来管理Rabbit MQ,也可以不创建,使用系统默认的。

重点结束,到这里,是不是理解了Spring Boot自动装配的原理了。

四、创建自己的starter

4.1 应用场景

为什么要创建一个spring boot starter项目呢,目的是为了使用方便和代码复用。举个例子,假如你有个监控系统叫monitor-center,很多系统都需要调用这个监控系统获取监控数据。这个监控系统使用OAuth 2.0进行授权的,在使用监控系统前,需要先使用账户密码登录安全中心,获取token,然后使用token访问监控系统。如果token过期,又需要访问安全中心,刷新token。之后是访问监控系统获取监控数据。

上边细节还挺繁琐的,如果每个监控应用都实现一遍,工作任务就太大了,于是我们考虑做一个monitor-boot-starter,包装所有细节。然后提供一个MonitorService给客户使用。

4.2 创建项目

下面开始创建项目:

4.2.1 第一步,新建starter项目

创建一个Spring boot工程,取名monitor-boot-starter,引入如下依赖:

<?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.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.erbadagang.demo</groupId>
    <artifactId>monitor-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>monitor-boot-starter</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-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
    </dependencies>

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

</project>

这里需要注意两个细节:

  1. 命名规范,Spring官方的starter,都叫spring-boot-starter-<模块名称>,我们自己定义的start,都命名为 <模块名称>-boot-starter。
  2. 添加了另一个依赖spring-boot-configuration-processor,并且在plugin的configuration中排除它,这在后边会讲到。

4.2.2 第二步,创建一个config包

添加以下三个类:

package com.erbadagang.demo.monitor.autoconfig;

/**
 * 这是自动装配类,初始化monitor-boot-starter中所有需要用到的bean。
 */
@Configuration
@ConditionalOnClass(MonitorService.class)
@EnableConfigurationProperties(MonitorProperties.class)
public class MonitorAutoConfiguration {
    @Autowired
    private MonitorProperties properties;
  
    @Bean
    @ConditionalOnMissingBean
    public MonitorService monitorService() {
        return new MonitorService(properties);
    }
}
/**
* 这是配置类,用于从application.yml或application.properties中读取配置信息
*/
@ConfigurationProperties(prefix = "demo.monitor")
public class MonitorProperties {
    private String loginUrl;
    private String username;
    private String password;
    private String serverUrl;

    // 省略getter,setter方法...
}
/**
* 这是我们这个应用的核心类,应该在自动装配类中创建。
*/
public class MonitorService {
    private MonitorProperties properties;

    public MonitorService(MonitorProperties properties) {
        this.properties = properties;
    }

    public void subscribe(String url, Consumer<String> callback) {
        System.out.println("login to security center:");
        System.out.println("loginUrl=" + properties.getLoginUrl());
        System.out.println("username=" + properties.getUsername());
        System.out.println("password=" + properties.getPassword());
        System.out.println("connect to monitor:");
        System.out.println("serverUrl=" + properties.getServerUrl());
      
        System.out.println("receive monitor data");
        callback.accept("current time:" + new Date().toString());
        callback.accept("current time:" + new Date().toString());
        callback.accept("current time:" + new Date().toString());
    }

    public void unsubscribe(String url) {
        System.out.println("unsubscribe:" + url);
    }
}

每个类的功能和作用,都做了注释。

4.2.3 第三步,配置spring.factories

在resources目录下,创建META-INF文件夹,然后创建文件spring.factories,添加以下内容。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.erbadagang.demo.monitor.autoconfig.MonitorAutoConfiguration

Spring boot starter启动的时候,这个文件的的内容会被合并到spring-boot-autoconfigure的spring.factories中。这就告诉Spring Boot,系统启动时,除了扫描默认的自动装配类,也扫描com.erbadagang.demo.monitor.autoconfig.MonitorAutoConfiguration这个类。

这个项目已经创建结束了,现在只需要打包安装就可以使用了。使用IDE或者执行以下maven命令:mvn clean compile install

五、使用自定义Spring Boot starter

我们来创建一个项目,使用刚才我们自己创建的monitor-boot-starter。

5.1 第一步,创建Spring Boot工程

取名demo,添加以下依赖:

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

<dependency>
  <groupId>com.erbadagang.demo</groupId>
  <artifactId>monitor-boot-starter</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>

5.2 第二步,在application.yml中,添加以下内容:

demo:
  monitor:
    server-url: https://www.demo-monitor.com
    username: MessiLoveRidingBike
    password: 123456

当你在application.yml中输入demo的时候,是不是还带自动提示功能的,这是咋整的?后续分析... ...

5.3 第三步,使用MonitorService

在DemoApplication.java类中,注入MonitorService并调用subscribe方法。

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {

    @Autowired
    private MonitorService monitorService;

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

    @Override
    public void run(String... args) throws Exception {
        monitorService.subscribe("abc", data -> System.out.println("receive data:" + data));
    }
}

运行程序,得到输入如下:

login to security center:
loginUrl=https://www.demo-login.com
username=MessiLoveRidingBike
password=123456
connect to monitor:
serverUrl=https://www.demo-monitor.com
receive monitor data
receive data:current time:Fri Sep 11 13:53:05 CST 2020
receive data:current time:Fri Sep 11 13:53:05 CST 2020
receive data:current time:Fri Sep 11 13:53:05 CST 2020

六、Spring Boot Configuration Metadata

刚才我们在使用monitor-boot-starter的时候,居然还带自动提示。这是为什么呢,现在来解释一下。

在我们工程monitor-boot-starter的pom.xml中,我们添加了pring-boot-configuration-processor这个依赖,这个依赖的作用是,在编译时,为所有的添加了@ConfigurationProperties的类添加元数据,这些元数据是可以被IDE读到的,我们在application.yml中输入demo的时候,就会自动提示demo.monitor.login-url

<?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.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.erbadagang.demo</groupId>
    <artifactId>monitor-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>monitor-boot-starter</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-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
    </dependencies>

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

</project>

当然根据官方的要求,需要在spring-boot-maven-plugin中排除掉spring-boot-configuration-processor。具体请参见Configuring the Annotation Processor

Configuration Metadata还可以做的更加智能,不但给出智能提示,还把可选项都可以列出来。

七、总结

如果要创建一个spring boot starter,可以参考以下步骤:

  1. 首先需要引入spring-boot-autoconfigure和spring-boot-configuration-processor依赖,后边这个依赖是用来产生配置文件元数据的。
  2. 创建AutoConguration类,这里需要理解两个注解,@ConditionalOnClass(MonitorService.class)和@ConditionalOnMissingBean,第一个注解是放在自动装配类上的,表示只有当MonitorService类存在的时候才执行自动装配。第二个类是放在创建Bean的方法上,表示只有当前类实例不存在的时候才创建,这给用户很大的灵活性。可以自己创建,也可以使用系统默认的Bean。
  3. 创建配置文件类,从application.yml中读取配置文件信息。
  4. 在resources目录下,创建META-INF文件夹,并在该文件夹下创建spring.factories,添加需要自动装配的类。Spring Boot会在启动时,合并spring.factories中的内容。
  5. 最后就是打包安装,然后使用啦。

八、AutoConguration的另类用法

以前,为了实现parent基础项目的某个service被其他业务类项目复用,使用了类似starter的解决方案,实现公用逻辑可以在被其他项目通过@Autowired自动装配。
方案是按照自己想法写的没有遵循Spring Boot Starter规范,但是达到了同样的目的,可以说是轻量级的starter,供大家以后工作中参考。

业务逻辑:parent项目——pay_common编写邮件发送逻辑,后续各个项目使用时直接POM引入pay_common依赖,配置yml后通过@Autowired使用,来避免各个项目都造轮子。

8.1 parent项目——pay_common

编写一个普通的@Service类来实现邮件发送功能

package com.erbadagang.pay.common.service;

import com.erbadagang.pay.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.messaging.MessagingException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.mail.internet.MimeMessage;

/**
 * @ClassName: MailService
 * @Description:邮件发送service
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020年1月18日 下午1:37:09
 * @Copyright:
 */
@Service //这里使用了Service注解,没有使用starter的标准用法注解。
@Slf4j
public class MailService {

    @Autowired
    private JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String from;

    @Value("${spring.mail.toUser}")
    private String toUser;

    @Value("${spring.mail.ccUsers}")
    private String ccUsers;

    @Value("${spring.mail.bccUsers}")
    private String bccUsers;

    @Async
    public void sendMail(String subject, String text) throws MessagingException, javax.mail.MessagingException {
        log.info("开始发送邮件通知,邮件内容为:[{}]", text);
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper mMessageHelper = new MimeMessageHelper(message, true, "UTF-8");
        // 发件人邮箱
        mMessageHelper.setFrom(from);
        // 收件人邮箱
        mMessageHelper.setTo(toUser);
        // 邮件的主题也就是邮件的标题
        mMessageHelper.setSubject(subject);
        mMessageHelper.setText(text, true);
        if (StringUtils.isNotEmpty(ccUsers)) {
            mMessageHelper.setCc(ccUsers.split(","));
        }
        if (StringUtils.isNotEmpty(bccUsers)) {
            mMessageHelper.setBcc(bccUsers.split(","));
        }
        mailSender.send(message);
        log.info("发送邮件通知成功,邮件内容为:[{}]", text);
    }

}

说明:
没有写AutoConfiguration类,直接写Service了。所以没有下面的AutoConfiguration注解:

@Configuration
@ConditionalOnClass(MailService.class) //正常我们依赖MailService触发自动装配
@EnableConfigurationProperties(MonitorProperties.class) //直接在Service类里面使用@Value注解读取配置文件了。

8.2 配置AutoConfiguration

编写spring.factories (也是Properties)文件。

spring.factories
加上com.erbadagang.pay.common.service.MailService ,来让依赖这个项目的其他Springboot项目自动扫描MailService进行自动装配:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.erbadagang.pay.common.autoconfig.schedlock.ShedlockConfig,\
com.erbadagang.pay.common.service.MailService

8.3 使用方引入依赖

业务项目的pom.xml引入parent项目——pay_common的依赖。

        <dependency>
            <groupId>com.pay.parent</groupId>
            <artifactId>pay_common</artifactId>
        </dependency>

8.4 @Autowired MailService

业务项目使用MailServic的逻辑如下:

package com.erbadagang.pay.controller;

import com.erbadagang.pay.common.message.JsonResult;
import com.erbadagang.pay.common.service.MailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessagingException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName: RestTemplateController
 * @Description:mail邮件发送。
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020年1月14日 下午12:44:08
 * @Copyright:
 */
@RestController
@Slf4j
@RequestMapping(value = "/mail")
public class MailController {

    @Autowired
    private MailService mailService;

    @RequestMapping(value = "/send")
    public JsonResult<String> sendMail() throws MessagingException, javax.mail.MessagingException {
        mailService.sendMail("邮件发送by springboot ", "邮件正文,通过spring-boot-starter-mail");
        log.info("发送邮件完成");
        System.out.println();
        return JsonResult.of("发送邮件完成", true, "成功调用");
    }

}

8.5 配置信息

业务项目在yml文件配置Mail的信息


spring:
  application:
    name: erbadagang-payment-settlement
  mail:
    host: ${MAIL_HOST:mail.guo.com.cn}
    username: ${MAIL_USERNAME:guoxiuzhi}
    password: ${MAIL_PASSWORD:!QA12s32t4LKJ45f0}
    toUser: jbcode@126.com
    ccUsers:
    bccUsers:
    protocol: smtp
    properties:
      mail:
        smtp:
          starttls:
            enable: true

8.6 结论

综上自己“发明”了一个简单的Spring Boot Starter,并使用之。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343