zoukankan      html  css  js  c++  java
  • Spring基础(二)

    编写 Spring 应用

    因为是刚刚开始,所以我们首先为 Taco Cloud 做一些小的变更,但是这些变更会展现 Spring 的很多优点。在刚开始的时候,比较合适的做法是为 Taco Cloud 应用添加一个主页。在添加主页时,我们将会创建两个代码构件:

    • 一个控制器类,用来处理主页相关的请求;
    • 一个视图模板,用来定义主页看起来是什么样子。

    测试是非常重要的,所以我们还会编写一个简单的测试类来测试主页。但是,要事优先,我们需要先编写控制器。

    处理 Web 请求

    Spring 自带了一个强大的 Web 框架,名为 Spring MVC。Spring MVC 的核心是控制器(controller)的理念。控制器是处理请求并以某种方式进行信息响应的类。在面向浏览器的应用中,控制器会填充可选的数据模型并将请求传递给一个视图,以便于生成返回给浏览器的 HTML。

    在第 2 章中,我们将会学习更多关于 Spring MVC 的知识。现在,我们会编写一个简单的控制器类以处理对根路径(比如,/)的请求,并将这些请求转发至主页视图,在这个过程中不会填充任何的模型数据。

    在 src/main/java/tacos 包下创建 HomeController.java 文件,编写代码如下。

    package tacos;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    
    @Controller                   // <--- 控制器
    public class HomeController {
    
      @GetMapping("/")            // <--- 处理对根路径“/”的请求
      public String home() {
        return "home";                 // <--- 返回视图名
      }
    
    }
    

    可以看到,这个类带有 @Controller。就其本身而言,@Controller 并没有做太多的事情。它的主要目的是让组件扫描将这个类识别为一个组件。因为 HomeController 带有 @Controller,所以 Spring 的组件扫描功能会自动发现它,并创建一个 HomeController 实例作为 Spring 应用上下文中的 bean。

    实际上,有一些其他的注解与 @Controller 有着类似的目的(包括 @Component、@Service 和 @Repository)。你可以为 HomeController 添加上述的任意其他注解,其作用是完全相同的。但是,在这里选择使用 @Controller 更能描述这个组件在应用中的角色。

    home() 是一个简单的控制器方法。它带有 @GetMapping 注解,表明如果针对 / 发送 HTTP GET 请求,那么这个方法将会处理请求。该方法所做的只是返回 String 类型的 home 值。

    这个值将会被解析为视图的逻辑名。视图如何实现取决于多个因素,但是因为 Thymeleaf 位于类路径中,所以我们可以使用 Thymeleaf 来定义模板。

    为何使用 Thymeleaf

    你可能会想为什么要选择 Thymeleaf 作为模板引擎呢?为何不使用 JSP?为何不使用 FreeMarker?为何不选择其他的几个可选方案?

    简单来说,我必须要做出选择,我喜欢 Thymeleaf,相对于其他的方案,我会优先使用它。即便 JSP 是更加显而易见的选择,但是组合使用 JSP 和 Spring Boot 需要克服一些挑战。我不想脱离第 1 章的内容定位,所以在这里就此打住。在第 2 章中,我们将会看一下其他的模板方案,其中也包括 JSP。

    模板名称是由逻辑视图名派生而来的,再加上 /templates/ 前缀和 .html 后缀。最终形成的模板路径将是 /templates/home.html。所以,我们需要将模板放到项目的 src/main/resources/templates/home.html 目录中。现在,就让我们来创建这个模板。

    定义视图

    为了让主页尽可能简单,除了欢迎用户访问站点之外,它不会做其他的任何事情。在 src/main/resources/templates 目录下创建 home.html 文件,编写代码如下。

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
      <head>
        <title>Taco Cloud</title>
      </head>
    
      <body>
        <h1>Welcome to...</h1>
        <img th:src="@{/images/TacoCloud.png}" />
    
        <a th:href="@{/design}" id="design">Design a taco</a>
      </body>
    </html>
    

    上面代码是一个基本的 Thymeleaf 模板,它定义了 Taco Cloud 的主页。

    这个模板并没有太多需要讨论的。唯一需要注意的一行代码是用于展现 Taco Cloud Logo 的 标签。它使用了 Thymeleaf 的 th:src 属性和 @{...} 表达式,以便于引用相对于上下文路径的图片。除此之外,它就是一个 Hello World 页面。

    但是,我们再讨论一下这个图片。我将定义 Taco Cloud Logo 的工作留给你,你需要将它放到应用的正确位置中。

    这里大家可以下载实验楼提供的链接下载图片,在实验楼 WebIDE 终端中执行以下命令。

    # 下载图片
    wget https://labfile.oss.aliyuncs.com/courses/1517/TacoCloud.png
    # 创建目录
    mkdir -p /home/project/taco-cloud/src/main/resources/static/images/
    # 移动图片到指定目录
    mv TacoCloud.png /home/project/taco-cloud/src/main/resources/static/images/
    

    图片是使用相对于上下文的 /images/TacoCloud.png 路径来进行引用的。回忆一下我们的项目结构,像图片这样的静态资源是放到 /src/main/resources/static 文件夹中的。这意味着,在项目中,Taco Cloud Logo 图片必须要位于 src/main/resources/static/images/TacoCloud.png。

    我们已经有了一个处理主页请求的控制器并且有了渲染主页的模板,现在基本就可以启动应用来看一下它的效果了。在此之前,我们先看一下如何为控制器编写测试。

    测试控制器

    在测试 Web 应用时,对 HTML 页面的内容进行断言是比较困难的。幸好 Spring 对测试提供了强大的支持,这使得测试 Web 应用变得非常简单。

    对于主页来说,我们所编写的测试在复杂性上与主页本身差不多。测试需要针对根路径“/”发送一个 HTTP GET 请求并期望得到成功结果,其中视图名称为 home 并且结果内容包含“Welcome to...”。

    在 src/test/java/tacos 包下新建 HomeControllerTest.java 文件中,编写代码如下。

    package tacos;
    
    import static org.hamcrest.Matchers.containsString;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    
    @RunWith(SpringRunner.class)
    @WebMvcTest(HomeController.class)         // <--- 针对HomeController的Web测试
    public class HomeControllerTest {
    
      @Autowired
      private MockMvc mockMvc;                 // <--- 注入MockMvc
    
      @Test
      public void testHomePage() throws Exception {
        mockMvc.perform(get("/"))                // <--- 发起对“/”的GET
    
          .andExpect(status().isOk())            // <--- 期望得到HTTP 200
    
          .andExpect(view().name("home"))         // <--- 期望得到home视图
    
          .andExpect(content().string(             // <--- 期望包含“Welcome to...”
                 containsString("Welcome to...")));
      }
    
    }
    

    对于这个测试,我们首先注意到的可能就是它使用了与 TacoCloudApplicationTests 类不同的注解。HomeControllerTest 没有使用 @SpringBootTest 标记,而是添加了 @WebMvcTest 注解。这是 Spring Boot 所提供的一个特殊测试注解,它会让这个测试在 Spring MVC 应用的上下文中执行。更具体来讲,在本例中,它会将 HomeController 注册到 Spring MVC 中,这样的话,我们就可以向它发送请求了。

    @WebMvcTest 同样会为测试 Spring MVC 应用提供 Spring 环境的支持。尽管我们可以启动一个服务器来进行测试,但是对于我们的场景来说,仿造一下 Spring MVC 的运行机制就可以。测试类被注入了一个 MockMvc,能够让测试实现 mockup。

    通过 testHomePage() 方法,我们定义了针对主页想要执行的测试。它首先使用 MockMvc 对象对 /(根路径)发起 HTTP GET 请求。对于这个请求,我们设置了如下的预期:

    • 响应应该具备 HTTP 200 (OK) 状态;
    • 视图的逻辑名称应该是 home;
    • 渲染后的视图应该包含文本 "Welcome to..."。

    如果在 MockMvc 对象发送请求之后,这些期望有不满足的话,那么这个测试会失败。但是,我们的控制器和模板引擎在编写时都满足了这些预期,所以测试应该能够通过,并且带有成功的图标——至少能够看到一些绿色的背景,表明测试通过了。

    在实验楼 WebIDE 终端执行以下命令测试程序。

    # 进入项目根目录
    cd /home/project/taco-cloud
    # 测试
    mvn test
    

    测试结果如下。

    控制器已经编写好了,视图模板也已经创建完毕,而且我们还通过了测试,看上去我们已经成功实现了主页。尽管测试已经通过了,但是如果能够在浏览器中看到结果那会更有成就感,毕竟这才是 Taco Cloud 的客户所能看到的效果。接下来,我们构建应用并运行它。

    构建和运行应用

    因为我们初始化项目时设置了项目类型为 Maven 项目,并在 pom.xml 中设置添加了 Spring Boot 的 Maven 插件,所以可以 Maven 命令来运行应用。

    在实验楼 WebIDE 终端中,执行以下命令运行程序。

    mvn clean spring-boot:run
    

    注:可通过 Ctrl + C 来终止程序运行。

    在应用启动的过程中,你会在控制台看到一些 Spring ASCII 码,随后会是描述应用启动各个步骤的日志条目。在控制台输出的最后,你将会看到一条 Tomcat 已经在 port(s): 8080 (http) 启动的日志,这意味着此时你可以打开 Web 浏览器并导航至主页,这样就能看到我们的劳动成果了。

    稍等一下!刚才说启动 Tomcat?但是我们是什么时候将应用部署到 Tomcat 的呢?

    Spring Boot 应用的习惯做法是将所有它需要的东西都放到一起,没有必要将其部署到某种应用服务器中。在这个过程中,我们根本没有将应用部署到 Tomcat 中……Tomcat 是我们应用的一部分!(在后面小节,我会介绍 Tomcat 是如何成为我们应用的一部分的。)

    现在,应用已经启动起来了,点击侧边栏的 Web 服务来访问实验环境中的 http://localhost:8080,你将会看到如下图所示的界面。如果你设计了自己的 Logo 图片,那么显示效果可能会有所不同。但是,与 xx 图相比,应该不会有太大的差异。

    看上去似乎并不太美观,但这不是一本关于平面设计的书。目前,略显简陋的主页外观已经足够了,它为我们学习 Spring 打下了一个良好的开端。

    到现在为止,我一直没有提及 DevTools。在初始化项目的时候,我们将其作为一个依赖添加了进来。在最终生成的 pom.xml 文件中,它表现为一个依赖项。那么,DevTools 是什么,它又能为我们做些什么呢?接下来,让我们快速浏览一下 DevTools 最有用的一些特性。

    了解 Spring Boot DevTools

    顾名思义,DevTools 为 Spring 开发人员提供了一些便利的开发期工具,其中包括:

    • 代码变更后应用会自动重启;
    • 当面向浏览器的资源(如模板、JavaScript、样式表)等发生变化时,会自动刷新浏览器(实验楼 WebIDE 中需要手动刷新 Web 服务的页面);
    • 自动禁用模板缓存;
    • 如果使用 H2 数据库的话,内置了 H2 控制台。

    DevTools 在 Spring Tool Suite、IntelliJ IDEA 和 NetBeans 中,它都能很好地运行。另外,因为它的目的是仅仅用于开发,所以能够很智能地在生产环境中把自己禁用掉。(我们将会在第 19 章学习应用部署的时候再讨论它是如何做到这一点的。)现在,我们主要关注 Spring Boot DevTools 最有用的特性,先从应用的自动重启开始。

    应用自动重启

    如果将 DevTools 作为项目的一部分,那么你可以看到,当对项目中的 Java 代码和属性文件做出修改后,这些变更稍后就能发挥作用。DevTools 会监控变更,当它看到有变化的时候,将会自动重启应用。

    更准确地说,当 DevTools 运行的时候,应用程序会被加载到 Java 虚拟机(Java virtual Machine,JVM)两个独立的类加载器中。其中一个类加载器会加载你的 Java 代码、属性文件以及项目中 src/main/ 路径下几乎所有的内容。这些条目很可能会经常发生变化。另外一个类加载器会加载依赖的库,这些库不太可能经常发生变化。

    当探测到变更的时候,DevTools 只会重新加载包含项目代码的类加载器,并重启 Spring 的应用上下文,在这个过程中另外一个类加载器和 JVM 会原封不动。这个策略非常精细,但是它能减少应用启动的时间。

    这种策略的一个不足之处就是自动重启无法反映依赖项的变化。这是因为包含依赖库的类加载器不会自动重新加载。这意味着每当我们在构建规范中添加、变更或移除依赖的时候,为了让变更生效,我们需要重新启动应用。

    浏览器自动刷新和禁用模板缓存

    默认情况下,像 Thymeleaf 和 FreeMarker 这样的模板方案在配置时会缓存模板解析的结果。这样的话,在为每个请求提供服务的时候,模板就不用重新解析了。在生产环境中,这是一种很好的方式,因为它会带来一定的性能收益。

    但是,在开发期,缓存模板就不太好了。在应用运行的时候,如果缓存模板,那么我们刷新浏览器就无法看到模板变更的效果了。即便我们对模板做了修改,在应用重启之前,缓存的模板依然会有效。

    DevTools 通过禁用所有模板缓存解决了这个问题。你可以对模板进行任意数量的修改,只需要刷新一下浏览器就能看到结果。

    如果你像我这样,连浏览器的刷新按钮都懒得点,那么对代码做出变更之后,马上在浏览器中看到结果就好了。幸运的是,DevTools 有一些特殊的功能可以供我们使用。

    DevTools 在运行的时候,它会和你的应用程序一起,同时自动启动一个 LiveReload 服务器。LiveReload 服务器本身并没有太大的用处。但是,当它与 LiveReload 浏览器插件结合起来的时候,就能够在模板、图片、样式表、JavaScript 等(实际上,几乎涵盖为浏览器提供服务的所有内容)发生变化的时候自动刷新浏览器。

    LiveReload 有针对 Google Chrome、Safari 和 Firefox 的浏览器插件(要对 Internet Explorer 和 Edge 粉丝说声抱歉)。请访问 LiveReload 官网,以了解如何为你的浏览器安装 LiveReload。

    内置的 H2 控制台

    虽然我们的项目还没有使用数据库,但是这种情况在第 3 章中就会发生变化。如果你使用 H2 数据库进行开发,DevTools 将会自动启用 H2。这样的话,我们可以通过 Web 浏览器进行访问。你只需要让浏览器访问 http://localhost:8080/h2-console ,就能看到应用所使用的数据。

    此时,我们已经编写了一个尽管非常简单却很完整的 Spring 应用。在本书中,我们将会不断扩展它。现在,我们要回过头来看一下都完成了哪些工作以及 Spring 发挥了什么作用。

  • 相关阅读:
    win 8升级win8.1的几个问题
    使用ghost硬盘对拷备份系统
    在odl中怎样实现rpc
    ASP.NET常见内置对象(一)
    [Xcode 实际操作]六、媒体与动画-(12)检测UIView动画的结束事件:反转动画并缩小至不可见状态
    [Xcode 实际操作]六、媒体与动画-(11)UIView视图卷曲动画的制作
    [Xcode 实际操作]六、媒体与动画-(10)UIView视图翻转动的画制作
    [Xcode 实际操作]六、媒体与动画-(9)使用CATransaction Push制作入场动画
    [Xcode 实际操作]六、媒体与动画-(8)使用CATransaction Reveal制作渐显动画
    [Xcode 实际操作]六、媒体与动画-(7)遍历系统提供的所有滤镜
  • 原文地址:https://www.cnblogs.com/sakura579/p/14059207.html
Copyright © 2011-2022 走看看