SpringBoot & SpringMVC (详解)

6. SpringBoot

Spring 的诞⽣是为了简化 Java 程序的开发的,⽽ Spring Boot 是为了快速开发 Spring 程序开发而诞生的。
Spring Boot 的优点:

  • 快速集成框架,Spring Boot 提供了启动添加依赖的功能,⽤于秒级集成各种框架。
  • 内置运⾏容器,⽆需配置 Tomcat 等 Web 容器,直接运⾏和部署程序
  • 快速部署项⽬,⽆需外部容器即可启动并运⾏项⽬。
  • 可以完全抛弃繁琐的 XML,使⽤注解和配置的⽅式进⾏开发。
  • ⽀持更多的监控的指标,可以更好的了解项⽬的运⾏情况。

6.1 SpringBoot 创建和使用演示

6.1.1 创建

  1. 安装Spirng Boot Helper插件,安装好之后它的名字就变成Spirng Initialzr and Assistant 了
  2. 创建 Spring Boot 项目

首先把 Maven 配置为国内源:

第⼀次打开 Spring Boot 项⽬需要加载很久,因为当前 Spring Boot 框架并没有在⾃⼰的本地仓库,为了加速 Spring Boot 项⽬的下载,在打开项⽬之前,先把 Maven 配置为国内源。

<1> 打开 settings

<2>

检查 User Settings file 的 settings.xml ⽂件是否存在,如果不存在,复制下⾯配置了国内源的 settings.xml ⽂件,放到 User Settings file ⽬录下。

xml 复制代码
// 配置了国内源的 settings.xml
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="htt p://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven. apache.org/xsd/settings-1.1.0.xsd"> 
<localRepository>C:\Users\intel\.m2\repository</localRepository> 
 <mirrors>
   <mirror>
     <id>alimaven</id>
     <name>aliyun maven</name> 
     <url>http://maven.aliyun.com/nexus/content/groups/public/</url> 
     <mirrorOf>central</mirrorOf>
   </mirror>
 </mirrors>
</settings>

如果存在,检查 settings.xml 是否配置了国内源,对照代码修改配置即可。

<3>删除本地存储 jar ⽬录中的所有⽂件

<4> 切换到 Idea 中,重新下载 jar 包:

如果出现中⽂会导致下载了 jar 包,但是在项⽬中不能正常使⽤;如果还是下载失败那就是本地⽹速问题,尝试更好⽹络,使⽤⼿机热点或朋友的⼿机热点尝试,如果还是不⾏,间隔 4 ⼩时之后再试。

正式开始创建项⽬:
点击 Finish 就完成 Spring Boot 的项⽬创建了。

网页版创建:

不使⽤ Idea 也可以创建 Spring Boot 项⽬,我们可以使⽤ Spring 官⽅提供的⽹⻚版来创建Spring Boot 项⽬。

<1> 先访问:https://start.spring.io

点击⽣成按钮会下载⼀个 Spring Boot 的 zip 包,解压 zip 之后⽬录如下:

<2> 使⽤ Idea 打开之后,Spring Boot 项⽬就算创建成功了

6.1.1 使用

Spring项目的特点:约定大于配置。

  • 约定把要注入到容器的类和启动类放到同级目录下,此时Spring Boot 项目才能将bean注入到容器中,类上标注 @SpringBootApplication 就可以启动 Spring Boot 项⽬了。

    对⽐ Spring 的项⽬我们也可以看到这⼀特点,⽐如在 Spring 中也是要配置 Bean 的扫描路径的,⽽ Spring Boot 则不需要,Spring 配置如下:
    项目目录介绍

  • src/main/java 为 Java 源代码

  • src/main/resources 为静态资源或配置⽂件,/static:静态资源⽂件夹;/templates:模版资源⽂件夹。

  1. 运行项目

    点击启动类的 main ⽅法就可以运⾏ Spring Boot 项⽬了,

    启动成功如下图所示:

  2. 输出 Hello world

    之前 Spring 是⼀个普通 Java 项⽬,没 办法直接和浏览器进⾏互动,⽤ Spring Boot 可以直接实现和浏览器及⽤户的交互。

    <1> 在创建的项⽬包路径下创建 UserController ⽂件,实现代码如下:

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

@RestController 
@RequestMapping("/user") 
public class UserController {
    @RequestMapping("/sayhi") 
    public String sayHi(){ 
        return  "Hi,Spring Boot."; 
    }
}

<2> 重新启动项⽬,访问 http://localhost:8080/user/sayhi 最终效果如下:

6.2 SpringBoot 热部署

  1. 添加热部署框架⽀持
    在 pom.xml 中添加如下框架引⽤:
xml 复制代码
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-devtools</artifactId>
 <scope>runtime</scope>
</dependency>
  1. Settings 开启项⽬⾃动编译

  2. 开启运⾏中热部署

⾼版本 Idea 设置(IntelliJ IDEA 2021.2 之后版本)

  1. 使⽤ Debug 启动(⾮Run)

6.3 SpringBoot 配置文件

6.3.1 配置文件作用

整个项⽬中所有重要的数据都是在配置⽂件中配置的:

  • 数据库的连接信息(包含⽤户名和密码的设置),
  • 项⽬的启动端⼝
  • 第三⽅系统的调⽤秘钥等信息
  • ⽤于发现和定位问题的普通⽇志和异常⽇志等

如果没有配置信息,那么 Spring Boot 项⽬就不能连接和操作数据库,甚⾄是不能保存可以⽤ 于排查问题的关键⽇志,所以配置⽂件⾮常重要。

6.3.2 配置⽂件的格式

  • 理论上讲 properties 可以和 yml ⼀起存在于⼀个项⽬当中,当 properties 和 yml ⼀起存在⼀个项⽬中时,如果配置⽂件中出现了同样的配置,⽐如 properties 和 yml 中都配置"server.port", 那么这个时候会以 properties 中的配置为主,也就是 .properties 配置⽂件的优先级最⾼,但加载 完 .properties ⽂件之后,也会加载 .yml ⽂件的配置信息。实际的业务当中,我们通常会采取⼀种统⼀的配置⽂件格式,这样可以更好的维护(降低故障率)。
6.3.2.1 .properties 详解

properties 配置⽂件是最早期的配置⽂件格式,也是创建 Spring Boot 项⽬默认的配置⽂件。

  1. 语法:
    properties 是以键值的形式配置的,key 和 value 之间是以"="连接。
java 复制代码
# 配置项⽬端⼝号
# 配置⽂件中使⽤"#"来添加注释信息。
server.port=8084 
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding= utf8
spring.datasource.username=root
spring.datasource.password=root

从上述配置key看出,properties 配置⽂件中会有很多的冗余的信息:

  1. 读取配置⽂件

主动的读取配置⽂件中的内容,可以使⽤ @Value 注解来实现,@Value 注解使⽤"${}"的格式读取。
@Component 在 Spring Boot 启动时候会注⼊到框架中,注⼊到框架中时会执⾏ @PostConstruct初始化⽅法,这个时候就能读取到配置信息了。

示例:

java 复制代码
import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class ReadYml { 
    @Value("${server.port}") 
    private String port;

    @PostConstruct
    public void postConstruct() { 
        System.out.println("Read YML,port:" + port); 
    }
}
6.3.2.2 .yml 详解

yml 是 YAML 是缩写,它的全称 Yet Another Markup Language 翻译成中⽂就是"另⼀种标记语⾔"。

  • yml 是⼀个可读性⾼,写法简单、易于理解,它的语法和 JSON 语⾔类似。
  • yml ⽀持更多的数据类型,它可以简单表达清单(数组)、散列表,标量等数据形态。它使⽤空⽩ 符号缩进和⼤量依赖外观的特⾊,特别适合⽤来表达或编辑数据结构、各种配置⽂件等。
  • yml ⽀持更多的编程语⾔,它不⽌是 Java 中可以使⽤在 Golang、PHP、Python、Ruby、 JavaScript、Perl 中。
  1. 语法
  • yml 是树形结构的配置⽂件,它的基础语法是"key: value",注意 key 和 value 之间使⽤英⽂冒汗加空 格的⽅式组成的,其中的空格不可省略。
  • 示例:使⽤ yml 连接数据库
yml 复制代码
spring:
 datasource:
   url: jdbc:mysql://127.0.0.0:3306/dbname?>characterEncoding=utf8 
   username: root
   password: root

properties:

yml:

  1. yml 配置读取

yml 读取配置的⽅式和 properties 相同,使⽤ @Value 注解即可。

java 复制代码
import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class ReadYml { 
    @Value("${string.hello}") 
    private String hello;

    @PostConstruct
    public void postConstruct() { 
        System.out.println("Read YML,Hello:" + hello); 
    }
}

配置不同数据类型及 null:

yml 复制代码
# 字符串 
string.value: Hello

#布尔值,true或false boolean.value: true boolean.value1: false

# 整数
int.value: 10
int.value1: 0b1010_0111_0100_1010_1110 # ⼆进制

# 浮点数
float.value: 3.14159
float.value1: 314159e-5 # 科学计数法

# Null,~代表null 
null.value: ~
  • 配置字符串:
    字符串默认不⽤加上单引号或者双引号,如果加英⽂的单双引号可以表示特殊的含义。
  1. 字符串默认不⽤加上单引号或者双引号。
  2. 单引号会转义特殊字符,特殊字符最终只是⼀个普通的字符串数据。
  3. 双引号不会转义字符串⾥⾯的特殊字符;特殊字符会作为本身想表示的意思。

配置:

yml 复制代码
string:
 str1: Hello \n Spring Boot. 
 str2: 'Hello \n Spring Boot.' 
 str3: "Hello \n Spring Boot."

读取:

java 复制代码
import org.springframework.beans.factory.annotation.Value; >import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class ReadYml { 
   @Value("${string.str1}") 
   private String str1;

   @Value("${string.str2}") 
   private String str2;

   @Value("${string.str3}") 
   private String str3;

   @PostConstruct
   public void postConstruct() { 
       System.out.println("string.str1:" + str1); 
       System.out.println("string.str2:" + str2); 
       System.out.println("string.str3:" + str3); 
   }
}

执行结果:

  • 配置对象:
yml 复制代码
student: 
 id: 1 
 name: Java 
 age: 18
yml 复制代码
# 行内写法:
student: {id: 1,name: Java,age: 18}

读取对象: @ConfigurationProperties

java 复制代码
# getter 和 setter ⽅法不能省略。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "student") 
@Component
public class StudentComponent {
  private int id;
   private String name;
   private int age;

   public int getId() { 
       return id;
   }

   public void setId(int id) { 
       this.id = id;
   }

   public String getName() { 
		return name;
   }

   public void setName(String name) { 
       this.name = name;
   }

   public int getAge() { 
       return age;
   }

   public void setAge(int age) { 
       this.age = age;
   }
	@Override
   public String toString() { 
       return "StudentComponent{" + 
               "id=" + id + 
               ", name='" + name + '\'' + 
               ", age=" + age + 
               '}';
   }
}

调用类:

java 复制代码
@Component
public class ReadYml2 {
   @Autowired
   private StudentComponent studentComponent;

   @PostConstruct
   public void postConstruct() { 
       System.out.println(studentComponent); 
   }
}

执行结果;

配置集合:

yml 复制代码
dbtypes:
 name:
   - mysql
   - sqlserver 
   - db2
yml 复制代码
# ⾏内写法
dbtypes: {name: [mysql,sqlserver,db2]}

读取集合: @ConfigurationProperties

java 复制代码
@Component 
@ConfigurationProperties("dbtypes")
@Data
public class ListConfig { 
  private List<String> name;
}

打印类:

java 复制代码
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class ReadYml2 { 
   @Autowired
   private ListConfig listConfig;

   @PostConstruct
public void postConstruct() { 
       System.out.println(listConfig.getName()); 
   }
}
6.3.2.3 properties VS yml
  1. properties 是以 key=value 的形式配置的键值类型的配置⽂件,⽽ yml 使⽤的是类似 json 格式的 树形配置⽅式进⾏配置的,yml 层级之间使⽤换⾏缩进的⽅式配置,key 和 value 之间使⽤": "英⽂冒号加空格的⽅式设置,并且空格不可省略。
  2. properties 为早期并且默认的配置⽂件格式,但其配置存在⼀定的冗余数据,使⽤ yml 可以很好的 解决数据冗余的问题。
  3. yml 通⽤性更好,⽀持更多语⾔,如 Java、Go、Python 等,如果是云服务器开发,可以使⽤⼀份 配置⽂件作为 Java 和 Go 的共同配置⽂件。
  4. yml ⽀持更多的数据类型。

SpringMVC程序开发

什么是MVC

MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分 为模型、视图和控制器三个基本部分。

  • Model(模型)是应⽤程序中⽤于处理应⽤程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
  • View(视图)是应⽤程序中处理数据显示的部分。通常视图是依据模型数据创建的。
  • Controller(控制器)是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据, 控制⽤户输⼊,并向模型发送数据。

什么是SpringMVC

  • Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它的正式名称"Spring Web MVC"来⾃其源模块的名称(Spring-webmvc),但它通常被称为"SpringMVC"。
  • MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框架,那么当⽤户在浏览器中输⼊了 url 之后, Spring MVC 项⽬就可以感知到⽤户的请求。
  • Spring MVC 是 Spring 框架的核⼼模块,⽽ Spring Boot 是 Spring 的脚⼿架,绝⼤部分的 Java 项⽬约等于 Spring MVC 项⽬。
  • 在创建 Spring Boot 项⽬时,我们勾选的 Spring Web 框架其实就是 Spring MVC 框架。

Spring MVC 创建和连接

Spring MVC 项⽬创建和 Spring Boot 创建项⽬相同(Spring MVC 使⽤ Spring Boot 的⽅式创建), 在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项⽬。 Spring MVC 中使⽤ @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作⽤。

  1. 创建 Spring MVC 项⽬
    <<1>> Spring MVC 可以基于 Spring Boot 创建,也就是创建⼀个 Spring Boot 项⽬,勾选上 Spring Web 模块即可。
    <<2>> 创建⼀个 UserController 类,实现⽤户到 Spring 程序的互联互通
java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;

@Controller // 让 spring 框架启动时,加载 
@ResponseBody // 返回⾮⻚⾯数据 
@RequestMapping("/user") // 路由器规则注册 
public class UserController {
    // 路由器规则注册 
    @RequestMapping("/hi")
    public String sayHi(){
        return "<h1>Hi,Spring MVC.</h1>"; 
    }
}

<<3>> 当访问地址:http://localhost:8080/user/hi 时就能打印"hello,spring mvc"的信息了。

  • @RequestMapping 注解:
    @RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路 由映射的。所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类 的某个⽅法的过程就叫路由映射。
  • @RequestMapping 修饰类:
java 复制代码
import com.example.demo.model.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/p")
public class PersonController { 
   @RequestMapping("/index")
   public Object index(Person person){
       // 获取参数 
       System.out.println(person.getName() +":"+ 
               person.getPassword());
       // 执⾏业务...
       return "/index.html";
}
}
  • @RequestMapping 修饰类+修饰⽅法,当修饰类和⽅法时,访问的地址是类 + ⽅法。
java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import >org.springframework.web.bind.annotation.ResponseBody;

@Controller
@ResponseBody // 定义返回的数据格式为⾮视图(text/html) 
public class UserController { 
   @RequestMapping("/hi")
   public String sayHi(){
       return "<h1>Hi,Spring MVC.</h1>";
   }
}

⽅法地址:http://localhost:8080/hi

  • @RequestMapping默认接收GET请求,显示的指定 @RequestMapping 来接收 POST请求
java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import >org.springframework.web.bind.annotation.RequestMethod; import >org.springframework.web.bind.annotation.ResponseBody;

@Controller
@ResponseBody // 定义返回的数据格式为⾮⻚⾯
public class UserController {
   @RequestMapping(value = "/hi",method= RequestMethod.POST) 
   public String sayHi(){
       return "<h1>Hi,Spring MVC.</h1>";
   }
}
  • GET请求的3种写法:
java 复制代码
// 写法1 
@RequestMapping("/index")

// 写法2
@RequestMapping(value = "/index",method = RequestMethod.GET)

// 写法3 
@GetMapping("/index")
  • post 请求的 2 种写法:
java 复制代码
// 写法1
@RequestMapping(value = "/index",method = RequestMethod.POST)

// 写法2 
@PostMapping("/index")

获取参数

  • 传递单个参数

在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参。

示例:

java 复制代码
@RequestMapping("/m1")
public Object method_1(String name){ 
   System.out.println("参数 name:"+name); 
   return "/index.html";
}

程序的执⾏结果:

  • 传递对象

Spring MVC 可以⾃动实现参数对象的赋值

示例:

定义对象:

java 复制代码
import lombok.Data;

@Data
public class Person { 
    private int id; 
    private String name; 
    private String password;
}

传递对象代码实现:

java 复制代码
@RequestMapping("/m2")
public Object method_2(Person p){ 
    System.out.println("对象中的 name:"+p.getName()); 
    System.out.println("对象中的 password:"+p.getPassword()); 
    return "/index.html";
}

前端访问:

最终执⾏结果:

  • 表单参数传递/传递多个参数(⾮对象)

当有多个参数时,前后端进⾏参数匹配时,是以参数的名称进⾏匹配的,因此参数的位置不影响后端获取参数的结果。

java 复制代码
@RequestMapping("/m3")
public Object method_3(String name, String pwd) { 
    System.out.println("name 参数:" + name); 
    System.out.println("pwd 参数:" + pwd); 
    return "/index.html";
}

前台访问地址:

  • 后端参数重命名(后端参数映射)

某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,⽐如前端传递了⼀个 time 给后端,⽽后端⼜是有 createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现 这种情况,我们就可以使⽤ @RequestParam 来重命名前后端的参数值。

java 复制代码
@RequestMapping("/m4")
public Object method_4(@RequestParam("time") String createtime) { 
    System.out.println("时间:" + createtime);
    return "/index.html";
}

前端访问地址:

  • 设置必传参数:
    如果我们是前端传递⼀个⾮ time 的参数,就会出现程序报错的情况:

    这是因为后端@RequestParam 注解已经声明了前端必须传递⼀个 time 的参数,但是前端没有给后端传递,查看 @RequestParam 注解的实现细节:
  • ⾮必传参数设置

如果我们的实际业务前端的参数是⼀个⾮必传的参数,我们可以通过设置 @RequestParam 中的 required=false 来避免不传递时报错。

java 复制代码
@RequestMapping("/m4")
public Object method_4(@RequestParam(value = "time", required = false) Stri ng createtime) {
    System.out.println("时间:" + createtime);
    return "/index.html";
}
  • @RequestBody 接收JSON对象

  • 后端接收代码:
java 复制代码
@RequestMapping(value = "/m5", method = RequestMethod.POST) public Object method_5(@RequestBody Person person) { 
    System.out.println("Person:" + person);
    return "redirect:/index.html";
}
  • 获取URL中参数@PathVariable

后端实现代码:

java 复制代码
@PostMapping("/m6/{name}/{password}")
public Object method_6(@PathVariable String name, @PathVariable String pass word) {
    System.out.println("name:" + name);
    System.out.println("password:" + password);
    return "redirect:/index.html";
}

前端⽅法地址:

@PostMapping("/m6/{name}/{password}") 中的 {password} 参数不能省略

  • 上传⽂件@RequestPart
java 复制代码
@RequestMapping("/param9")
public String param9(String name, @RequestPart("myfile") MultipartFile fil e) throws IOException {
    // 获取⽂件后缀名
    String fileName = file.getOriginalFilename().substring(file.getOrigina lFilename().lastIndexOf("."));
    // ⽂件保存地址
    String filePath = ClassUtils.getDefaultClassLoader().getResource("stat ic").getPath() +
         "/" + UUID.randomUUID() + fileName;
    // 保存⽂件
    file.transferTo(new File(filePath));
    return filePath + " 上传成功.";
}

获取项⽬⽬录的⼏种⽅式:

java 复制代码
ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();

new ClassPathResource("").getFile().getAbsolutePath(); 

ClassUtils.getDefaultClassLoader().getResource("").getPath(); 

ResourceUtils.getFile("classpath:static/").getPath();
  • 获取 Request 和 Response 对象
java 复制代码
@RequestMapping("/param10")
public String param10(HttpServletResponse response, HttpServletRequest requ est) {
    String name = request.getParameter("name");
    // 获取所有 cookie 信息
    Cookie[] cookies = request.getCookies();
    return name + " 你好.";
}
  • 传统获取 header/cookie
java 复制代码
@RequestMapping("/param10")
@ResponseBody
public String param10(HttpServletResponse response, HttpServletRequest requ est) {
    String name = request.getParameter("name");
    // 获取所有 cookie 信息
    Cookie[] cookies = request.getCookies();
    String userAgent = request.getHeader("User-Agent");
    return name + ":"+userAgent;
}
  • 简洁的获取 Cookie---@CookieValue
java 复制代码
@RequestMapping("/cookie")
@ResponseBody
public String cookie(@CookieValue("bite") String bite) { 
    return "cookie:" + bite;
}
  • 简洁获取 Header---@RequestHeader
java 复制代码
@RequestMapping("/header")
@ResponseBody
public String header(@RequestHeader("User-Agent") String userAgent) { 
    return "userAgent:"+userAgent;
}
  • Session 存储和获取
    Session 存储:
java 复制代码
@RequestMapping("/setsess")
@ResponseBody
public String setsess(HttpServletRequest request) {
    // 获取 HttpSession 对象,参数设置为 true 表示如果没有 session 对象就创建⼀个 session
    HttpSession session = request.getSession(true);
    if(session!=null){
        session.setAttribute("username","java");
    }
    return "session 存储成功";
}

读取 Session :

java 复制代码
@RequestMapping("/sess")
@ResponseBody
public String sess(HttpServletRequest request) {
    // 如果 session 不存在,不会⾃动创建
    HttpSession session = request.getSession(false);
    String username = "暂⽆";
    if(session!=null && session.getAttribute("username")!=null){ 
        username = (String) session.getAttribute("username");
    }
    return "username:"+username;
}
  • 获取 Session 更简洁的⽅式:
java 复制代码
@RequestMapping("/sess2")
@ResponseBody
public String sess2(@SessionAttribute(value = "username",required = false) String username) {
    return "username:"+username;
}

返回数据

默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图 (xxx.html),⽽现在都是前后端分离的,后端只需要返给给前端数据即可,这个时候我们就需要使⽤ @ResponseBody 注解了。
@ResponseBody 返回的值如果是字符会转换成 text/html,如果返回的是对象会转换成 application/json 返回给前端。

@ResponseBody 可以⽤来修饰⽅法或者是修饰类,修饰类表示类中的所有⽅法都会返回 html 或者 json,⽽不是视图。
组合注解:@RestController,@RestController = @Controller + @ResponseBody。

  • 返回静态⻚⾯
    <<1>> 创建前端⻚⾯ index.html
java 复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1. 0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge"> 
    <title>hello,spring mvc</title>
    <script src="index.js"></script>
</head>
<body>
    <h1>Hello,Spring MVC.</h1>
</body>
</html>

创建控制器 controller:

java 复制代码
import com.example.demo.model.Person;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/p")
public class PersonController { 
    @RequestMapping("/index") 
    public Object index(){ 
        // 执⾏业务...
        // 返回view -> index.html 
        return "/index.html";
    }
}
  • 返回 text/html
java 复制代码
@RequestMapping("/m7") @ResponseBody
public String method_7() { 
    return "<h1>Hello,HTML~</h1>";
}


示例:实现计算器功能

使⽤ postman 传递参数,或使⽤ form 表单的⽅式提交参数。

前端⻚⾯:

java 复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1. 0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge"> 
    <title>计算器示例</title>
</head>
<body>
    <form action="http://localhost:8080/calc/sum">
        <h1>计算器</h1>
        数字1:<input name="num1" type="text"><br>
        数字2:<input name="num2" type="text"><br>
        <input type="submit" value=" 点击相加 ">
    </form>
</body>
</html>

controler代码:

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.ResponseBody;

@ResponseBody
@Controller
@RequestMapping("/calc")
public class CalcController {
    @RequestMapping("/sum")
    public String sum(Integer num1,Integer num2){
        return String.format("<h1>计算的结果是:%d</h1><a href='javascript:h istory.go(-1);'>返回</a>",num1+num2);
    }
}
  • 返回 JSON 对象
java 复制代码
@RequestMapping("/m8")
@ResponseBody
public HashMap<String, String> method_8() { 
    HashMap<String, String> map = new HashMap<>(); 
    map.put("Java", "Java Value"); 
    map.put("MySQL", "MySQL Value"); 
    map.put("Redis", "Redis Value");
    return map;
}


示例:实现登录功能,前端使⽤ ajax,后端返回 json 给前端

后端代码:

java 复制代码
@RequestMapping(value = "/login")
    @ResponseBody
    public HashMap<String,Object> login(String username, String password){ 
        HashMap<String,Object> res = new HashMap<>();
        int succ = 200;
        if(username!=null && password!=null && 
        username.equals("admin") && password.equals("admin")){ 
            res.put("msg","登录成功");
        }else{
            res.put("msg","登录失败");
        }
        res.put("succ",succ);
        return res;
    }

前端代码:

java 复制代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1. 0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src="js/jquery-1.9.1.min.js"></script> 
    <title>Document</title>
    <script>
        function mysub() {
            var username = jQuery("#username").val();
            var password = jQuery("#password").val(); 
            jQuery.getJSON("/user/login",
                {
                    "username":username, 
                    "password":password
                },
                function (result) {
                if(result.succ==200){ 
                    alert("返回结果:"+result.msg);
                }else{
                    alert("操作失败,请重试。");
                }
            });
        }
    </script>
</head>
<body>
    <div style="text-align: center;">
        <h1>登录</h1>
        ⽤户:<input id="username">
        <br>
        密码:<input id="password" type="password">
        <br>
        <input type="button" value=" 提交 " onclick="mysub()" style="margin -top: 20px;margin-left: 50px;">
    </div>
</body>
</html>
  • 请求转发或请求重定向
java 复制代码
// 请求重定向 
@RequestMapping("/index")
public String index(){
    return "redirect:/index.html";
}

// 请求转发 
@RequestMapping("/index2")
public String index2(){
    return "forward:/index.html";
}

转发是服务器帮转的,⽽重定向是让浏览器重新请求另⼀个地址

请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发。

请求重定向地址发⽣变化,请求转发地址不发⽣变化。

请求重定向与直接访问新地址效果⼀直,不存在原来的外部资源不能访问;请求转发服务器端转发有可能造成原外部资源不能访问。

请求转发 forward 导致问题演示:

请求转发如果资源和转发的⻚⾯不在⼀个⽬录下,会导致外部资源不可访问。

java 复制代码
@Controller
@RequestMapping("/user")
public class UserController { 
    @RequestMapping(value = "/index") 
    public String sayHi(){ 
        return "forward:/index.html"; 
    }
}

程序的⽬录:

程序的执⾏结果:

将转发 foward 换成重定向 redirect,⻚⾯就可以正常获取到外部资源 js 了。

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/user")
public class UserController { 
    @RequestMapping(value = "/index") 
    public String sayHi(){ 
        return "redirect:/index.html"; 
    }
}

综合示例:

示例一:使⽤ ajax 实现登录功能,⽆需连接数据库,如果⽤户名和密码都是 root,则显示登录成功,可以直 接访问 main ⻚⾯,否则显示登录失败。每次访问 main ⻚⾯时,验证是否已经登录,如果登录了显示 欢迎信息,否则让⽤户先去登录⻚⾯登录。

示例二:带头像功能的注册功能实现。

6.4 SpringBoot 单元测试

什么是单元测试?

单元测试(unit testing),是指对软件中的最⼩可测试单元进⾏检查和验证的过程就叫单元测试。

单元测试是开发者编写的⼀⼩段代码,⽤于检验被测代码的⼀个很⼩的、很明确的(代码)功能是否正确。执⾏单元测试就是为了证明某段代码的执⾏结果是否符合我们的预期。如果测试结果符合我们的预 期,称之为测试通过,否则就是测试未通过(或者叫测试失败)。

  • 可以⾮常简单、直观、快速的测试某⼀个功能是否正确
  • 使⽤单元测试可以帮我们在打包的时候,发现⼀些问题,因为在打包之前,所以的单元测试必须通 过,否则不能打包成功。
  • 使⽤单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进⾏任何改 变的情况下,测试功能。

6.4.1 Spring Boot 单元测试使用

Spring Boot 项⽬创建时会默认单元测试框架 spring-boot-test,⽽这个单元测试框架主要是依靠另⼀ 个著名的测试框架 JUnit 实现的, Spring Boot 项⽬创建时⾃动添加。

xml 复制代码
<dependency> 
 <groupId>org.springframework.boot</groupId> 
 <artifactId>spring-boot-starter-test</artifactId> 
 <scope>test</scope>
</dependency>

spring-boot-starter-test 的 MANIFEST.MF⾥⾯有具体的说明:

  1. ⽣成单元测试类

    最终⽣成的代码:
java 复制代码
import org.junit.jupiter.api.Test;


class UserControllerTest {

    @Test 
    void save() {

    }
}

这个时候,此⽅法是不能调⽤到任何单元测试的⽅法的,此类只⽣成了单元测试的框架类,具体的业务 代码要⾃⼰填充。

  1. 添加单元测试代码
    <<1>> 添加 Spring Boot 框架测试注解:@SpringBootTest
java 复制代码
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserControllerTest {

    @Test 
    void save() {

    }
}

<<2>> 添加单元测试业务逻辑

java 复制代码
import com.example.demo.model.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest
class UserControllerTest {

    @Resource
    private UserController userController;

    @Test
    void save() {
        User user = new User(); 
        user.setId(1);
        user.setName("Java Test"); 
        user.setPassword("123");
        boolean result = userController.save(user); 
        // 使⽤断⾔判断最终的结果是否符合预期 
        Assertions.assertEquals(true, result); 
        //        Assertions.assertTrue(result);
    }
}

如果断⾔失败,则后⾯的代码都不会执⾏

6.8 SpringBoot 日志文件

6.8.1 日志的功能

  • 发现和定位问题
  • 记录⽤户登录⽇志,⽅便分析⽤户是正常登录还是恶意破解⽤户
  • 记录系统的操作⽇志,⽅便数据恢复和定位操作⼈
  • 记录程序的执⾏时间,⽅便为以后优化程序提供数据⽀持

6.8.2 日志的使用

Spring Boot 内置了⽇志框架,默认情况下使⽤的是 info ⽇志级别将⽇志输出到控制台的。

6.8.3⾃定义⽇志打印

  1. 在程序中得到⽇志对象

在程序中获取⽇志对象需要使⽤⽇志⼯⼚ LoggerFactory。

Logger 对象是属于 org.slf4j 包下的,不要导⼊错包,Spring Boot 中内置了⽇志框架 Slf4j,可以直接在程序中调⽤ slf4j 来输出⽇志。

代码示例:

java 复制代码
	private static Logger logger = LoggerFactory.getLogger(UserController.class );
  1. 使⽤⽇志对象打印⽇志
java 复制代码
// 2.使⽤⽇志打印⽇志 
logger.info("--------------要输出⽇志的内容----------------");


6.8.4⽇志级别

6.8.4.1 ⽇志级别的作用
  1. ⽇志级别可以帮你筛选出重要的信息,⽐如设置⽇志级别为 error,那么就可以只看程序的报错⽇ 志了,对于普通的调试⽇志和业务⽇志就可以忽略了,从⽽节省开发者信息筛选的时间。
  2. ⽇志级别可以控制不同环境下,⼀个程序是否需要打印⽇志,如开发环境我们需要很详细的信息, ⽽⽣产环境为了保证性能和安全性就会输⼊尽量少的⽇志,⽽通过⽇志的级别就可以实现此需求。
6.8.4.2 ⽇志级别的分类
  • trace:微量,少许的意思,级别最低
  • debug:需要调试时候的关键信息打印
  • info:普通的打印信息(默认⽇志级别)
  • warn:警告,不影响使⽤,但需要注意的问题
  • error:错误信息,级别较⾼的错误⽇志信息
  • fatal:致命的,因为代码异常导致程序退出执⾏的事件

越往上接收到的消息就越少,如设置了 warn 就只能收到 warn、error、fatal 级别的⽇志了。

6.8.4.3 ⽇志级别的设置

⽇志级别配置只需要在配置⽂件中设置"logging.level"配置项即可。⽇志的输出级别,默认是 info。

示例:

yml 复制代码
logging: 
  level: 
    root: error
    
java 复制代码
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

@RestController 
@RequestMapping("/user") 
public class UserController {

    // 1.得到⽇志对象
    private static Logger logger = 
            LoggerFactory.getLogger(UserController.class);

    @Value("${server.port}") 
    private String port;

    @Value("${spring.datasource.url}") 
    private String url;

    @RequestMapping("/sayhi") 
    public String sayHi() { 
        // 2.使⽤⽇志打印⽇志

        logger.trace("================= trace ==============="); 
        logger.debug("================= debug ==============="); 
        logger.info("================= info ===============");
        logger.warn("================= warn ==============="); 
        logger.error("================= error ===============");

        return "Hi," + url; 
    }
}
6.8.4.4 日志持久化

在⽣产环境上咱们需要将⽇志保存下来,以便出现问题之后追 溯问题,把⽇志保存下来的过程就叫做持久化。在配置⽂件中指定⽇志的存储⽬录或者是指定⽇志保存⽂件名之后, Spring Boot 就会将控制台的⽇志写到相应的⽬录或⽂件下了。

示例一:

yml 复制代码
# 设置⽇志⽂件的⽬录
logging:
  file:
    path: D:\\home\\ruoyi
yml 复制代码
# 设置⽇志⽂件的⽂件名
logging:
  file:
    name: D:\\home\\ruoyi\\spring-1204.log

示例二:

controller 包下 error 级别以上的⽇志保存到 log_all.log 下,将 service 下 warn 级别以上的⽇志保存到 log_all.log 下。

  • 实现的关键步骤:
    不同包定义不同的⽇志级别。
    使⽤⽇志对象打印所有类型的⽇志。
    设置固定的⽇志保存⽂件名。
6.8.4.5 使用Lombok输出日志

使⽤ @Slf4j 注解,在程序中使⽤ log 对象即可输⼊⽇志,并且只能使⽤ log 对象才能输出, 这是 lombok 提供的对象名。

  1. 添加 lombok 依赖
xml 复制代码
<dependency> 
 <groupId>org.projectlombok</groupId> 
    <artifactId>lombok</artifactId> 
    <version>1.18.20</version> 
    <optional>true</optional> 
</dependency>
  1. 输出⽇志
java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;

@RestController 
@RequestMapping("/p")
@Slf4j
public class PersonController {

    @RequestMapping("/log")
    public void loggerTest() { 
        log.error("------------------- error -----------------"); 
    }
}

lombok 原理

打开target目录可以看到:

lombok 的更多注解:

相关推荐
逊嘘11 分钟前
【Java语言】抽象类与接口
java·开发语言·jvm
morris13118 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
monkey_meng35 分钟前
【Rust中的迭代器】
开发语言·后端·rust
余衫马38 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng42 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
七星静香43 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员44 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU44 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie61 小时前
在IDEA中使用Git
java·git
Elaine2023911 小时前
06 网络编程基础
java·网络