添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Repository files navigation

Spring-boot demo

JSP支持

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId><!--支持jsp格式-->
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId><!--支持jsp一些语法-->
    <version>1.2</version>
    <scope>compile</scope>
</dependency>

application.yml 相关配置

spring:
  mvc:
    view:
      prefix: /WEB-INF/jsp/
      suffix: .jsp

Config 文件需要继承 SpringBootServletInitializer 重写 configure() 方法

统一异常处理@ControllerAdvice

  • Mustache
  • 当你使用上述模板引擎中的任何一个,它们默认的模板配置路径为: src/main/resources/templates 。当然也可以修改这个路径,具体如何修改,可在后续各模板引擎的配置属性中查询并修改。

    Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则:

  • /static
  • /public
  • /resources
  • /META-INF/resources
  • 举例:我们可以在 src/main/resources/ 目录下创建 static ,在该位置放置一个图片文件。启动程序后,尝试访问 http://localhost:8080/d.jpg 。如能显示图片,则配置成功。PS:在此目录下放入 favicon.ico ,页面会自动加载图标。

    在Spring Boot中使用Thymeleaf,只需要引入下面依赖,并在默认的模板路径 src/main/resources/templates 下编写模板文件即可完成。

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

    更多Thymeleaf的页面语法,还请访问Thymeleaf的官方文档查询使用。

    统一异常处理

    创建全局异常处理类:通过使用 @ControllerAdvice 定义统一的异常处理类,而不是在每个 Controller 中逐个定义。 @ExceptionHandler 用来定义函数针对的异常类型,最后将 Exception 对象和请求URL映射到 error.html

    @ControllerAdvice
    public class GlobalExceptionHandler {
        public static final String DEFAULT_ERROR_VIEW = "error/500";
        @ExceptionHandler(value = RuntimeException.class)// 用来定义函数针对的异常类型
        public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
            ModelAndView mav = new ModelAndView();
            mav.addObject("url", req.getRequestURL());
            mav.addObject("exception", e);
            mav.setViewName(DEFAULT_ERROR_VIEW);// 将Exception对象和请求URL映射到error.html中
            return mav;
    

    通过@ControllerAdvice统一定义不同Exception映射到不同错误处理页面。而当我们要实现RESTful API时,返回的错误是JSON格式的数据,而不是HTML页面,这时候也能轻松支持。本质上,只需在@ExceptionHandler之后加入@ResponseBody,就能让处理函数return的内容转换为JSON格式。

    Web flux

    Reactor 本质就是: jdk8 stream + jdk9 reactive stream

    Reactive Programming 作为观察者(Observer)的延伸,不同于传统的命令编程方式(Imperative programming)同步拉取数据的方式,如迭代器模式(Iterator)。而是采用数据发布者同步或异步地推送到数据流(Data Streams)的方案。当该数据流订阅者监听到传播变化时,立即作出响应动作。在实现层面上,Reactive Programming可结合函数式编程简化面向对象语言语法的臃肿性,屏蔽并发实现的复杂细节,提供数据流的有序操作,从而达到提升代码的可读性,以及减少Bug出现的目的。同时,Reactive Programming结合背压(Backpressure)的技术解决发布生成数据的速率高于订阅端消费的问题

    添加依赖:

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

    webFlux支持通过Controller方式来进行调用,也可以通过Router方式进行调用:

    Annotated Controller 注解驱动

    TestController中,举例说明了通过servlet的阻塞I/O 和 reactive stream非阻塞的差别,可以提高吞吐量(但并不能解决性能问题),以及MonoFlux的区别。UserController列举了Flux的常规用法。

    补充:服务事件发送 server-sent events(适用于服务器向前端推送数据的场景):

    需要设置响应的Content-Type="text/event-stream"

    设置编码类型CharacterEncoding=utf-8

    指定事件标识:event:${eventName}\n

    事件内容:

    格式:data + 数据内容 + 2个回车

    栗子:data:${dataContent}\n\n

    var sse = new EventSource("SSE"); // 传入请求地址url sse.onmessage = function(e){ console.log("message:", e.data, e); sse.addEventListener("${eventName}", function(e){ console.log("listen event:", e.data); // 满足条件后断开,否则会继续重连 if(e.data == 3) { sse.close(); });

    Router Functions 开发模式

  • 编写 HandlerFunction (输入ServerRequest, 返回ServerResponse),举例 UserHandler
  • 定义 RouterFunction (将请求URL 和 HandlerFunction 对应起来),举例 AllRouters
  • 之后会交由Spring的 HttpHandler -> Server (Netty或Servlet3.1+)处理
  • 响应式在网页请求下,看到差别并不是很大,它更多适用于服务器之间的Rest调用,异步非阻塞特性才能更好的呈现出来,更有价值。

    服务之间的Rest调用参考: IUserApi

    WebSocket

    pom.xml 加入如下依赖集成 WebSocket:

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

    创建一个 WebSocket 配置类 WebSocketConfiguration ,在配置类上加入注解 @EnableWebSocket ,表明开启 WebSocket,内部实例化 ServerEndpointExporter 的 Bean,该 Bean 会自动注册 @ServerEndpoint 注解声明的端点,代码如下:

    @Configuration
    @EnableWebSocket
    public class WebSocketConfiguration {
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
    

    端点服务类

    使用 @ServerEndpoint 定义一个端点服务类,在端点服务类中,可以定义 WebSocket 的打开@OnOpen、关闭@OnClose、错误@OnError和接收客户端消息@OnMessage的方法,具体参考:WsServerEndpoint

    向具体客户端推送消息

    不同客户端访问时,会为每个客户端生成自己的端点实例,所以要想为每个客户端推送不同的消息,需要记录下每个客户端对应的端点实例,参考WsClientsManager。之后,根据不同客户端获取到对应实例中的session来进行sendMsg。

    依赖容器对象

    因为Spring容器中的对象采用单实例方式,不同客户端连接时会生成不同的WebSocket对象,无法按照对象方式来直接使用容器对象,可以把容器对象定义为WebSocket的类属性。在容器启动时,注册类属性,其后生成的对象都可以使用容器对象。

    仅需要添加devtools依赖:

    <!--支持热部署,devtools会监听classpath下的文件变动,并且会立即重启应用(发生在保存时机),注意:因为其采用的虚拟机机制,该项重启是很快的-->
    <!--Intellij要想支持还需要进一步设置,自行google-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <!-- optional=true,依赖不会传递,该项目依赖devtools;之后依赖myboot项目的项目如果想要使用devtools,需要重新引入 -->
      <optional>true</optional>
    </dependency>
    <plugins>
      <plugin>
        <!--用于将应用打成可直接运行的jar-->
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!--支持热部署-->
        <configuration>
          <fork>true</fork>
        </configuration>
      </plugin>
      <!-- spring Boot在编译的时候,是有默认JDK版本的,如果我们期望使用我们要的JDK版本的话,那么要配置呢 -->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>

    Swagger2

    Swagger2它可以轻松的整合到Spring Boot中,并与Spring MVC程序配合组织出强大RESTful API文档。它既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明。

    首先添加相关依赖:

    <!--Swagger2依赖-->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.7.0</version>
    </dependency>
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.7.0</version>
    </dependency>

    其次,添加Swagger2相关依赖配置Swagger2Config

    在完成了上述配置后,其实已经可以生产文档内容,但是这样的文档主要针对请求本身,而描述主要来源于函数等命名产生,对用户并不友好,我们通常需要自己增加一些说明来丰富文档内容。如下所示,我们通过@ApiOperation注解来给API增加说明、通过@ApiImplicitParams@ApiImplicitParam注解来给参数增加说明。具体使用参看示例

    启动Spring Boot程序,访问:http://localhost:8080/swagger-ui.html

    Event

  • 事件驱动模型也就是我们常说的观察者,或者发布-订阅模型。
  • 事件驱动首先是一种对象间的一对多的关系,当目标发生改变(发布),观察者(订阅者)就可以接收到改变,观察者如何处理(如行人如何走,是快走/慢走/不走,目标红绿灯不会管的),目标无需干涉,所以就松散耦合了它们之间的关系。
  • Spring 事件 - 监听器(同步/异步):

  • 普通应用事件:ApplicationEvent
  • 应用上下文事件:ApplicationContextEvent
  • 事件监听器:
  • 接口编程模:ApplicationListener
  • 注解编程模型:@EventListener
  • 事件发布器:ApplicationEventPublisher
  • 事件广播器:
  • 接口:ApplicationEventMulticaster
  • 实现类:SimpleApplicationEventMulticaster
  • 执行模式:同步或异步
  • 事件发布,实际是调用AbstractApplicationContext.publishEvent(event),其内部调用了AbstractApplicationEventMulticaster获取对应的Listener(如果Listener是SmartApplicationListener类型,对应的Listener的获取是根据EventTypesourceType去决定的),由相应的Listener执行onApplicationEvent(event)

    Spring自己是支持异步方式处理事件(通过新的线程方式)通过,@Async @EventListener

    想采用其他方式的异步响应处理,如MQ方式,可以实现ApplicationListener,实现onApplicationEvent(event)方法,该方法判断是否开启异步enableAsync(),来采用自定义异步执行器public MQ asyncExecutor()。

    使用Spring event步骤:

    定义需要监听的事件

    定义监听器Listener有三种方式

    无序监听器ApplicationListener(监听器事件过多,具体执行顺序是不保证的)

    有序监听器SmartApplicationListener

    使用注解@EventListener

    重试一个失败的操作有时是必要的,Spring Batch 提供了重试策略,添加相关依赖

    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
        <version>1.1.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

    Config类中添加@EnableRetry开启重试支持

    重试方式有两种:

  • 使用注解重试
  • 使用Template重试
  • Redis及缓存

    添加redis依赖

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

    如需要开启缓存支持,需要在Config类中增加@EnableCaching注解开启缓存功能。

    application.yml中添加redis相关配置

    RedisTemplate及其序列化方式设置,参考RedisConfig

    @CacheConfig相关使用参考CacheService

    Json序列化

  • Jackson相关使用介绍
  • Jackson相关demo
  • 添加相关依赖

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

    测试用例类前需要添加@RunWith(SpringRunner.class)@SpringBootTest,如若有数据库相关测试用例需加@Transactional测试完成后进行回滚

    若是需要对JPA接口进行单元测试,可以使用@DataJpaTest

    Controller接口测试

    Mybatis

    添加依赖(使用的MySQL)

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.1.1</version>
    </dependency>

    关于数据库连接相关设置

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/msg?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
        username: root
        password: root

    dao层接口需要加@Mapper注解,之后spring会自动扫描到

    具体sql写入与dao层接口同名的XML文件

    补充:由于IntelliJ IDEA的原因,放在java目录下xml文件最后不会被编译,导致运行时找不到dao的实现接口,解决方法在pom.xml添加:

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>

    多数据源配置

    a.多数据库yml配置

    demo:
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver
        slave:
          url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver

    b.相关config,具体代码

    * 配置多数据源 @Configuration public class DataSourceConfig { * 主数据源 @Primary @Bean(name = "masterDataSource") @Qualifier("masterDataSource") @ConfigurationProperties(prefix="demo.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); * 从数据源 @Bean(name = "slaveDataSource") @Qualifier("slaveDataSource") @ConfigurationProperties(prefix="demo.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); * 主数据源详情配置 @Configuration // 制定分库的mapper文件地址,以及分库到层代码 @MapperScan(basePackages = "com.gtw.mybatis.repository.mapper.master", sqlSessionTemplateRef = "masterSqlSessionTemplate") public class MasterDataSourceConfig { * 创建SqlSessionFactory @Primary @Bean(name = "masterSqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); // sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() // .getResources("classpath*:mapper/master/*.xml")); return sessionFactoryBean.getObject(); * 创建事务管理 @Primary @Bean(name = "masterTransactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); * 将SqlSessionFactory包装到SqlSessionTemplate中 @Primary @Bean(name = "masterSqlSessionTemplate") public SqlSessionTemplate sqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory);

    此种方式指明哪个包下dao接口或配置文件走哪个数据库,缺点很明显,相同的dao接口和配置文件要复制多份到不同包路径下,不易维护和扩展。

    具体使用主从分离的读写方式,可以参看mybatis-master-slave

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>

    对象实体常用注解使用

    JPA提供了@CreatedDate等审计功能,自动生成创建时间、修改时间、创建人等,开启审计功能需要在Config文件中加@EnableJpaAuditing,参考审计示例

    仓储层Repository接口不需要进行实现,只要继承Repository就好(还有其他CrudRepository等等),具体可参考仓储层接口定义

    分页功能与模糊查询功能可参考分页与模糊查询

    其他功能,如需请参考官方文档

    Querydsl

    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <version>${querydsl.version}</version>
    </dependency>
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-core</artifactId>
        <version>${querydsl.version}</version>
    </dependency>
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-collections</artifactId>
        <version>${querydsl.version}</version>
    </dependency>

    工程构建时需要引入插件,这样会在工程模块的target/generated-sources/java对应目录下会生成相应的查询类QClass

    <build>
        <plugins>
            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <!-- If you are not using JPA or JDO -->
                            <outputDirectory>target/generated-sources/java</outputDirectory>
                            <processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    集合查询的querysql使用,参考Querydsl使用

    Jpa集成Querysql,引入依赖与插件

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--querydsl支持-->
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>${querydsl.version}</version>
            <classifier>apt</classifier>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <dependencies>
                    <dependency>
                        <groupId>com.querydsl</groupId>
                        <artifactId>querydsl-apt</artifactId>
                        <version>${querydsl.version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <!--JPAAnnotationProcessor查找使用javax.persistence.Entity注释注释的域类型,并为它们生成查询类型-->
                            <!--<outputDirectory>target/generated-sources/java</outputDirectory>-->
                            <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    Repository层使用QueryDsl,需要继承QueryDslPredicateExecutor,参考ProductRepository

    也可用使用JPAQueryFactory,参考OrderRepositoryCustomer

    Oauth2

    Oauth2相关介绍理解,参考本人博客理解Oauth2

    Oauth2 Server

    <!--security-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!--oauth2-->
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
    </dependency>
    <!--redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    关于Oauth2ServerConfig请参考SecurityConfig

    用户名和密码的校验,需要指定UserServiceUserService需要继承UserDetailsService

    指定Token的缓存方式

    指定密码的加密方式

    设置AuthenticationManager

    认证服务配置

    资源服务配置

    自定义Starter

    Spring Boot Starter 是一个 Maven 或 Gradle 模块,其唯一目的是提供 "启动" 某个特性所需的所有依赖项。可以包含一个或多个 Auto-Configure Module (自动配置模块)的依赖项,以及可能需要的任何其他依赖项。这样,在Spring 启动应用程序中,只需要添加这个 starter 依赖就可以使用其特性。

    通常一个完整的 starter 需要包含下面两个组件:

  • Auto-Configure Module
  • Starter Module
  • 来自 Spring 官方的 starter 都是 以 spring-boot-starter 开头,比如:

  • spring-boot-starter-web
  • spring-boot-starter-aop
  • 如果我们自定义 starter 功能名称叫acme,那么我们的命名是这样的:

    acme-spring-boot-starter

    acme-spring-boot-autoconfigure

    如果 starter 中用到了配置 keys,也要注意不要使用 Spring Boot 使用的命名空间。

    Parent Module 创建

    先来全局看一下项目结构:

    一级目录结构:

    ├── pom.xml ├── gtw-spring-boot-autoconfigure ├── gtw-spring-boot-sample └── gtw-spring-boot-starter

    二级目录结构:

    ├── pom.xml ├── gtw-spring-boot-autoconfigure │ ├── pom.xml │ └── src ├── gtw-spring-boot-sample │ ├── pom.xml │ └── src └── gtw-spring-boot-starter ├── pom.xml └── src

    创建一个空的父亲 Maven Module,主要提供依赖管理,这样 SubModule 不用单独维护依赖版本号:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    Auto-Configure Module 构建

    Maven依赖

    <dependencies>
        <!-- Compile dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <!-- 属性是要在 application.yml 中使用的,当需要使用这些属性时,为了让ID 给出更友好的提示,需要在 pom.xml 中添加依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        对于类路径上的每个自动配置类,Spring Boot 必须计算 @Conditional… 条件值,用于决定是否加载自动配置及其所需的所有类,
        根据 Spring 启动应用程序中 starter 的大小和数量,这可能是一个非常昂贵的操作,并且会影响启动时间,
        为了提升启动时间,需要在 pom.xml 中添加一个依赖.
        这样,Spring Boot 在启动期间读取这些元数据,可以过滤出不满足条件的配置,而不必实际检查这些类,提升启动速度
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    配置属性管理

    @Data
    @ConfigurationProperties(prefix = "gtw.greeting")
    public class GtwProperties {
         * GreetingProperties 开关
        boolean enable = false;
         * 需要打招呼的成员列表
        List<String> members = new ArrayList<>();
    

    新建AutoConfiguration

    @Configuration 注解标记类 AutoConfiguration,作为 starter 的入口点。这个配置包含了需要提供starter特性的所有 @Bean 定义:

    @Configuration
    @EnableConfigurationProperties(GtwProperties.class)
    @ConditionalOnProperty(value = "gtw.greeting.enable", havingValue = "true")
    @ConditionalOnClass(DefaultSqlSession.class)
    public class GtwAutoConfiguration {
        @Bean
        public GreetingService greetingService(GtwProperties gtwProperties){
            return new GreetingService(gtwProperties.getMembers());
    

    在 resources 目录下新建文件 META-INF/spring.factories ,向文件写入内容:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
     com.gtw.spring.boot.autoconfigure.GtwAutoConfiguration

    Spring 启动时会在其 classpath 中所有的 spring.factoreis 文件,并加载里面的声明配置,AutoConfiguration 类就绪后,我们的 Spring Boot Starter 就有了一个自动激活的入口点。

    关于条件配置,参考 @Conditional注解,灵活配置 Spring Boot

    补充:Spring boot自动装配说明

    是一种用于声明在应用中扮演“组件”角色的注解,实现单个功能。当任何组件标注它时,被视作组件扫描的候选对象。

    @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc {
    // 加载单一配置内容,只有一种模式
    @Configuration
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    

    接口编程方式

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    @Import({CacheConfigurationImportSelector.class})
    public @interface EnableCaching {
    
    static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        // 可条件化,支持多种模式的配置
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];
            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            return imports;
    

    允许在Bean装配时增加前置条件判断

    Spring注解
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional({OnClassCondition.class}) // 实现方式在这里
    public @interface ConditionalOnClass {
        Class<?>[] value() default {};
        String[] name() default {};
    
    Spring Boot 自动装配

    底层装配技术

  • Spring 模式注解装配
  • Spring @Enable模块装配
  • Spring 条件装配
  • Spring 工厂加载机制
  • 实现类:SpringFactoriesLoader
  • 配置资源:META-INF/spring.factories
  • Starter Module 构建

    Starter Module 构建很简单,可以认为它就是一个空 module,除了依赖 Auto-Configure Module,其唯一作用就是为了使用 starter 功能特性提供所有必须依赖,所以为 starter module 的 pom.xml 文件添加如下内容:

    <dependencies>
        <dependency>
            <groupId>com.gtw.starter</groupId>
            <artifactId>gtw-spring-boot-autoconfigure</artifactId>
            <version>${gtw.autoconfigure.version}</version>
        </dependency>
        <!-- 在此处添加其他必要依赖,保证starter可用 -->
    </dependencies>

    同样在 resources 目录下新建文件 META-INF/spring.providers , 其内容如下:

    providers: gtw-spring-boot-autoconfigure
    

    该文件主要作用是说明 starter module 的依赖信息,多个依赖以逗号分隔就好,该文件不会影响 starter 的使用,可有可无。

    Sample Module 构建

    可以通过 Spring Initializr 正常初始化一个 Spring Boot 项目 (gtw-spring-boot-sample),引入刚刚创建的 starter 依赖,在 sample pom.xml 中添加依赖:

    <dependencies>
        <!-- spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- gtw-starter -->
        <dependency>
            <groupId>com.gtw.starter</groupId>
            <artifactId>gtw-spring-boot-starter</artifactId>
            <version>${gtw.starter.version}</version>
        </dependency>
    </dependencies>

    配置 application.yml 属性:

    gtw:
      greeting:
        enable: true
        members:
          - 韩梅梅

    使用测试:

    @SpringBootApplication
    public class SampleApplication {
        @Autowired
        private GreetingService greetingService;
        @PostConstruct
        public void sayHello(){
            greetingService.sayHello();
        public static void main(String[] args) {
            SpringApplication.run(SampleApplication.class, args);