一、前言
研究Spring boot也有一小段时间了,最近会将研究东西整理一下给大家分享,大概会有10~20篇左右的博客,整个系列会以一个简单的博客系统作为基础,因为光讲理论很多东西不是特别容易理解,并且如果每次通过一个简单的小程序也无法系统的把握好一些知识点,所以就以一个简单的系统作为基础来讲,看看通过spring boot如何实现一个完整系统。本系列除了Spring boot基本的知识点之外,还会涉及到Spring boot与数据库、缓存(redis)、消息队列等的结合以及多实例部署等方面的内容。有兴趣的同学可以关注一下。
二、Spring boot 简介
Spring boot 从名称上就可以看出,它是基于Spring的一个框架,所以不熟悉Spring的同学还是得先去学习一下Spring。其次,Spring boot帮我们集成很多常用的功能,使得整个配置更加简单。用过Spring的同学应该知道,虽然Spring一直在努力的减少配置的复杂性,但是,配置一个完全可用的(web)环境还是挺麻烦的,比如需要配置日志、数据库、缓存等,然后再配置tomcat,最后将程序发布到tomcat目录下。而Spring boot则帮我们大大简化了这个过程,它提供了很多starter,只要引入对应的jar包就可以了。例如,我们需要集成tomcat,只需要引入tomcat的starter即可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency>
备注:本文的例子都是基于Maven来实现的,所以如果不熟悉Maven,可以先去看下怎么用,如果熟悉gradle的话,也可以根据情况对配置做相应调整。
我们可以从官方文档上查看Spring boot提供的starter:
这里我只截取了一小部分,可以看到Spring boot支持缓存、批处理、mq、es等等,完整的列表参考官方文档。其他就不多解释了,后续通过示例来讲解整个Spring boot功能,我们先看Spring boot来如何实现一个web版的Hello World!
三、Hello World程序
3.1 Hello World 源码
第一步:导入jar包
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.8.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
第二步:编写控制器类
package com.pandy.blog; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.HashMap; import java.util.Map; @Controller public class HelloWorld { @RequestMapping("/hello") @ResponseBody public Map<String, Object> hello() { Map<String, Object> map = new HashMap<>(); map.put("hello", "world"); return map; } }
第三步:编写启动类(入库)
package com.pandy.blog; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) throws Exception { SpringApplication.run(Application.class, args); } }
运行该类的main方法,然后访问http://localhost:8080/hello,就可以看到如下结果:
是不是感觉很开心?一行配置都没有,就可以直接运行一个Web应用。不过开心完以后有没有想过这是怎么实现的呢?接下来我们一行一行解析上面的代码,虽然行数不多,但是还是有很多东西值得我们去学习和理解的。
3.2 pom文件分析
我们先从pom文件入手,pom文件里面只引入了两个依赖项。第一个是spring-boot-starter-parent,熟悉Maven的朋友应该知道,Maven也可以跟类一样,从父pom文件中继承配置。我们可以看下spring-boot-starter-parent的pom文件,由于篇幅问题,这里面只看两部分,其他东西比较容易理解,大家可以自己读一下。第一个部分是:
该文件又继承了另一个pom文件,即spring-boot-dependencies,这个文件其实就是包含了一大堆的jar,其作用是统一管理spring boot所依赖的jar包的版本,所以之后大家可以看到,各个组件里面引入jar的时候就不再需要再指定版本号了。另一个地方需要说明一下是配置文件的管理:
大家可以看到,默认情况下会将/src/main/resources目录下的文件作为资源文件加入到classpath下,另外,这个地方的仅仅对application*.yml,application*.yaml,application*.properties三种文件进行过滤。这个过滤是指什么呢?大家配置过spring mvc的人应该都知道,配置数据库时,我们通常将数据库的信息配置在一个properties文件中,然后在spring的配置文件中通过<property name="driverClass" value="${jdbc.driver}" /> 的形式引入,这个filter的作用就是在编译的时候将配置文件中配置的名值对替换到spring的配置文件中${xxx}字符,但这个功能不是必要的,即使不进行替换,Spring也能在运行时读取到配置项。
总结一下:spring-boot-starter-parent的作用::
1)jar包的版本管理。
2)配置文件的过滤。
3)常用插件管理。
spring-boot-starter-parent最核心的功能是管理了Spring boot所依赖的所有jar包。不过通parent的方式有一个很明显的问题,很多公司自己有自己的parent文件,而maven是没办法配置多个parent的。如果不使用spring-boot-starter-parent,那应该怎么做??实际上Spring boot提供了另一种方式来解决这个问题,就是在自己的pom文件中加入spring boot的依赖的管理:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.9.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
其实从上面的分析可以看到,这个也是spring-boot-starter-parent 的pom文件的parent,而这个pom文件里面主要就是管理了一大堆的jar包版本。所以导入这个后,就不需要自己再去做版本管理,各个starter会自己根据需要导入对应的jar,但版本号由spring-boot-dependencies统一管理。但是这样的话,spring-boot-starter-parent中的插件就无法使用,并且默认配置文件的过滤功能也没有了。不过这没什么影响,一方面这些功能不是必须的,另一方面如果需要,自己添加也是件很容易的事情。
3.3 HelloWorld类解析:
我们再看下HelloWorld这个类,用过Spring mvc应该知道,其实这个类跟Spring boot没半毛钱关系,业务代码更是没任何跟spring相关的东西,这也是spring一直奉行的一个原则,侵入性极小,这也是Spring成功的一个主要原因。这个类里面跟spring相关的是三个注解,即@Controller,@RequestMapping,@ResponseBody,但是这三个注解也都是Spring mvc提供的。跟Spring boot没有太多联系,在这我就不细讲了,如果不是很清楚,可以去看下Spring MVC的内容,三个注解的基本作用如下:
- Controller:标识为一个控制器,spring会自动实例化该类。
- RequestMapping:url映射。
- ResponseBody:将返回结果自动转换为json串。
3.4 Application类解析
最后我们看下Application这个类,你会发现这个类的东西更少,总共就一行有用的代码,即SpringApplication.run(Application.class, args);这个方法的作用是加载Application这个类,那Application这个类有什么特别之处吗?可以看一下,其实这个类的唯一特殊的地方是一个注解@SpringBootApplication,所以Spring boot的运行肯定跟这个注解有着诸多的联系,我们可以看下这个注解的源码:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
该注解的主要方法就不说了,大家看下就知道,主要是为上面这些注解提供别名。该注解上前四个注解(@Target(ElementType.TYPE),@Retention(RetentionPolicy.RUNTIME),@Documented,@Inherited)大家应该都知道,不熟悉的朋友自己去看下JDK如何实现自定义的注解。我们详细解释一下后面三个注解:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan。
先看一下SpringBootConfiguration,这个注解比较简单,源码如下:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
这个注解仅仅是继承了@Configuration,大家应该知道,Spring提供三种方式的配置:(1)xml文件配置(2)annotation配置(3)Java 类配置。而@Configuration就是用于标识一个类为配置类的注解。Spring 4以后比较推崇通过Java类的方式来进行配置,所以Spring boot也倾向于这种方配置式。并且从源码可以看出,SpringBootConfiguration的作用就是标识类为配置类。
接下来我们看一下@EnableAutoConfiguration注解,这个注解的源码有点复杂,在这不做细讲,后面的文章再详细解析期实现方式。这里说一下该注解的作用,它的主要功能是实现自动配置,什么叫做自动配置?就是Spring boot会根据你引入的jar包做一些自动的配置,例如,在classpath有HSQLDB的jar,spring boot就会自动给你配置一个内存数据库。在这个例子里面我们也可以看到,因为我们引入了Spring-mvc、tomcat等相关的jar,spring boot就会猜测你是一个web工程,然后就会自动做一些spring mvc的配置,比如对静态资源的支持、将返回结果自动转为json格式数据的支持等。这些都是自动配置的结果。对Spring Enable*注解熟悉的同学应该能够更容易理解这个注解,因为Spring中有很多类似的注解。
最后我们再看下@ComponentScan,这个注解不是Spring boot提供的,而是Spring提供的,Spring扫描的包或类,即哪些包和类会自动纳入Spring IoC容器的管理,IoC根据配置对这些类进行实例化。
现在我们再总结一下SpringBootConfiguration这个注解的作用:
1)标志该类为一个配置类。
2)指定扫描的包,便于Spring IoC容器对其进行实例和生命周期的管理。
3)自动配置,通过引入的jar包,猜测用户的意图进行自动化配置。
四、总结
本文详细分析了Spring boot实现的一个web版的Hello World,通过这个例子,我们了解了Spring boot的基本操作,并通过对每行的代码的分析,对Spring boot的原理有了一个大致的了解。总体来讲,Spring boot 统一管理了jar包,然后会根据我们选择的starter来进行自动化配置,通过这种方式来解决复杂的依赖管理,精简配置,从而使得开发者能够更加专注于自己的业务,而不需要做那些很复杂的配置工作。同时,Spring boot这种快速、轻量级的服务也非常适合微服务架构,这个后续有机会再跟大家分享,欢迎继续关注。