《Spring实战》之第一章 Spring起步

本文为书籍《Spring实战(第五版)》的摘抄笔记,推荐大家购买实体书籍学习。

什么是Spring

任何实际的应用都是由许多组件组成的,每个组件负责整个应用功能的一部分,这些组件需要与其它的应用元素进行协调,以完成自己的任务。当应用程序运行时,需要以某种方式创建并引入这些组件

Spring的核心是提供了一个容器container),通常称为Spring应用上下文(Spring application context),它们会创建管理应用组件。这些组件也可以称为bean,会在Spring应用上下文中装配在一起,从而形成一个完整的应用程序。

将bean装配在一起的行为是通过一种基于依赖注入dependency injection,DI)的模式实现的。此时,组件不会再创建它所依赖的组件并管理他们的生命周期。使用依赖注入的应用程序依赖于单体的实体(容器)来创建和维护所有组件,并将其注入到需要的bean中,通常这是通过构造器参数 属性访问方法来实现的。

举例来说,假设在应用的众多组件中,有两个是我们需要处理的:库存服务(用来获取库存水平)和商品服务(用来提供基本的商品信息)。商品服务需要依赖于库存服务,这样他才能提供完整的商品信息。下图阐述这些bean和应用上下文之间的关系。

应用组件通过Spring的应用上下文来进行管理并实现互相注入
在核心容器之上,Spring及其一系列的相关库提供了Web框架、各种持久化可选方案、安全框架与其他系统集成、运行时监控、微服务支持,反应式编程以及众多现代应用开发所需要的特性

在历史上,指导Spring应用上下文将bean装配在一起的方式是使用一个或者多个XML文件(描述各个组件以及它们与其他组件的关联关系)。例如,如下的XML描述了两个bean,也就是InventoryService beanProductService bean,并且通过构造器参数将InventoryService装配到了ProductService 中:

    <bean id="inventoryService" class="com.excmple.InventoryService">
    </bean>
    <bean id="ProcdcutService" class="com.excmple.ProductService">
        <constructor-arg ref="inventoryService"/>
    </bean>

但是在最近的Spring版本中,基于Java的配置更为常见,如下基于Java的配置类是与XML配置等价的:

@Configuration
public class ServiceConfiguration {
    @Bean
    public InventoryService inventoryService(){
        return new InventoryService();
    }
    @Bean
    public ProductService productService(){
        return new productService();
    }
}

@Configuration注解会告诉这是一个配置类,会为Spring应用上下文提供bean。这个配置类的方法使用@bean注解进行了标注,表明这些方法所返回的对象会以bean的形式添加到Spring的应用上下文中(默认情况下,这些所对应的beanID与定义他们的方法名称是相同的)。

相对于基于XML的配置方式,基于Java的配置会带来多项额外的收益,包括更强的类型安全性以及更好的重构能力。即便如此,不管是使用Java还是使用XML的显示配置,只有当Spring不能进行自动配置的时候才是必要的。

在Spring技术中,自动配置起源于所谓的自动装配autowiring)和组件扫描component scanning),借助组件扫描技术,Spring能够自动发现应用类路径下的组件,并将他们创建成Spring应用上下文中的bean,并借助自动装配技术,Spring能够自动为组件注入他们所依赖的其他bean。

最近,随着Spring Boot的引入,自动配置的能力已经远远超出了组件扫描和自动装配。Spring Boot是Spring框架的扩展,提供了很多增强生产效率的方法,最为大家所熟知的增强方法就是自动配置autoconfiguration),Spring Boot能够基于类路径中的条目、环境变量和其他因素合理猜测需要配置的组件并将它们装配在一起。

初始化Spring应用

在书本中,我们将会创建一个名为Taco Cloud的在线应用,它能够订购人类所发明的一种美味,也就是墨西哥煎玉米卷(taco)。当然,在这个过程中,为了达成我们的目标,我们将会用到Spring,SpringBoot以及各种相关的库和框架。

我们有很多种初始化Spring应用的可选方案。书中推荐的是使用Spring Initializr初始化应用。

Spring Initializr是一个基于浏览器的Web应用,同时也是一个REST API,能够生成一个Spring项目的骨架。

书中使用的是使用Spring Tool Suite的工具,可以在如Eclipse上使用,我由于使用的是Idea,Idea自带的新建中就有Spring Initializr选项,所以我都是使用Idea来创建的。

使用IDEA 初始化Spring项目

如果要使用IDEA初始化Spring项目,就在File>New菜单下选择Project菜单项。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

需要注意:使用Spring Initializr本质都是从一个web应用下载的初始化模板,如果IDE构建时出现网络问题,可以自己去start.spring.io下载。

最后设置项目在本地路径即可。

在这里插入图片描述

检查Spring项目的结构

项目加载到IDE之后,我们将其展开。
在这里插入图片描述
这是一个典型的Maven或Gradle项目结构,其中源代码放在了“src/main/java”中,测试代码放到了“src/test/java”中,而非Java的资源放到了“src/main/resources”。在这个项目结构中,我们需要注意一下几点。

  • mvnw和mvnw.cmd:这是Maven包装器(wrapper)脚本。借助这些脚本,即便你的机器上没有安装Maven,也可以构建项目。
  • pom.xml:这是Maven构建规范,随后我们将深入介绍该文件。
  • TacoCloudApplication.java:这是Spring Boot主类,它会启动该项目。随后,我们会详细介绍这个类。
  • application.properties:这个文件起初是空的,但是他们为我们提供了指定配置属性的地方。在本章中,我们会稍微修改一下这个文件,但是我会将配置属性的详细阐述放到第五章。
  • static:在这个文件夹下,你可以存放任意为浏览器提供服务的静态内容(图片、样式表、JavaScript等),该文件夹初始为空。
  • templates:这个文件夹中存放用来渲染页面到浏览器的模板文件,这个文件夹初始是空的。不过我们很快就会往里添加Thymeleaf模板。
  • TacoCloudApplicationTests.java:这是一个简单的测试类,他能确保Spring应用上下文可以成功加载。在开发应用的过程中,我们会将更多的测试添加进来。

现在我们来看看Spring Initializr提供的几个条目。

探索构建规范

在填充Initializr表单的时候,我们声明项目要使用Maven来进行构建。因此,Spring Initializr所生成的pom.xml文件已经包含了我们所选择的依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version><!--Spring Boot的版本-->
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>com.brunocheng</groupId>
    <artifactId>taco-cloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging><!--默认没有packageing标签,也意味着默认的打包方式为JAR-->
    
    
    <name>taco-cloud</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency><!--Starter依赖-->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin><!--Spring Boot插件-->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

在pom.xml文件中,我们第一个需要注意的地方就是<packaging>。我们选择了将应用构建成一个可执行的JAR文件,而不是WAR文件,这可能是你所做出的最奇怪的选择之一,对于Web应用来说尤为如此。毕竟,传统的应用都是打包成WAR文件,JAR只是用来打包库和较少的桌面UI应用的。

打包为JAR文件是基于云思维做出的选择,尽管WAR文件非常适合部署到传统的Java应用服务器上,但对于大多数云平台来说,他们并不是理想的选择。有些云平台也能够部署和运行WAR文件,但是所有的云平台都能够运行可执行的JAR文件。因此,默认会使用基于JAR的打包方式,除非我们明确告诉它采用其他的方式。

如果你想将应用部署到传统的Java应用服务器上,那么需要选择基于WAR的打包方式并要包含一个Web初始化类。第二章会有详细的介绍如何创建WAR文件。

接下来,请留意<parent>元素,更具体来说是它的<version>子元素,这表明我们的项目要以spring-boot-starter-parent作为其父POM。除了其他的一些功能之外,这个父POM为Spring项目常用的一些库提供了依赖管理,现在你不需要指定他们的版本,因为这是通过父POM来管理的。这里的2.4.3表明要使用Spring Boot 2.4.3,所以会根据这个版本的Spring Boot来定义继承依赖管理。

你可能也会注意到这三个依赖的artifactId上都有starter这个单词。Spring Boot依赖的特别之处在于它们本身并不包含库代码,而是传递性的拉取其他库。这种依赖主要有三个好处:

  • 构建文件会显著减小并且更易于管理,因为这样不必为每个所需的依赖库都声明依赖。
  • 我们能够根据他所提供的功能来思考依赖,而不是根据库的名称,如果是开发Web应用,那么你只需要添加web starter就可以了,而不必添加一堆单独的库再编写Web应用。
  • 我们不必再担心库版本的问题,你可以直接相信给定版本的SpringBoot,传递性引入的库的版本是兼容的。现在,你只需要关心使用的是哪一个版本的Spring Boot就可以了。

到最后,构建规范还包含一个Spring Boot插件。这个插件提供了一些重要的功能。

  • 它提供了一个Maven goal,允许我们使用Maven来运行应用。后面会尝试使用这个goal。
  • 它会确保依赖的所有库都会包含在可执行JAR文件中,并且能够保证他们在运行时类路径下是可用的。
  • 它会在JAR中生成一个mainifest文件,将引导类(在我们的场景中,也就是TacoCloudApplication)声明为可执行JAR的主类。

引导应用
因为我们将会通过可执行JAR文件的形式来运行应用,所以很重要的一点就是要有一个主类,它将会在JAR运行的时候被执行。我们同时还需要一个最小化的Spring配置,以引导该应用。这就是TacoCloudApplication类所做的事情。

package com.brunocheng.tacocloud.controller;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
//@SpringBootApplication表面这是一个SpringBoot的应用,它是一个组合注解
public class TacoCloudApplication {

    public static void main(String[] args) {
        SpringApplication.run(TacoCloudApplication.class, args);//运行应用
    }
}

尽管在TacoCloudApplication 中只用很少的代码,但是它包含了很多的内容。其中,最强大的一行代码也是最短的。@SpringBootApplication注解明确表明这是一个Spring Boot应用。但是。@SpringBootApplication远比看上去更强大。

@SpringBootApplication是一个组合注解,它组合了三个其他的注解。

  • @SpringBootConfiguration:将该类声明为配置类。尽管这个类目前还没有太多的配置,但是后续我们可以按需添加基于Java的Spring框架配置。这个注解实际上是@Configuration注解的特殊形式。
  • @EnableAutoConfiguration:启用Spring Boot的自动配置。我们随后会介绍自动配置的更多功能。就现在来说,我们只需要知道这个注解会告诉Spring Boot自动配置它认为我们会用到的组件。
  • @ComponentScan:启动组件扫描。这样我们能够通过像@Component@Controller@Service这样的注解声明其他类,Spring会自动发现它们并将它们注册为Spring应用上下文中的组件。

TacoCloudApplication 另外一个很重要的地方是它的main()方法。这是JAR文件执行的时候要运行的方法。在大多数情况下,这个方法都是样板代码,我们编写的每个Spring Boot应用都会有一个类似或完全相同的方法(类名不同则另当别论)。

这个main()方法会调用SpringApplication中静态的run()方法,后者会真正执行应用的引导过程,也就是创建Spring的应用上下文。在传递给run()的两个参数中,一个是配置类,另一个是命令行参数。尽管传递给run()的配置类不一定要和引导类相同,但这是最便利和最典型的做法。

测试应用
测试时软件开发的重要组成部分。鉴于此,Spring Initializr为我们提供了一个测试类作为起步。

package com.brunocheng.tacocloud;

import org.junit.Test;//注意导入的包,还有一个类似@Test注解
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)//使用Spring的运行器、
/*
* @RunWith是Junit的注解,它会提供一个测试运行器来指导Junit如何运行,可以想象为Junit的一个插件。
* SpringRunnner是SpringJunit4ClassRunner的别名
* */
@SpringBootTest
/*
* @SpringBootTest会告诉Junit在测试的时候添加上Spring Boot功能。
* */
public class TacoCloudApplicationTests {
    @Test
//测试方法
    public void contextLoads() {
    }
}

TacoCloudApplicationTests类中的内容不多:这个类中只有一个空的测试方法。即便如此,这个测试类还是会执行必要的检查,确保Spring应用上下能够成功加载。如果你所做的变更导致Spring应用上下文无法创建,那么这个测试将会失败,你就可以做出反应来解决相关问题了。

另外,注意这个类带有@RunWith(SpringRunner.class)注解。@RunWith是JUnit的注解,它会提供一个测试运行器(runner)来指导JUnit如何运行测试。可以将其想象为个JUnit应用一个插件,以提供自定义的测试行为。在本例中,为JUnit提供的是SpringRunner,这是一个Spring提供的测试运行器,它会创建运行所需的Spring应用上下文。

编写Spring应用

处理Web请求

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

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

package com.brunocheng.tacocloud.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller//控制器
public class HomeController {

    @GetMapping("/")//处理对根路径“/”的请求
    //等同于@RequestMapping(method = RequestMethod.GET)
    public String home() {
        return "home";//返回视图名
    }
}

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

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

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

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

定义视图

为了让主页尽可能简单,除了欢迎用户访问站点之外,它不会做其他的任何事情。下面的代码展现了Thymeleaf模板,它定义了Taco Cloud的主页。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset=" UTF-8 />">
    <title>Taco Cloud</title>
</head>
<body>
    <h1>Welcome to...</h1>
    <img th:src="@{/images/good.jpg}">

</body>
</html>

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

启动TacoCloudApplication即可访问。
在这里插入图片描述

测试控制器

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

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

package com.brunocheng.tacocloud.controller;

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;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.Matchers.containsString;
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
/*
* @WebMvcTest注解是Spring Boot提供的一个特殊测试注解,它会让这个测试在SpringMVC应用到上下文中。
* */
public class HomeControllerTest {

    @Autowired
    private MockMvc mockMvc;
    //MockMvc是模拟请求
    @Test
    public void testHome() throws Exception {
        //模拟发送一起请求
        mockMvc.perform(MockMvcRequestBuilders.get("/"))//对/路径(根路径)发送HTTP.GET请求
                .andExpect(status().isOk())//响应状态为200
                .andExpect(view().name("home"))//视图逻辑名称为homw
                .andExpect(content().string(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对象发送请求之后,这些期望有不满足的话,那么这个测试会失败。但是,我们控制器和模板引擎在编写是满足了这个预期,所以测试应该能够过,并且带有绿色的背景。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值