美文网首页javaWeb学习
自定义Spring Boot Starter

自定义Spring Boot Starter

作者: 梅西爱骑车 | 来源:发表于2020-10-07 08:11 被阅读0次

    一、起源

    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,并使用之。

    相关文章

      网友评论

        本文标题:自定义Spring Boot Starter

        本文链接:https://www.haomeiwen.com/subject/igqtpktx.html