概述
Spring Tool Suit + Gradle 构建第一个Spring Boot 项目01中完成了基本环境的搭建,也顺利的启动了SpringBoot。
将工程托管到Github
不过,真正开始我们的系列之前,我们先将工程托管到GitHub上吧
请参考以前的博文 Git-将已有的项目转换为GIT项目托管到 GITHUB 仓库
Windows操作系统,我们使用GitBash来托管到Github.
Mr.Yang@Mr MINGW64 ~ $ cd d: Mr.Yang@Mr MINGW64 /d $ cd workspace Mr.Yang@Mr MINGW64 /d/workspace $ cd workspace-sts-3.9.2/ Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2 $ cd masterSpringMvc/ Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc $ ls bin/ build/ build.gradle gradle/ gradlew* gradlew.bat src/ Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc $ git init Initialized empty Git repository in D:/workspace/workspace-sts-3.9.2/masterSprin gMvc/.git/ Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ ls -al total 70 drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 18:22 ./ drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:48 ../ -rw-r--r-- 1 Mr.Yang 197609 28851 3月 7 15:48 .classpath drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 18:22 .git/ -rw-r--r-- 1 Mr.Yang 197609 260 3月 7 15:48 .gitignore drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:48 .gradle/ -rw-r--r-- 1 Mr.Yang 197609 1026 3月 7 15:49 .project drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:49 .settings/ drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:49 bin/ drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:48 build/ -rw-r--r-- 1 Mr.Yang 197609 601 3月 7 15:48 build.gradle drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:48 gradle/ -rwxr-xr-x 1 Mr.Yang 197609 5299 3月 7 15:48 gradlew* -rw-r--r-- 1 Mr.Yang 197609 2260 3月 7 15:48 gradlew.bat drwxr-xr-x 1 Mr.Yang 197609 0 3月 7 15:48 src/ Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ vim .gitignore
查看生成的 .gitignore
.gradle /build/ !gradle/wrapper/gradle-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### nbproject/private/ build/ nbbuild/ dist/ nbdist/ .nb-gradle/
使用 IntelliJ 的话,要忽略自动生成的文件,即“.idea”和“*.iml”。使用 Eclipse 的话,应该将“.classpath”文件和“.settings”文件夹提交上去。不管是哪种情况,都要忽略“.gradle”文件夹和 build 文件夹。
Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ git add . warning: LF will be replaced by CRLF in .gitignore. The file will have its original line endings in your working directory. warning: LF will be replaced by CRLF in build.gradle. The file will have its original line endings in your working directory. warning: LF will be replaced by CRLF in gradle/wrapper/gradle-wrapper.properties. The file will have its original line endings in your working directory. warning: LF will be replaced by CRLF in gradlew. The file will have its original line endings in your working directory. warning: LF will be replaced by CRLF in src/main/java/masterSpringMvc/MasterSpringMvcApplication.java. The file will have its original line endings in your working directory. warning: LF will be replaced by CRLF in src/test/java/masterSpringMvc/MasterSpringMvcApplicationTests.java. The file will have its original line endings in your working directory. Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ git commit -m "Initial commit" [master (root-commit) aebd701] Initial commit 12 files changed, 345 insertions(+) create mode 100644 .gitignore create mode 100644 bin/main/application.properties create mode 100644 bin/main/masterSpringMvc/MasterSpringMvcApplication.class create mode 100644 bin/test/masterSpringMvc/MasterSpringMvcApplicationTests.class create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 src/main/java/masterSpringMvc/MasterSpringMvcApplication.java create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/masterSpringMvc/MasterSpringMvcApplicationTests.java Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ git remote add origin https://github.com/yangshangwei/masterSpringMvc.git Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ git remote -v origin https://github.com/yangshangwei/masterSpringMvc.git (fetch) origin https://github.com/yangshangwei/masterSpringMvc.git (push) Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master) $ git push -u origin master fatal: HttpRequestException encountered. ▒▒▒▒▒▒▒▒ʱ▒▒▒▒ Username for 'https://github.com': yangshangwei Counting objects: 28, done. Delta compression using up to 4 threads. Compressing objects: 100% (22/22), done. Writing objects: 100% (28/28), 53.90 KiB | 0 bytes/s, done. Total 28 (delta 0), reused 0 (delta 0) To https://github.com/yangshangwei/masterSpringMvc.git * [new branch] master -> master Branch master set up to track remote branch master from origin. Mr.Yang@Mr MINGW64 /d/workspace/workspace-sts-3.9.2/masterSpringMvc (master)
最后将本地项目关联到Git
如果你还不了解 Git 的话,我推荐下面的两个教程:
https://try.github.io,这是一个很好的交互式教程,可以引导你一步步地学习基础的 Git 命令;
http://pcottle.github.io/learnGitBranching,这是一个很棒的教程,它将 Git 以类似于树形的结构进行了可视化,它同时展现了 Git 的基本和高级功能。
Gradle构建
如果你还不熟悉 Gradle 的话,那么可以将其视为 Maven 的继任者,它是一个现代化的构建工具。与 Maven 类似,它会使用约定,例如如何组织 Java 应用的结构。我们的源码依然会放在“src/main/java”之中,Web 应用的代码放到“src/main/webapp”之中,诸如此类。
与 Maven 类似,我们可以使用 Gradle 插件来处理各种构建任务。但是,Gradle 真正的闪光点在于,它允许我们使用 Groovy DSL 编写自己的构建任务。默认库使得管理文件、声明任务之间的依赖以及增量执行 job 都变得非常容易。
我们来看下自动生成grable配置文件
buildscript { ext { springBootVersion = '2.0.0.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'masterSpringMvc' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') }
在这里,都看到了什么呢?
对 Spring Boot 插件的依赖,它分布在 Maven 中央仓库中。
我们的项目是 Java 工程。Gradle 可以为 Eclipse 生成 IDE 工程文件。
我们的项目的依赖是托管在 Maven 中央仓库中的。
类路径在生产环境下包含 spring-boot-starter-web,在测试环境中,还包含 spring-boot-starter-test。
可运行的 JAR
Spring Boot 主要的一个优势在于将应用所需的所有内容都放到一个易于重发布的JAR 文件中,其中包含了Web服务器。如果你运行 java jar masterSpringMvc-0.0.1-SNAPSHOT.jar 的话,Tomcat 将会在 8080 端口上启动,就像在开发期一样。如果要将其部署到生产环境或云中,这都是相当便利的。
在这里,主要的依赖是 spring-boot-starter-web,Spring Boot 提供了很多的 Starter,它们会对应用的很多方面进行自动化配置,这是通过提供典型的依赖和 Spring 配置来实现的。
例如,spring-boot-starter-web 将会包含对 tomcat-embedded 和 Spring MVC 的依赖。它会自动运行 Spring MVC 最为常用的配置并提供一个分发器(dispatcher),使其监听“/”根路径,还会提供错误处理页面,就像之前所看到的 404 页面那样。除此之外,还有一个典型的视图解析器(view resolver)配置。
为什么一个main函数就能启动web并提供这么多功能
我们启动应用时,是从main 函数中启动,这种方式有很大的优势,因为我们可以在 IDE 中像运行其他程序那样运行这个应用。我们可以对其进行调试,并且不需要插件就能实现一些类的重新加载。
在开发模式下,当我们在 Eclipse 中保存文件或者在 IntelliJ 中点击“Make Project”就会触发重新加载的过程。只有 JVM 支持切换至新编译版本的类文件时,它才是可行的,如果修改静态变量或配置文件的话,我们必须要重新加载应用。
启动类
package masterSpringMvc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MasterSpringMvcApplication { public static void main(String[] args) { SpringApplication.run(MasterSpringMvcApplication.class, args); } }
需要注意的是@SpringBootApplication 注解,如果看一下这个注解的代码的话,就会发现它实际上组合了 3 个其他的注解,也就是@SpringBootConfiguration、@EnableAutoConfiguration 和@ComponentScan
如果你之前使用过基于Java的配置,那么你应该对@Configuration 非常熟悉。它表明我们的这个类将会处理 Spring的常规配置,如 bean 的声明。
@ComponentScan 也是一个比较经典的注解,它会告诉 Spring 去哪里查找 Spring组件(服务、控制器等)。在默认情况下,这个注解将会扫描当前包以及该包下面的所有子包。
在这里,比较新颖的是@EnableAutoConfiguration 注解,它会指导 Spring Boot 发挥其魔力。如果你将其移除掉的话,就无法从 Spring Boot 的自动配置中受益了。
使用 Spring Boot 来编写 MVC 应用的第一步通常是在代码中添加控制器。将控制器放到 controller 子包中,这样它就能够被@ComponentScan 注解所发现:
package masterSpringMvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HelloController { @RequestMapping("/") @ResponseBody public String hello() { return "Hello Spring Boot"; } }
启动Spring Boot,访问 http://localhost:8080/
是不是已经不是我们第一次访问时的WhiteableError Page了?
幕后的 Spring Boot
我们之前构建传统的Spring MVC 应用,都会编写相关的XML 文件或Java
注解配置类。
一般来讲,初始的步骤如下所示:
1.初始化 Spring MVC 的 DispatcherServlet;
2.搭建转码过滤器,保证客户端请求进行正确地转码;
3.搭建视图解析器(view resolver),告诉 Spring 去哪里查找视图,以及它们是使用哪种方言编写的(JSP、Thymeleaf 模板等);
4.配置静态资源的位置(CSS、JS);
5.配置所支持的地域以及资源 bundle;
6.配置 multipart 解析器,保证文件上传能够正常工作;
7.将 Tomcat 或 Jetty 包含进来,从而能够在 Web 服务器上运行我们的应用;
8.建立错误页面(如 404)。
不过,Spring Boot 为我们处理了所有的事情。因为这些配置一般是与应用相关的,所以你可以无限制地将它们进行组合。
在一定程度上来讲,Spring Boot 是带有一定倾向性的 Spring 项目配置器。它基于约定,并且默认会在你的项目中使用这些约定。
分发器和 multipart 配置
接下来,让我们看一下在幕后到底发生了什么。
我们使用默认生成的Spring Boot配置文件,并将其设置为debug模式。在src/main/resources/application.properties 中添加下面这一行:
debug=true
重新启动应用,观察日志,就能看到Spring Boot 的自动配置报告。它分为两部分:一部分是匹配上的(positive matches),列出了应用中,所有的自动配置,另一部分是没有匹配上的(negative matches),这部分是应用在启动的时候,需求没有满足的Spring Boot 自动配置:
2018-03-07 19:56:18.561 DEBUG 18780 --- [ main] ConditionEvaluationReportLoggingListener : ============================ CONDITIONS EVALUATION REPORT ============================ Positive matches: ----------------- ....... ....... ....... Negative matches: ----------------- ....... ....... .......
因篇幅原因,这里就不贴出来了,实在是太多,可以自行观察控制台。
重点关注DispatcherServletAutoConfiguration注解
这是一个典型的 Spring Boot 配置类。
与其他的 Spring 配置类相同,它使用了@Configuration 注解;
一般会通过@Order 注解来声明优先等级,可以看到 DispatcherServletAutoConfiguration需要优先进行配置;
其中也可以包含一些提示信息,如@AutoConfigureAfter 或@AutoConfigureBefore,从而进一步细化配置处理的顺序;
它还支持在特定的条件下启用某项功能。通过使用@ConditionalOnClass
(DispatcherServlet.class)这个特殊的配置,能够确保我们的类路径下包含
DispatcherServlet,这能够很好地表明 Spring MVC 位于类路径中,用户当前希望将其启动起来。
这个文件中还包含了 Spring MVC 分发器 Servlet 和 multipart 解析器的典型配置。整个Spring MVC 配置被拆分到了多个文件之中。
另外,值得一提的是,这些 bean 会遵循特定的规则,以此来检查是否处于激活状态。在@Conditional(DefaultDispatcherServletCondition.class)条件满足的情况下,ServletRegistrationBean 函数才会启用,这有些复杂,但是能够检查在你的配置中,是否已经注册了分发器 Servlet。
只有在满足@ConditionalOnMissingBean(name=DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)条件的情况下,MultipartResolver 函数才会处于激活状态,例如,当我们自己还没有注册的时候。
这意味着 Spring Boot 仅仅是基于常见的使用场景,帮助我们对应用进行配置。不过,可以在任意的地方覆盖这些默认值,并声明自己的配置。
因此,通过查看 DispatcherServletAutoConfiguration,就了解了为什么我们已经拥有了分发器 Servlet 和 multipart 解析器。
视图解析器、静态资源以及区域配置
重点关注WebMvcAutoConfiguration注解 ,它声明了视图解析器、地域解析器(localeresolver)以及静态资源的位置
视图解析器的配置并没有什么特殊之处,这里真正有意思的是使用了配置属性,从而允许用户对其进行自定义。
它的意思就是说“将会在用户的 application.properties 文件中查找两个变量,这两个变量的名字是 spring.view.prefix 和 spring.view.suffix”。在配置中只需两行代码就能将视图解析器搭建起来了,这是非常便利的。
错误与转码配置
还记得在没有添加控制器的时候,第一次启动应用吗?当时看到了一个有意思的“Whitelabel Error Page”输出。
重点关注ErrorMvcAutoConfiguration注解
定义了一个 bean,即 DefaultErrorAttributes,它通过特定的属性暴露了有用的错误信息,这些属性包括状态、错误码和相关的栈跟踪信息。
定义了一个 BasicErrorController bean,这是一个 MVC 控制器,负责展现我们所看到的错误页面。
允许我们将 Spring Boot 的 whitelabel 错误页面设置为无效,这需要将配置文件application.properties 中的 error.whitelable.enabled 设置为 false。
我们还可以借助模板引擎提供自己的错误页面。例如,它的名字是 error.html,ErrorTemplateMissingCondition 条件会对此进行检查。
至于转码的问题,非常简单的HttpEncodingAutoConfiguration 将会负责处理相关的事宜,这是通过提供 Spring 的 CharacterEncodingFilter 类来实现的。通过 spring.http.encoding.charset配置,我们可以覆盖默认的编码(“UTF-8”),也可以通过 spring.http.encoding.enabled 禁用这项配置。
嵌入式 Servlet 容器(Tomcat)的配置
默认情况下,Spring Boot 在打包和运行应用时,会使用 Tomcat 嵌入式 API(Tomcat embedded API)。
重点关注EmbeddedServletContainerAutoConfiguration注解
可以将 Spring Boot 与 Tomcat、tc-server、Jetty 或者 Undertow 结合使用。服务器可以很容易地进行替换,只需将 spring-boot-starter-tomcat JAR 依赖移除掉,并将其替换为 Jetty或 Undertow 对应的依赖即可。
HTTP 端口
通过在 application.properties 文件中定义 server.port 属性或者定义名为 SERVER_PORT的环境变量,我们可以修改默认的 HTTP 端口。
通过将该变量设置为−1,可以禁用 HTTP,或者将其配置为 0,这样的话,就会在随机的端口上启动应用。对于测试,这是很便利的。
SSL 配置
配置 SSL 是一项很麻烦的事情,但是 Spring Boot 有一项很简单的解决方案。我们只需一点属性就能保护服务器了:
server.port = 8443 server.ssl.key-store = classpath:keystore.jks server.ssl.key-store-password = secret server.ssl.key-password = another-secret
不过,为了使上面的例子运行起来,我们需要生成一个 keystore 文件
如何制作,请参考以前的博文
http://blog.csdn.net/yangshangwei/article/details/72725123#t5
其他配置
在配置中,我们可以通过简单地声明@Bean 元素来添加典型的 Java Web 元素,如Servlet、Filter 和 ServletContextListener。
除此之外,Spring Boot 还为我们内置了 3 项内容:
在 JacksonAutoConfiguration 中,声明使用 Jackson 进行 JSON 序列化;
在HttpMessageConvertersAutoConfiguration中,声明了默认的HttpMessageConverter;
在 JmxAutoConfiguration 中,声明了 JMX 功能。
至此,我们现在已经有了一个非常简陋的 Spring Web 应用,虽然我们没有对其进行什么配置,但是它可以输出 RESTful JSON 的“Hello Spring Boot”。 其中原理,暂不深究~