zoukankan      html  css  js  c++  java
  • 如何基于Spring Boot搭建一个完整的项目

    前言

    使用Spring Boot做后台项目开发也快半年了,由于之前有过基于Spring开发的项目经验,相比之下觉得Spring Boot就是天堂,开箱即用来形容是绝不为过的。在没有接触Spring Boot 之前,以为Spring Boot 是一个新的框架体系。正好Spring Boot出现先的时候,也是微服务特别火的时候,大家不约而同把Spring Boot 和微服务等同起来了,但其实并不是如此,那么到底什么是Spring Boot ? 基于Spring Boot 又是如何开发的呢 ? 带着这两个问题,大家继续往下看。

    Spring Boot 特点

    Our primary goals are:

    1. Provide a radically faster and widely accessible getting started experience for all Spring development.
    2. Be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults.
    3. Absolutely no code generation and no requirement for XML configuration.

    上述是从Spring Boot官方文档上摘录的,简述言之Spring Boot框架的目的就是开箱即用、去除配置。Spring Boot并不是重复去造就一个轮子,它的出现是为Spring的开发带来一种全新的体验,从而大大的降低Spring框架的使用门槛。

    下面主要从几个特性介绍Spring Boot是如何简化开发的。

    Spring Boot启动器

    使用过Spring Boot的同学都知道,快速编写一个Spring Boot的Web Demo需要在pom文件中加入如下:

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

    Spring Boot 提供了很多像spring-boot-starter-web这样的启动器,名称都是spring-boot-starter-* 。

    这里写图片描述

    截图部分来自Spring Boot官网,只是Spring Boot启动器中小部分,更多启动器可以到官网上查找。

    启动器为何物? 以spring-boot-starter-web 这个web开发用到的启动器为例。

    这里写图片描述

    从上图可以看出spring-boot-starter-web.jar包里面没有任何代码,对没有任何代码。只有一个pom文件。what ? 其中包中pom.xml的内容如下所示:

    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-tomcat</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.hibernate</groupId>
    		<artifactId>hibernate-validator</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>com.fasterxml.jackson.core</groupId>
    		<artifactId>jackson-databind</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-webmvc</artifactId>
    	</dependency>
    </dependencies>
    

    以前在基于Spring框架下进行web开发通常需要在模块的pom文件中引入spring-webmvc、spring-web、jackson、tomcat等依赖模块。如果使用Spring Boot的启动器,只需要引入spring-boot-starter-web模块就可以。看到这里聪明的读者应该明白了为嘛启动器的包中没有代码只有pom文件了吧。 简言之Spring Boot的启动器就是将不同的第三方组件依赖进行了套件封装,为开发Spring应用提供了一个易用的套件库。开发者再也不用在pom中引入那么多模块了,一个套件搞定一个相关的功能所需要的所有依赖库,这个体验很棒吧。

    继续解锁下一个Spring Boot的牛逼特性。
    

    内嵌Web容器(Tomcat或者Jetty)

    早期基于Spring框架开发的时候,Web容器是不可以缺少的一个步骤。通常把代码编译成war包,然后扔到tomcat容器中进行运行。 这时候需要单独的下载tomcat容器,有点小麻烦。Spring Boot直接把通过内嵌web容器,直接运行。 那么Spring Boot是如何内嵌的web容器的呢? 其中以tomcat容易为例。

    上面介绍启动器的时候,有介绍spring-boot-starter-web启动器,这是进行web开发的时候需要在pom中引入的模块。进入到这个启动器的内部pom文件,如下描述:

    <description>
    Starter for building web, including RESTful, applications using 
    Spring MVC. Uses Tomcat as the default embedded container
    </description>
    
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-tomcat</artifactId>
    </dependency>
    

    看了spring-boot-starter-web启动器的内部pom描述,很容易明白。tomcat是通过spring-boot-starter-web启动器引入的,并且在Spring Boot 中默认的Web容器是tomcat,当然也可以换成更轻量级的Jetty,这里不做介绍 。关键在于spring-boot-starter-tomcat 这个tomcat启动器,引入它之后,所有tomcat相关的jar包都引入进来了。在启动Spring Boot服务的时候,就会调用tomcat容器的启动。为了提高点文章的逼格来点源码分析。

    在启动Spring Boot的时候,控制台的日志打印其实很好的告知了我们Spring Boot在启动的时候,框架做了哪些事情。选出两条和Tomcat相关的日志:

    s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8094 (http)
    s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8094 (http)
    

    通过日志发现了一个类 TomcatEmbeddedServletContainer,搜寻到这个类属spring-boot.jar包里面的。通过下图所示的spring-boot-1.5.9.RELEASE.jar包目录结构和包名很容易猜测到Web容器的启动就是这个包来控制的。

    这里写图片描述

    核心接口 EmbeddedServletContainer,其中三个容器分别对接口进行了实现,对方法进行了重写。 接口代码如下:

    public interface EmbeddedServletContainer {
    /**
     * Starts the embedded servlet container.
     */
    void start() throws EmbeddedServletContainerException;
    
    /**
     * Stops the embedded servlet container
     */
    void stop() throws EmbeddedServletContainerException;
    
    /**
     * Return the port this server is listening on.
     */
    int getPort();
    }
    

    其实除了内嵌web容器的启动和spring-boot包有关外,整个Spring Boot的服务启动,都是在这个包中实现的,感兴趣的同学可以跟这个包死磕到底,深入下去。

    完全去配置文件化

    Spring Boot之所以号称降低了Spring的开发门槛其实是有杀手锏的,完全去配置化就是重量级的杀手锏。 早起完成Spring的开发,最烦的就是各种xml配置文件。在Spring Boot的项目工程中确实看不到了那些烦人的xml,它们到哪去了?

    回到Spring Boot的项目工程,其中还有两处变化的没有介绍。

    • Resource资源文件下的四个属性文件。application.properties 、application-dev.properties、application-prd.properties、application-stg.properties。
    • spring-boot-autoconfigure-*.jar(省去了版本号).

    属性文件这里就不介绍了,配置相关的属性的地方,根据不同的环境(测试、开发、生产)提供了不同名称的属性文件。

    spring-boot-autoconfigure-*.jar 很明显从名字就知道,自动配置相关的jar包。没错,Spring Boot的属性自动配置全部都是由这个包实现的。

    这里写图片描述

    打开spring-boot-autoconfigure-*.jar,包名称就告诉了我们一切。在这个包中集成了我们大部分平时开发需要的中间件,比方说: amqp 消息中间件、cache缓存中间件、data数据持久化相关的中间件。基于Spring的框架,如果要集成各种常用中间件, 少不了一个使用了 @Configuration注解的配置类和各种属性配置。但是使用Spring Boot框架,@Configuration注解的配置类,全部不需要了。只要在application.properties 配置属性值就可以直接使用。

    这里把集成Redis作为一个例子。查看spring-boot-autoconfigure-*.jar 中data包目录下的redis包可以看到几个类,其中重点在于RedisAutoConfiguration 和 RedisProperties 这两个类。

    • RedisProperties类
      这里写图片描述

    通过 @ConfigurationProperties(prefix = "spring.redis")注解,把在application.properties 中配置的redis 相关的名称为spring.redis.*的属性全部注入到 RedisProperties类的属性变量中。

    • RedisAutoConfiguration类

    在这个类中主要定义了几个Bean, 主要是JedisConnectionFactory 和 JedisPoolConfig、StringRedisTemplate等。以前我们基于Spring框架,需要自己定义Bean全部都被Spring Boot框架自己定义了,其他的中间件也是如此。 再结合Spring Boot中使用的是3.0+版本的Servlet包,使用注解完成各种Listener、Filter、Servlet 以及Bean的定义,让无xml化不是梦了。

    思考: 如何修改基于框架下的自动配置的属性值?

    完全依赖框架的自动配置,其实也是件蛮不安全的事情。有次生产环境上,redis一直抛出一个异常。但是没多久又好了,持续了很多天。从异常可以判断是客户端使用了redis连接池提供的无效链接。通过Jedis的源码可以知道,使用的连接池是apache-common-pool2组件。其中相关的参数有BaseObjectPoolConfig类中的

    private boolean testOnBorrow = false;
    private boolean testWhileIdle = false;
    

    这两个参数值默认都是false。其中testOnBorrow 代指获取连接池的连接前,检查链接的有效性。 testWhileIdle 是空闲的时候检查链接的有效性。 需要把这两个参数都设置为true。因为默认的自动配置值都是false,但是这两个属性值又不在RedisProperties类中,所以不能通过application.properties文件来修改。那怎么修改呢? 现在项目重写了JedisConnectionFactory 和 JedisPoolConfig、StringRedisTemplate这三个Bean,通过重写Bean来修改属性值。异常没有了,但是总觉得这不是最好的方式,大家如果有更好的方式,欢迎留言,谢谢!

    等等: 说好的是工程介绍,怎么全是文字,自己都不想看了。好吧,回到正题,搭建基于Spring Boot的工程项目需要哪些东西?

    整个项目的结构

     <modules>
        <module>pa-market-update</module>
        <module>pa-market-delete</module>
        <module>pa-market-common</module>
        <module>pa-market-add</module>
        <module>pa-market-schedule</module>
    </modules>
    

    完整的项目一般都是根据业务进行功能划分的。上面是主pom文件的内容,common是公共模块,schedule是定时任务模块(每个项目应该都少不了定时任务吧),其他的各自业务模块,名字随便取的。

    单个模块下包目录结构

    这里写图片描述

    在前后端分离的项目中,通常要求基于Restful风格编写接口,基于这种风格,项目每个子模块的包目录结构其实是可以固定。

    • aspect 包: 基于AOP的拦截器一般都放在这个包。
    • consts包 : 常量包
    • dto包:表现层和业务层的中间产物
    • entity包: 对应表结构的实体类
    • service包: 业务层,主要实现业务逻辑的Service类
    • utils包: 工具类
    • web包: Controller控制层,也是一个项目的入口,通过它可以知道有多少接口
    • Application 类: Spring Boot的启动类,注意它放的位置是有讲究的

    集成Mybatis框架

    DAO层在一个项目中是必不可以少的,在这里采用常用的ORM框架Mybatis。 在pom文件中需要加入的模块。

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

    mybatis-spring-boot-starter这个启动器不是Spring Boot官方提供的,是Mybatis团队自己集成的,所以名字跟官方的启动器名字不一样。

    再加上 @Mapper注解的Mapper类和Mapper.xml文件,就可以了。当然少不了在application.properties中配置数据库的基本信息。使用ORM框架就这么简单。其他第三方框架也是如此,这里就不重复了。

    日志框架

    日志打印是一个项目必不可少的部分,在这里介绍log4j2的日志框架。

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

    log4j2不支持属性文件,只支持xml文件或者json文件。这里介绍xml文件的配置。

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="INFO" packages="com.pa.market" >
    <appenders>
    	<!--这个输出控制台的配置 -->
    	<Console name="Console" target="SYSTEM_OUT">
    		<PatternLayout pattern="%d{HH:mm:ss} %-5p - %msg%n" />
    		<!-- 设置级别 -->
    		<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="NEUTRAL" />
    		<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
    		<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY" />
            <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
    	</Console>
    	<RollingFile name="RollingFile" fileName="/data/logs/market/market-inquiry.log"
    		filePattern="/data/logs/market/market-inquiry.%d{yyyy-MM-dd}.%i.log">
    		<PatternLayout>
    			<charset>UTF-8</charset>
    			<Pattern>%d{HH:mm:ss} %-5p - %msg%n</Pattern>
    		</PatternLayout>
    		<Policies>
    			<TimeBasedTriggeringPolicy />
    			<SizeBasedTriggeringPolicy size="256 MB" />
    		</Policies>
    	</RollingFile>
    </appenders>
    <loggers>
         <!-- 将业务dao接口填写进去,并用控制台输出即可 -->  
        <logger name="com.pa.market.common.mapper" level="DEBUG" additivity="false">  
            <appender-ref ref="Console"/>  
            <appender-ref ref="RollingFile" />
        </logger>
    	<AsyncRoot level="info">
    		<appender-ref ref="Console" />
    		<appender-ref ref="RollingFile" />
    	</AsyncRoot>
    </loggers>
    </configuration>
    

    如果需要打印Mybatis框架中的日志,可以配置 loggers 标签。每个标签的含义,在官网上可以查询到,这里不做介绍。

    打包部署

    Spring Boot 框架下如果想打包相应的模块代码,需要在资源文件下添加一个assembly.xml文件。文件内容如下:

    <assembly  
    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">  
    <id>release</id>  
    <formats>  
        <format>tar.gz</format>  
    </formats>  
    <fileSets>   
        <fileSet>  
            <directory>${project.basedir}\src\main\resources</directory>  
            <outputDirectory>conf</outputDirectory>  
        </fileSet> 
        <fileSet>  
            <directory>${project.basedir}\src\script</directory>  
            <outputDirectory>script</outputDirectory>  
            <fileMode>0755</fileMode>  
        </fileSet>   
    </fileSets>  
       
    <dependencySets>  
        <dependencySet>  
            <useProjectArtifact>true</useProjectArtifact> 
            <outputDirectory>lib</outputDirectory>  <!-- 将scope为runtime的依赖包打包到lib目录下。 -->  
            <scope>runtime</scope>  
        </dependencySet>  
    </dependencySets>  
    </assembly>  
    

    通过这个xml文件,maven执行mvn package命令的时候,会出现一个release包,其中包含conf、lib两个文件夹。其中conf中存放的是模块下Resource目录下存放的资源文件,lib包下是模块所有依赖的jar包。在部署到linux服务器的时候,这两个文件很重要。

    maven的很多功能是通过插件来实现的,所以基于assembly.xml的打包功能,需要在pom文件中配置如下插件。

    <build>
    	<plugins>
    		<plugin>
    			<groupId>org.apache.maven.plugins</groupId>
    			<artifactId>maven-jar-plugin</artifactId>
    			<configuration>
    				<excludes>
    					<exclude>*</exclude>
    				</excludes>
    			</configuration>
    		</plugin>
    		<plugin>
    			<artifactId>maven-assembly-plugin</artifactId>
    			<executions>  <!--执行器 mvn assembly:assembly -->
    				<execution>
    					<id>make-zip</id><!--名字任意 -->
    					<phase>package</phase><!-- 绑定到package生命周期阶段上 -->
    					<goals>
    						<goal>single</goal><!-- 只运行一次 -->
    					</goals>
    					<configuration>
    						<descriptors> <!--描述文件路径 -->
    							<descriptor>src/main/resources/assembly.xml</descriptor>
    						</descriptors>
    					</configuration>
    				</execution>
    			</executions>
    		</plugin>
    	</plugins>
    </build>
    

    总结

    通过后面介绍如何搭建工程的大篇幅粘贴,终于把文章的篇幅给填充上去了, 哈哈,这里再多唠叨几句。Spring Boot 框架的出现,让Spring 开发变得异常的简单,对于开发者来说是好事也是坏事。好处,开发门槛低了,可以更多的关注业务代码的书写。坏处,框架封的越简单,框架层面出现问题后越傻眼,对底层的框架原理的理解也越少。所以作为有技术追求的你,懂得应该怎么做吧。小编在使用框架的时候,遇到了不少疑问,大部分也都记录下来了。待把疑问解决后,后续会和大家继续分享,敬请期待! 从Spring 到 Spring Boot 再到 Spring Cloud ,东西太多,大家一起加油!



    许多年前 你有一双清澈的双眼

    奔跑起来 像是一道春天的闪电

    想看遍这世界 去最遥远的远方

    感觉有双翅膀 能飞越高山和海洋




  • 相关阅读:
    [NOI2012]美食节——费用流(带权二分图匹配)+动态加边
    mysqlsla slow-query常用用法
    [POI2000]病毒
    mysqlsla安装
    mysqldumpslow
    [学习笔记]约数
    查看MySQL数据的连接
    [学习笔记]质数
    关于ulimit -a中需要修改的两个值
    Miller-Rabin与二次探测
  • 原文地址:https://www.cnblogs.com/1024Community/p/8643467.html
Copyright © 2011-2022 走看看