Spring Boot学习笔记
Spring Boot学习笔记
视频出处:动力节点springboot视频教程
为什么要使用Spring Boot?
- 使用Spring和SpringMVC时需要用到大量的xml配置文件,配置各种对象,把使用的对象放入到Spring容器中才能使用对象,十分繁琐。同时,用Spring整合其他框架时,需要了解其他框架的配置规则。
- Spring Boot就好比是不需要配置文件的Spring和SpringMVC,已经提前把常用的框架和第三方库配置好了,直接使用即可。开发起来效率更高,更为方便。
第1章 XML和JavaConfig
Spring使用XML作为容器配置文件,在3.0之后加入了JavaConfig,使得可以用java类作为配置文件使用。
这一点就是Spring Boot移除SSM中大量配置文件所用到的重要技术。
1.1 JavaConfig
1.1.1 介绍
JavaConfig:使用java类作为xml配置文件的代替,是配置Spring容器的纯java方法。在这个java类中可以创建java对象,把对象注入Spring容器中。
优点:
- 避免繁琐的xml配置。
- 可以使用面向对象的方式,一个配置类可以继承另一个配置类,可以重写方法。
1.1.2 XML配置容器
①创建maven工程,添加依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!-- 编译插件 -->
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <!-- 插件的版本 -->
            <version>3.5.1</version>
            <!-- 编译级别 -->
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <!-- 编码格式 -->
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>
②创建数据类
package com.tsccg.pojo;
public class Student {
    private String name;
    private Integer age;
    private String sex;
    //get和set...
    //toString
}
③resources 目录下创建 Spring 的配置文件 :beans.xml
声明一个Student对象:
<bean id="student" class="com.tsccg.pojo.Student">
    <property name="name" value="张三"/>
    <property name="age" value="20"/>
    <property name="sex" value="男"/>
</bean>
单元测试:
/**
 * 使用xml方式创建对象
 */
@Test
public void beanTest01() {
    ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
    Student student = (Student) app.getBean("student");
    System.out.println("xml方式创建对象:"+student);
}
结果:
xml方式创建对象:Student{name='张三', age=20, sex='男'}
1.1.3 JavaConfig配置容器
需要使用两个注解:
- @Configuration:放在类上,声明当前类为配置类。
- @Bean:放在方法上,将方法返回的对象注入到容器中。
创建配置类:
package com.tsccg.javaconfig;
import com.tsccg.pojo.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
/**
 * @Author: TSCCG
 * @Date: 2021/12/13 18:12
 */
@Configuration//声明此类为配置类,在这个类中有很多方法, 方法的返回值是对象。
public class SpringConfig {
    /**
     * 创建Student对象并返回
     * 通过@Bean注解将对象注入容器,对象默认id为方法名
     */
    @Bean
    public Student createStudent() {
        Student student = new Student();
        student.setName("李四");
        student.setAge(22);
        student.setSex("女");
        return student;
    }
    /**
     * 自定义Bean对象id
     */
    @Bean(name = "liSiStudent")
    public Student createStudent2() {
        Student student = new Student();
        student.setName("李四");
        student.setAge(22);
        student.setSex("女");
        return student;
    }
}
单元测试:
/**
 * 使用javaConfig创建Student对象
 */
@Test
public void beanTest02() {
    ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
    Student student = (Student) app.getBean("createStudent");
    System.out.println("javaConfig方式创建对象:"+student);
}
/**
     * 自定义bean对象id
     */
@Test
public void beanTest03() {
    ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
    Student student = (Student) app.getBean("liSiStudent");
    System.out.println("自定义id:"+student);
}
结果:
javaConfig方式创建对象:Student{name='李四', age=22, sex='女'}
自定义id:Student{name='李四', age=22, sex='女'}
1.2 @ImporResource
@ImporResource是在配置类里导入其他的xml配置文件,等同于xml配置的
<import resource="classpath:xxx.xml"/>
用法:
①在beans.xml文件中声明一个Student对象
<bean id="wangWuStudent" class="com.tsccg.pojo.Student">
    <property name="name" value="王五"/>
    <property name="age" value="18"/>
    <property name="sex" value="女"/>
</bean>
②在配置类上添加@ImporResource注解,导入beans.xml文件
@Configuration
@ImportResource(value="classpath:beans.xml")//导入beans.xml配置文件,指定从类路径下导入
public class SpringConfig {
    ...
}
③单元测试
/**
     * 配置类导入xml配置文件
     */
@Test
public void beanTest04() {
    ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
    Student student = (Student) app.getBean("wangWuStudent");
    System.out.println("JavaConfig导入xml配置文件:"+student);
}
结果:
JavaConfig导入xml配置文件:Student{name='王五', age=18, sex='女'}
④导入多个配置文件
@Configuration
@ImportResource(value={"classpath:beans.xml","classpath:applicationContext.xml"})
public class SpringConfig {
    ...
}
1.3 @PropertySource
@PropertySource用于在配置类里读取properties属性配置文件,等同于xml配置里的:
<context:property-placeholder location="classpath:xxx.properties"/>
用法:
①在resources目录下创建属性配置文件config.properties
dog.name=小哈
dog.type=哈士奇
dog.age=1
②创建数据类
使用@Component注解创建对象并放入容器,使用@Value注解读取配置文件中的数据并注入对象属性
package com.tsccg.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
 * @Author: TSCCG
 * @Date: 2021/12/13 19:59
 */
@Component("myDog")//创建对象并注入容器中
public class Dog {
    @Value("${dog.name}")//读取属性配置文件中的数据,注入到对象属性中
    private String name;
    @Value("${dog.type}")
    private String type;
    @Value("${dog.age}")
    private Integer age;
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", age=" + age +
                '}';
    }
}
③修改配置类
1)使用@PropertySource注解读取属性配置文件
2)使用@ComponentScan注解扫描数据类,等同于<context:component-scan base-package="com.tsccg.pojo"/>
@Configuration//声明此类为配置类。
@PropertySource(value="classpath:config.properties")//读取属性配置文件
@ComponentScan(basePackages = "com.tsccg.pojo")//组件扫描器
public class SpringConfig {
    ...
}
④单元测试
/**
 * @PropertySource 读取属性配置文件
 */
@Test
public void beanTest05() {
    ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
    Dog myDog = (Dog) app.getBean("myDog");
    System.out.println("外部属性配置文件注入:"+myDog);
}
结果:
外部属性配置文件注入:Dog{name='小哈', type='哈士奇', age=1}
第2章 Spring Boot入门
2.1 介绍
Spring Boot是Spring家族的一个成员,可以简化Spring和SpringMVC的使用。核心还是IOC容器。
特性:
- Create stand-alone Spring applications
- Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
- Provide opinionated 'starter' dependencies to simplify your build configuration
- Automatically configure Spring and 3rd party libraries whenever possible
- Provide production-ready features such as metrics, health checks, and externalized configuration
- Absolutely no code generation and no requirement for XML configuration
翻译:
- 可以独立创建一个Spring应用程序
- 内嵌有Tomcat、Jetty或Undertow(无需部署WAR文件),可以单独启动一个web应用。
- 提供了starter初始依赖以简化项目的构建配置。如在ssm项目中,整合MyBatis框架需要在Spring配置文件中配置MyBatis的对象:DataSource数据源、SqlSessionFactory、Dao代理对象。而在Spring Boot项目中,在pom.xml中加入一个mybatis-spring-boot-starter依赖即可。
- 尽可能地自动配置Spring以及第三方库。就是把Spring和第三方库中的对象都创建好,放到容器中,开发人员可以直接使用。(如SpringMVC的中央调度器,MyBatis框架的对象)
- 提供了生产准备功能,如统计(运行时长等)、健康检查(监控项目是否正常运转)和外部化配置(类如属性配置文件)
- 绝对不会生成代码,无需xml配置文件
2.2 创建Spring Boot项目
2.2.1 方式1:使用Spring Boot提供的初始化器
国内地址:https://start.springboot.io/
步骤:
①新建项目

②初始化设置

③添加依赖

④设置项目路径并创建

⑤项目结构
 
启动类:Application.java
package com.tsccg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
测试类:ApplicationTests.java
package com.tsccg;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
    @Test
    void contextLoads() {
    }
}
⑥项目依赖
<?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>
    <!--继承spring-boot-starter-parent,位于仓库中-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--当前项目的坐标-->
    <groupId>com.tsccg</groupId>
    <artifactId>02-springboot-demo-first</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <!--项目描述,可删去-->
    <name>02-springboot-demo-first</name>
    <description>Demo project for Spring Boot</description>
    <!--jdk版本-->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <!--依赖,版本为继承的spring-boot-starter-parent指定版本-->
    <dependencies>
        <!--web起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <!--编译插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
web起步依赖相关jar:

⑦也可以通过浏览器打开初始化器页面

下载自动生成的项目文件,导入本地即可。
2.2.2 方式2:使用Maven向导
使用Maven向导的方式就是直接创建一个maven项目,按Spring Boot项目结构添加所需文件和配置。
使用Maven创建的好处是可以不用联网。
步骤:
①创建一个空的Maven项目

②修改pom.xml文件
1)添加spring-boot-starter-parent坐标
2)添加web启动依赖及其他配置
<?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 http://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.6.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tsccg</groupId>
    <artifactId>03-springboot-demo-second</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--添加其他所需配置-->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--web起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <!--编译插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
③修改项目结构

1)创建启动类Application.java,加入@SpringBootApplication 注解
package com.tsccg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
2)在resources目录下创建static、templates文件夹,springboot核心配置文件application.properties
3)创建测试类ApplicationTests.java,加入@SpringBootTest注解
package com.tsccg;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApplicationTests {
    @Test
    void contextLoads() {
    }
}
2.3 基于Spring Boot的web例子
在Spring Boot项目中,使用SpringMVC时,不需要提前在配置文件中进行配置,直接使用就行。
步骤:
①基于前面创建的Spring Boot项目,我们直接写一个Controller类:HelloController

②启动Application类的main方法

可以发现内嵌的Tomcat已启动,默认为8080端口。
③在浏览器访问 http://localhost:8080/hello

2.4 @SpringBootApplication注解分析
启动类Application上的@SpringBootApplication注解是Spring Boot项目的重要注解。
其为一个复合注解,内部主要包含@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解:
- @SpringBootConfiguration:内部包含@Configuration注解,作用是声明当前类为配置类,可当作配置文件。
- @EnableAutoConfiguration:开启自动配置,把一些java对象配置好,注入Spring容器中。
- @ComponentScan:组件扫描器,找到注解,根据注解的功能创建对象,给属性赋值等等。组件扫描器默认扫描的是 @ComponentScan 注解所在的类,类所在的包和子包。

2.5 Spring Boot核心配置文件
Spring Boot 的核心配置文件用于配置 Spring Boot 程序。
名字必须以 application 开始,后缀有两种格式:
- 
.properties:student.name=张三 student.age=21
- 
.yml/.yaml:#属性与上级属性之间留两个空格,值与前面的冒号之间必须留一个空格 student: name: 张三 age: 21
2.5.1 .properties文件(默认)
基于前面2.3的web例子进行修改。
①修改application.properties属性配置文件,设置Tomcat启动时开放的端口号以及上下文路径
application.properties:
#设置端口号
server.port=8082
#设置上下文路径
server.servlet.context-path=/boot
②启动应用:

③在浏览器访问 http://localhost:8082/boot/hello

2.5.2 .yml文件(推荐)
同基于前面2.3的web例子进行修改。
①对原有的application.properties文件进行删除或修改文件名称
 若两种格式的文件同时存在,则优先用application.properties
②在resource目录下创建一个application.yml文件,在其中设置端口号和上下文路径
#设置端口号与上下文路径
#属性与上级属性之间留两个空格,值与前面的冒号之间留一个空格
server:
  port: 8083
  servlet:
    context-path: /boot2
③启动应用

若配置未生效则用maven执行clean-->install操作。
④在浏览器访问 http://localhost:8083/boot2/hello

2.5.3 多环境配置
在实际开发的过程中,我们的项目会经历很多的阶段(开发->测试->上线),每个阶段的配置也会不同。例如:端口、上下文路径、数据库等。
为了方便在不同的环境之间切换,SpringBoot 提供了多环境配置,具体操作如下:
基于2.3的web项目进行修改。
①分别为开发、测试、生产环境创建一个配置文件
命名必须以application-自定义环境标识.properties|yml为准

②在application.yml中指定使用哪个环境的配置,如下:

启动应用,在浏览器访问 http://localhost:9081/dev/hello

③修改application.yml,指定为测试环境

重启应用,在浏览器中访问 http://localhost:9082/test/hello

2.5.4 自定义配置项
SpringBoot 的核心配置文件中,除了使用内置的配置项之外,我们还可以添加自定义配置项,然后采用@Value或@ConfigurationProperties读取配置项的属性。
2.5.4.1@Value
用法:@Value("${key}") , key 来自 application.properties(yml)
例子:基于2.3web案例进行修改
①在application.properties核心配置文件中加入内置配置项和自定义配置项
#内置配置项
server.port=8081
server.servlet.context-path=/read
#自定义配置项
student.name=张三
student.age=18
wite=www.xxx.com
②修改HelloController
1)添加私有属性并用@Value注解读取application.properties文件的数据进行注入
2)添加处理器方法将属性响应到浏览器
@RestController
public class HelloController {
    @Value("${server.port}")
    private String port;
    @Value("${student.name}")
    private String name;
    @Value("${student.age}")
    private Integer age;
    @Value("${wite}")
    private String wite;
    @RequestMapping("/data")
    public String readData() {
        return name + "由于年龄未到" + age + ",所以不能从" + port + "端口访问" + wite;
    }
}
③启动应用,在浏览器访问 http://localhost:8081/read/data

2.5.4.2@ConfigurationProperties
@ConfigurationProperties:可以把配置文件中的数据映射为java对象,适用于自定义配置项较多的情况。用在类上或配置类方法上。
属性:prefix,用于匹配配置文件中某些配置项开头的内容,如:指定prefix为student,那么就会匹配student.name、student.age等以student开头的配置项。
prefix可以不指定,如果不指定,那么会去配置文件中寻找与该类的属性名一致的配置项,prefix的作用是区分同名配置。
案例演示:(基于上一个例子)
①创建一个java类Student
1)使用@Component注解创建对象并注入容器
2)使用@ConfigurationProperties注解从配置文件中读取配置数据为该类注入属性
package com.tsccg.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "student")
public class Student {
    private String name;
    private Integer age;
    
    //get set
    //toString
}
使用ConfigurationProperties 注解,IDEA 会出现一个警告,但是不影响程序的执行。在pom中加入如下依赖后,重启项目即可消除。
<!--处理使用@ConfigurationProperties 注解出现警告问题-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
②创建StudentController类
@RestController
public class StudentController {
    @Resource//自动注入
    private Student student;
    @RequestMapping("/student")
    public String readStudent() {
        return student.toString();
    }
}
③开启应用,在浏览器访问http://localhost:8081/read/student

2.6 在Spring Boot项目中使用JSP
Spring Boot默认不支持JSP,而是使用模板技术代替jsp。
若要使用jsp需要进行以下配置:
①添加依赖
<!--引入Spring Boot内嵌的Tomcat对JSP的解析包不加解析不了jsp页面-->
<!--如果只是使用JSP页面可以只添加该依赖-->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--如果要使用servlet必须添加该以下两个依赖-->
<!--servlet依赖的jar包-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
</dependency>
<!--jsp依赖jar包-->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.1</version>
</dependency>
<!--如果使用 JSTL 必须添加该依赖-->
<!--jstl 标签依赖的 jar 包 start-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>
②指定jsp文件编译后的存放目录
SpringBoot 要求 jsp 文件必须编译到指定的 META-INF/resources 目录下才能访问,否则访问不到。
在pom.xml的build标签配置如下信息
<resources>
    <resource>
        <!--源文件位置-->
        <directory>src/main/webapp</directory>
        <!--指定编译到 META-INF/resource,该目录不能随便写-->
        <targetPath>META-INF/resources</targetPath>
        <!--指定要把哪些文件编译进去,**表示 webapp 目录及子
目录,*.*表示所有文件-->
        <includes>
            <include>**/*.*</include>
        </includes>
    </resource>
    <!--把src/main/resources下面的所有文件,都包含到classes目录-->
    <resource>
        <directory>src/main/resources</directory>
        <includes>
            <include>**/*.*</include>
        </includes>
    </resource>
</resources>
③在src/main/目录下创建存放jsp文件的目录:webapp ,并在项目中指定为Web Resource Directory,创建一个jsp文件index.jsp

④在index.jsp中获取请求作用域的数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${data}
</body>
</html>
⑤在application.properties配置文件中配置SpringMVC中的视图解析器
server.port=9090
#视图前缀
spring.mvc.view.prefix=/
#视图后缀
spring.mvc.view.suffix=.jsp
⑥创建JspController,返回视图
package com.tsccg.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class JspController {
    @RequestMapping("/doSome")
    public String doSome(Model model) {
        //向请求作用域中放入数据
        model.addAttribute("data","Spring Boot整合Jsp");
        return "index";
    }
}
⑦开启应用,访问 http://localhost:9090/doSome

2.7 Spring Boot中使用容器对象(ApplicationContext)
我们点进启动类中SpringApplication的run方法,发现run方法中返回了一个ConfigurationApplicationContext类型对象,继续点开ConfigurationApplicationContext,发现是一个接口,继承了ApplicationContext。

ApplicationContext为Spring的容器对象,通过该对象可以直接获取容器中的Bean对象。
当我们在不想启动整个项目的前提下测试部分代码时,可以通过main方法中的SpringApplication.run()语句获取返回的Spring容器对象,获取业务bean进行调用。
演示:
①创建业务接口 HelloService
package com.tsccg.service;
public interface HelloService {
    void sayHello(String name);
}
②创建业务接口实现类 HelloServiceImpl
package com.tsccg.service.impl;
import com.tsccg.service.HelloService;
import org.springframework.stereotype.Service;
@Service(value = "helloService")
public class HelloServiceImpl implements HelloService {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello " + name);
    }
}
③在启动类main方法中,获取容器对象,取出业务bean对象调用其方法
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        //获取容器对象
        ConfigurableApplicationContext app = SpringApplication.run(Application.class, args);
        //获取业务bean对象
        HelloService helloService = (HelloService) app.getBean("helloService");
        //调用方法
        helloService.sayHello("张三");
    }
}
④启动应用

2.8 CommandLineRunner接口
开发中可能会有这样的情景:需要在容器启动后执行一些内容,比如读取配置文件,数据库连接之类的。SpringBoot 给我们提供了两个接口来帮助我们实现这种需求,使用任意一个都可以
- CommandLineRunner
- ApplicationRunner
它们的执行时机为容器启动完成的时候,这两个接口中都有一个 run 抽象方法,我们只需要实现这个方法即可。这两个接口的不同之处在于 :
- ApplicationRunner接口中的run方法参数为 ApplicationArguments
- CommandLineRunner接口中run方法的参数为 String 数组
@FunctionalInterface
public interface CommandLineRunner {
	void run(String... args) throws Exception;
}
@FunctionalInterface
public interface ApplicationRunner {
    void run(ApplicationArguments args) throws Exception;
}
演示使用:
以2.7中的例子为基础,进行修改。
①修改启动类
使启动类实现CommandLineRunner接口,实现其run方法
@SpringBootApplication
public class Application implements CommandLineRunner {
    @Resource
    private HelloService helloService;
    public static void main(String[] args) {
        System.out.println("准备创建容器对象");
        SpringApplication.run(Application.class, args);
        System.out.println("创建容器对象后");
    }
    
    @Override
    public void run(String... args) throws Exception {
        System.out.println("容器对象创建好后,执行的方法");
        String result = helloService.sayHello("李四");
        System.out.println("调用容器对象中的方法:" + result);
    }
}
②开启应用

第3章 Spring Boot与web组件
三个内容:
- 拦截器 HandlerInterceptor
- Servlet
- 过滤器 Filter
3.1 拦截器
拦截器是SpringMVC中的一种对象,能够拦截对Controller的请求,实现对请求的预先处理。
3.1.1 回顾SpringMVC中使用拦截器
①自定义拦截器,实现HandlerInterceptor接口
public interface HandlerInterceptor {
    
 default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     return true;
 }
 default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
 }
 default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
 }
}
②在SpringMVC配置文件中注册拦截器类
<mvc:interceptors>
	<mvc:interceptor>
    	<mvc:mapping path="拦截的url" />
        <bean class="拦截器类全限定名称"/>
    </mvc:interceptor>
</mvc:interceptors>
3.1.2 在Spring Boot中使用拦截器
在Spring Boot中使用拦截器与SpringMVC中使用的步骤大体一致,都是先自定义拦截器,然后将其注册到项目中。
只不过在Spring Boot中要把拦截器注册到@Configuration修饰的配置类中。
具体步骤:
①自定义拦截器
创建java类实现 HandlerInterceptor 接口,实现preHandle方法
package com.tsccg.handlerInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyLoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("执行了自定义拦截器的preHandle方法");
        return true;
    }
}
②注册拦截器
- 创建java类实现WebMvcConfigurer接口,实现其addInterceptors方法,用@Configuration注解修饰该类
- 将自定义的拦截器对象注册到项目中,设置拦截和放行的url
package com.tsccg.config;
import com.tsccg.handlerInterceptor.MyLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * 相当于SpringMVC配置文件
 */
@Configuration
public class MyAppConfig implements WebMvcConfigurer {
    /**
     * 注册拦截器
     * @param registry 登记系统中可以使用的拦截器对象
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //拦截的url
        String path = "/user/**";
        //放行的url
        String excludePath = "/user/login";
        registry.addInterceptor(new MyLoginInterceptor())
                .addPathPatterns(path).excludePathPatterns(excludePath);
    }
}
③创建测试用的Controller
@RestController
public class UserController {
    @RequestMapping("/user/account")
    public String account() {
        return "10000";
    }
    @RequestMapping("/user/login")
    public String login() {
        return "登录界面";
    }
}
④启动应用
1)通过浏览器访问受拦截的url: http://localhost:8080/user/account
可见,在发送请求后,后台执行了自定义的拦截器方法

2)通过浏览器访问放行的url: http://localhost:8080/user/login
可见,在发送请求后,后台没有执行拦截器方法

3.2 Servlet
ServletRegistrationBean用来注册Servlet对象
使用步骤:
①创建Servlet
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();
        out.print("使用Servlet对象");
        out.flush();
        out.close();
    }
}
②注册Servlet
@Configuration
public class MyAppConfig{
    /**
     * 注册Servlet
     * @return
     */
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        //ServletRegistrationBean reg = new ServletRegistrationBean(new MyServlet(),"/myServlet");
        
        ServletRegistrationBean reg = new ServletRegistrationBean();
        //注册Servlet对象
        reg.setServlet(new MyServlet());
        //设置请求路径
        reg.addUrlMappings("/myServlet");
        return reg;
    }
}
③启动应用,在浏览器中访问 http://localhost:8080/myServlet

3.3 过滤器
FilterRegistrationBean 用来注册 Filter 对象
使用步骤:
①自定义过滤器,实现javax.servlet.Filter接口
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("执行了自定义过滤器,方法:doFilter");
        chain.doFilter(request,response);
    }
}
②注册过滤器
@Configuration
public class MyAppConfig{
    /**
     * 注册过滤器对象
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        //注册自定义过滤器对象
        bean.setFilter(new MyFilter());
        //设置过滤的url
        bean.addUrlPatterns("/user/*");
        return bean;
    }
}
③创建Controller
@RestController
public class MyController {
    @RequestMapping("/user/account")
    public String userAccount() {
        return "过滤器使用测试";
    }
    @RequestMapping("/member/account")
    public String memberAccount() {
        return "过滤器使用测试2";
    }
}
④启动应用
1)访问过滤器指定范围内的地址: http://localhost:8080/user/account

2)访问过滤器指定范围外的地址: http://localhost:8080/member/account

3.4 字符集过滤器
3.4.1回顾SpringMVC使用字符集过滤器
CharacterEncodingFilter是框架提供的字符集过滤器,解决post方式请求中文字符乱码的问题。
在通过SpringMVC框架使用该过滤器时,需要在web.xml中注册该过滤器,配置其属性:
<!--注册字符集过滤器-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
3.4.2在Spring Boot中使用字符集过滤器
使用的方式有两种:
1.在配置类中注册
2.在Spring Boot核心配置文件中设置
1.在配置类中注册
步骤:
①创建Servlet,在不设置utf-8的情况下响应中文字符
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");//不设置utf-8字符集
        PrintWriter out = resp.getWriter();
        out.print("====中文测试数据====");//响应中文字符
        out.flush();
        out.close();
    }
}
②注册Servlet
@Configuration
public class MyAppConfig{
    /**
     * 注册Servlet
     * @return
     */
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        ServletRegistrationBean bean =
                new ServletRegistrationBean(new MyServlet(),"/user/inf");
        return bean;
    }
}
③开启应用,在浏览器访问 http://localhost:8080/user/inf
可见,响应的中文字符发生乱码。

④在配置类中注册字符集过滤器
@Configuration
public class MyAppConfig{
    /**
     * 注册Servlet
     * @return
     */
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        ServletRegistrationBean bean =
                new ServletRegistrationBean(new MyServlet(),"/user/inf");
        return bean;
    }
    /**
     * 注册过滤器
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        //创建框架提供的字符集过滤器类
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("utf-8");//设置encoding属性为utf-8
        filter.setForceEncoding(true);//设置请求对象和响应对象的字符编码集与encoding属性一致
        bean.setFilter(filter);//注册字符集过滤器
        bean.addUrlPatterns("/user/*");//设置过滤的url
        return bean;
    }
}
⑤在application.properties中添加如下配置
server.servlet.encoding.enabled=false
⑥重启应用,重新访问

2.在核心配置文件中设置
Spring Boot 项目默认启用了 CharacterEncodingFilter, 直接在application.properties中设置他的属性就可以:
#设置 spring boot 中 CharacterEncodingFitler 的属性值
server.servlet.encoding.enabled=true
server.servlet.encoding.charset=utf-8
#强制 request, response 使用 charset 他的值 utf-8
server.servlet.encoding.force=true
重启应用,重新访问:

第4章 ORM操作数据库
对象关系映射(Object Relational Mapping,简称ORM),是一种程序设计技术,用于实现面向对象程序语言里不同类型系统的数据之间的转换。
MyBatis就是ORM的一种,下面将展示在Spring Boot项目中使用MyBatis操作MySQL数据库。
建表:

步骤分析:
- 创建Spring Boot项目,勾选Web、MyBatis、MySQL Driver起步依赖
- 在application.properties中配置数据库连接信息
- 创建实体类Student
- 创建Controller,接收浏览器请求,访问Service
- 创建Service接口及其实现类,调用Dao接口方法
- 创建Dao接口,添加查询方法
- 在Dao接口同级目录下创建对应Mapper文件,指定namespace,写sql语句
- 在pom.xml中指定把src/main/java目录中的xml文件包含到classpath中
其中,创建Dao代理对象的方式有两种:
- 在每个Dao接口上添加@Mapper
- 在启动类上添加@MapperScan(basePackages = {"com.tsccg.dao","com.tsccg.dao2"})
4.1 第一种方式:@Mapper
①创建Spring Boot项目,勾选Web、MyBatis、MySQL Driver起步依赖

②在application.properties中配置数据库连接信息
#项目端口号
server.port=9090
#数据库连接信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db_mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
③创建Student实体类
package com.tsccg.pojo;
public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;
	//get set
    //toString
}
④创建Controller
package com.tsccg.controller;
@RestController
public class StudentController {
    @Resource
    private StudentService studentService;
    @RequestMapping("/find")
    public String findStudent(Integer id) {
        Student student = studentService.findById(id);
        return student.toString();
    }
}
⑤创建Service
StudentService接口:
package com.tsccg.service;
import com.tsccg.pojo.Student;
public interface StudentService {
    Student findById(Integer id);
}
接口实现类:
package com.tsccg.service.impl;
import javax.annotation.Resource;
@Service
public class StudentServiceImpl implements StudentService {
    @Resource
    private StudentDao studentDao;
    @Override
    public Student findById(Integer id) {
        return studentDao.findById(id);
    }
}
⑥创建Dao
StudentDao接口:
package com.tsccg.dao;
import com.tsccg.pojo.Student;
import org.apache.ibatis.annotations.Mapper;
@Mapper//告诉MyBatis这是dao接口,创建此接口的代理对象。
public interface StudentDao {
    Student findById(Integer id);
}
在同级目录下创建StudentDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tsccg.dao.StudentDao">
    <!--根据id查询学生信息-->
    <select id="findById" parameterType="int" resultType="com.tsccg.pojo.Student">
        select id,name,email,age from t_student where id = #{id}
    </select>
</mapper>
⑦由于是在src/main/java目录下创建的mapper映射文件,故需要在pom.xml中指定把src/main/java目录中的xml文件包含到classpath中
在build标签内添加如下语句:
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
    </resource>
</resources>
⑧开启应用,在浏览器访问 http://localhost:9090/find?id=1001

4.2 第二种方式:@MapperScan
第一种方式需要在每一个Dao接口上都加@Mapper,当Dao接口较多时不方便。
而这种方式只需要在启动类上添加@MapperScan(basePackages = "com.tsccg.dao")即可。
@SpringBootApplication
@MapperScan(basePackages = "com.tsccg.dao")
//@MapperScan(basePackages = {"com.tsccg.dao","com.tsccg.dao2"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
4.3 将Mapper文件与java代码分开管理
我们在项目中更偏向于将mapper文件与java代码分开管理,就是将mapper文件都放在src/main/resources目录下,将java代码都放在src/main/java目录下。
有两种分离方式:
- 第一种:在resources目录下创建com.tsccg.dao目录,使得编译后将mapper文件和Dao.class文件放在一起
- 第二种:在resources目录下创建自定义子目录,在核心配置文件中指定从类路径下的该目录找mapper文件
4.3.1第一种分离方式
在resources目录下创建com.tsccg.dao目录,将mapper文件都放进去。
这种分离方式可以在项目编译后,将mapper文件与StudentDao.class文件放在一起,如下:

步骤:
①在resources目录下创建com.tsccg.dao目录,将mapper文件移动至该目录下。

注意,在resources目录下创建com.tsccg.dao目录时,必须用/表示分层,如下:

不然会将com.tsccg.dao当作一个目录名,编译后不会将mapper文件与StudentDao.class放在一起。

②在pom.xml中注释掉原先设置的resources标签,不然会编译报错

③重启应用,通过浏览器重新访问

4.3.2第二种分离方式
第二种方式可以告诉程序从什么位置找到mapper文件,无需让mapper文件与编译后的StudentDao.class位于同级目录。
步骤:
①在resources目录下创建自定义的子目录,如mapper,然后将mapper文件放入。

②在application.properties核心配置文件中指定mapper文件所在位置
#指定mapper文件的位置
mybatis.mapper-locations=classpath:mapper/*.xml
#指定使用mybatis的日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
③同样需要在pom.xml中注释掉原先设置的resources标签,不然会编译报错

④重启应用,重新访问地址

4.4事务
在Spring Boot中使用事务很简单,底层用的还是Spring提供的事务管理。
使用步骤:
- 在入口类上添加 @EnableTransactionManagement注解开启事务支持(默认开启,但最好添上)
- 在访问数据库的Service接口实现类上添加 @Transactional注解即可
实例演示:
以4.1中的例子为基础进行修改。
①分别在入口类上和Service接口实现类上添加事务注解

②在Controller里添加删除方法
使用try...catch语句监控Service中是否抛出异常,响应相应信息。
@RestController
public class StudentController {
    @Resource
    private StudentService studentService;
    
    @RequestMapping("/delete")
    public String deleteStudentById(Integer id) {
        try {
            //通过id删除学生信息
            studentService.deleteById(id);
            return "删除成功";
        } catch (Exception e) {
            e.printStackTrace();
            return "删除失败";
        }
    }
}
③在Service接口中添加删除方法并在实现类中实现
StudentService:
public interface StudentService {
	//通过id删除学生信息
    void deleteById(Integer id);
}
StudentServiceImpl:
@Service
@Transactional//开启事务
public class StudentServiceImpl implements StudentService {
    @Resource
    private StudentDao studentDao;
    
    @Override
    public void deleteById(Integer id) {
        studentDao.deleteById(id);//调用Dao删除数据
        int a = 10 / 0;//抛出运行时异常
    }
}
④在Dao接口中定义删除方法并在Mapper文件中添加对应sql语句
StudentDao:
public interface StudentDao {
    void deleteById(Integer id);
}
StudentDao.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tsccg.dao.StudentDao">
    <delete id="deleteById" parameterType="int">
        delete from t_student where id = #{id}
    </delete>
</mapper>
⑤测试
开启应用,通过浏览器访问 http://localhost:9090/delete?id=1006

第5章 RESTful 接口架构风格
5.1 RESTful简介
REST(Representational State Transfer),表现层资源状态转移。
 REST是一种互联网软件架构设计的风格,并不是标准,可用也可不用。它只是提出了一组客户端与服务器交互时的架构理念和设计原则,基于这种理念和原则设计的接口可以更简洁,更有层次。说白了,就是一种统一的url命名格式,将CRUD操作的url命名规范化,统一化。
表现层资源状态转移 概念说明:
- 表现层:就是视图层,显示资源的,通过视图页面、jsp等显示操作资源的结果。
- 资源:服务器端的动静态资源文件,数据库表中的数据等都是资源。每个资源都是服务器上一个可命名的抽象概念,是以名词为核心来组织的,如用user表示用户在服务端数据库的信息。一个资源可由一个或多个url来标识,url既是资源的名称,也是Web上的地址。在浏览器等客户端上,可以通过资源的url与其进行交互。
- 资源状态:就是对于资源的表述,当我们通过浏览器访问一个视频、一段文字或一张图片时,对应资源的表述格式是不一样。
- 资源状态转移:资源状态转移说的是资源在客户端和服务端之间转移(请求资源-响应资源)的表述。通过转移和操作资源的表述来实现操作资源的目的。
5.2 RESTful的实现
我们过去访问一个资源所用的url五花八门,如访问一个用户信息:
http://localhost:8080/findUserById?id=1001		GET		//查询一个用户信息  
http://localhost:8080/addUser					POST 	//添加用户信息	
http://localhost:8080/updateUser				POST	//更新用户信息	
http://localhost:8080/deleteUserById?id=1001	GET		//删除一个用户信息   
以上这些url所操作的都是同一个资源,在url内就写明了对该资源的操作。
而REST是面向资源的,资源是通过url进行暴露的。REST中,url的设计只需要把资源通过合理的方式暴露出来即可,对资源的操作与url无关,操作是通过HTTP动词来体现的。
HTTP协议中,GET、POST、DELETE、PUT都是表示操作方式的动词。
它们分别对应四种基本操作:
- GET:获取资源
- POST:新建资源
- DELETE:删除资源
- PUT:更新资源
REST风格提倡url地址使用统一的风格设计,用名词表示资源,以及访问资源的信息,在url中,使用/分隔对资源的访问信息。
修改上面的url为REST风格:
http://localhost:8080/user/1001		GET		//查询一个用户信息  
http://localhost:8080/user			POST	//添加用户信息
http://localhost:8080/user/1001		DELETE	//删除一个用户信息
http://localhost:8080/user			PUT		//更新用户信息
现在问题有两个问题:
- 浏览器只支持GET和POST方式的请求,如何发送DELETE和PUT请求呢?
- 如何获取拼接到url中的请求参数值呢?
5.3 发送DELETE和PUT请求
在SpringMVC中 有一个过滤器,支持将POST请求转换为DELETE、PUT请求。
过滤器:org.springframework.web.filter.HiddenHttpMethodFilter
核心方法:

从过滤器方法中得出,我们可以在页面上用表单方式发送POST请求,在携带的请求参数中添加一个名为_method的参数,值为DELETE或PUT。
如下:
<form action="/user" method="POST">
    id:<input type="text" name="id"><br/>
    姓名:<input type="text" name="name"><br/>
    <!--请求方式参数,用户不需要看到,故设置为隐藏-->
    <input type="hidden" name="_method" value="PUT"><br/>
    <input type="submit" value="更新">
</form>
然后在项目中注册该过滤器即可将POST请求转换为PUT请求。
在application.properties中注册HiddenHttpMethodFilter过滤器:
spring.mvc.hiddenmethod.filter.enabled=true
此外,我们也可以通过ajax发送DELETE或PUT请求,但只有部分浏览器支持。
5.4 RESTful中的注解
在Spring Boot中开发RESTful主要由如下几个注解实现
① @PathVariable:获取拼接到url中的参数数据,是实现RESTful最主要的一个注解
② @GetMapping:接收和处理GET方式的请求,等同于 @RequestMapping(method=RequestMethod.GET)
③ @PostMapping:接收和处理POST方式的请求,等同于 @RequestMapping(method=RequestMethod.POST)
④ @DeleteMapping:接收和处理DELETE方式的请求,等同于 @RequestMapping(method=RequestMethod.DELETE)
⑤ @PutMapping:接收和处理PUT方式的请求,等同于 @RequestMapping(method=RequestMethod.PUT)
5.4 注解使用练习
以4.4中的例子为基础进行修改。
①在application.properties中注册HiddenHttpMethodFilter过滤器:
#设置端口号
server.port=9090
#注册HiddenHttpMethodFilter过滤器
spring.mvc.hiddenmethod.filter.enabled=true
②编写前端页面
在resources/static目录下新建index.html,发送crud四种请求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">
    <h3>查询</h3>
    <a href="/student/1001">查询</a>
    <hr>
    <h3>删除</h3>
    <form action="/student/1007" method="POST">
        <input type="hidden" name="_method" value="DELETE"><br>
        <input type="submit" value="删除"><br/>
    </form>
    <hr>
    <h3>添加</h3>
    <form action="/student" method="POST">
        id:<input type="text" name="id"><br/>
        姓名:<input type="text" name="name"><br/>
        <input type="submit" value="添加"><br/>
    </form>
    <hr>
    <h3>更新</h3>
    <form action="/student" method="POST">
        id:<input type="text" name="id"><br/>
        姓名:<input type="text" name="name"><br/>
        <input type="hidden" name="_method" value="PUT"><br/>
        <input type="submit" value="更新"><br/>
    </form>
</div>
</body>
</html>
③编写Controller
package com.tsccg.controller;
@RestController
public class StudentController {
    /**
     * 根据id获取学生信息
     * @PathVariable
     * 1.作用:获取url中的数据
     * 2.位置:处理器形参前
     * 3.value:路径变量值 {studentId}
     */
    @GetMapping("/student/{studentId}")//处理GET请求
    public String findStudent(@PathVariable(value="studentId") Integer id) {
        return "执行查询操作,id="+id;
    }
    /**
     * 添加学生
     */
    @PostMapping("/student")//处理POST请求
    public String addStudent(@RequestParam Map<String,String> student) {
        return "执行添加操作,student="+student;
    }
    
    /**
     * 根据id删除学生信息
     */
    @DeleteMapping("/student/{studentId}")//处理DELETE请求
    public String deleteStudent(@PathVariable("studentId") Integer id) {
        return "执行删除操作,id="+id;
    }
    
    /**
     * 更新学生信息
     */
    @PutMapping("/student")//处理PUT请求
    public String updateStudent(@RequestParam Map<String,String> student) {
        return "执行更新操作,student="+student;
    }
}
④测试
开启应用,从浏览器访问 http://localhost:9090/index.html

5.5 REST注意URL+请求方式必须唯一
在REST中,必须保证URL+请求方式是唯一的,若出现如下情况,会报错。
@GetMapping("/student/{studentId}")
public String findStudentById(@PathVariable(value="studentId") Integer id) {
    return "根据id查询学生信息";
}
@GetMapping("/student/{studentName}")
public String findStudentByName(@PathVariable(value="studentName") String name) {
    return "根据姓名查询学生信息";
}

第6章 Spring Boot集成Redis
6.1 Redis简介
Redis是一个NoSQL数据库,常用作缓存使用。通过Redis客户端可以使用多种语言在程序中访问Redis数据。其中,java语言使用的客户端库有:Jedis、Lettuce、Redisson等。
那么在Spring Boot中,使用的Redis客户端库是什么呢?
创建一个Spring Boot项目,勾选Web和Redis起步依赖:

查看项目导入的Redis相关依赖:

可以看出,在Spring Boot中,默认使用的Redis客户端库为lettuce
6.2 演示添加和获取操作
①创建Spring Boot项目,勾选web和redis起步依赖
其中,Spring Boot会根据redis的起步依赖在容器中创建两个对象:
- RedisTemplate
- SpringRedisTemplate
<!--Spring Boot会在容器中创建两个对象:RedisTemplate、SpringRedisTemplate-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在程序中使用RedisTemplate类的方法 操作redis数据, 实际就是调用的lettuce 客户端中的方法
②在核心配置文件application.properties中配置连接redis信息
#配置端口号
server.port=9090
#配置redis
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=123
③创建Controller
注入RedisTemplate对象,通过该对象的以ops开头的几个方法获取操作redis中各种类型数据的对象
如:
- opsForValue()---->ValueOperations---->String类型数据
- opsForHash()---->HashOperations---->Hash类型数据

然后就可以通过获取的对象执行set/get方法,管理redis中的数据了。
package com.tsccg.controller;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
public class RedisController {
	
    //注入RedisTemplate对象
    @Resource
    private RedisTemplate redisTemplate;
	
    //向redis中添加String类型数据
    @PostMapping("/student/{name}")
    public String setKey(@PathVariable String name) {
       	//获取管理redis中String类型的对象
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //执行添加操作
        valueOperations.set("name",name);
        return "添加了学生:"+name;
    }
	
    //从redis中获取添加的数据
    @GetMapping("/student")
    public String getValue() {
        //获取管理redis中String类型的对象
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //执行获取操作
        return (String)valueOperations.get("name");
    }
}
④测试
1)开启windows版redis服务

2)开启项目服务,通过postman客户端软件,以POST方式发送: http://localhost:9090/student/小明

3)再以GET方式发送请求:http://localhost:9090/student

4)通过Redis Desktop Manager桌面工具查看刚刚插入的数据

发现在redis中添加的数据为序列化的数据。
⑤修改Controller,注入StringRedisTemplate对象
package com.tsccg.controller;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@RestController
public class RedisController {
    //修改注入StringRedisTemplate对象
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    //修改添加的数据为age
    @PostMapping("/student/{age}")
    public String setKey(@PathVariable String age) {
        //通过StringRedisTemplate对象获取操作Stirng类型数据的对象
        ValueOperations valueOperations = stringRedisTemplate.opsForValue();
        valueOperations.set("age",age);
        return "添加了年龄:"+age;
    }
	//修改获取的数据为age
    @GetMapping("/student")
    public String getValue() {
        ValueOperations valueOperations = stringRedisTemplate.opsForValue();
        return (String)valueOperations.get("age");
    }
}
⑥重新测试
重启应用,重新发送添加请求:

再次查看redis库中存入的数据:

发现这次在redis中存入的数据是正常的。
6.3 对比RedisTemplate和StringRedisTemplate
RedisTemplate:把key和value经过序列化存到redis中,key和value是序列化的内容,不能直接识别。默认使用的是jdk的序列化方式,可以修改为其它的系列化方式。
StringRedisTemplate:把key和value作为String处理,使用的是String的序列化,可读性好。
序列化与反序列化:
- 序列化:把对象转换为可传输的字节序列过程就叫序列化
- 反序列化:把字节序列还原为对象的过程就叫反序列化。
为什么要进行序列化?
 我们在实际项目开发中,使用的redis都是放在linux上的,而为了让数据对象可以从其它地方跨平台存放到linux系统上去,就必须将对象序列化。
 序列化最终的目的就是为了让对象可以跨平台存储,可以通过网络传输。而我们进行跨平台存储和网络传输的方式就是IO,IO支持的数据格式就是字节数组。我们必须在把对象转换为字节数组前就指定一种规则(序列化),那么我们从IO流读取数据的时候再以这种规则把对象还原。(反序列化)
序列化的常见方式:
 序列化只是一种拆装组装对象的规则,这种规则也多种多样。比如现在常用的序列化方式有:JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protofbuff 等。
- 
jdk的序列化: 把java对象转为byte[], 二进制数据 
- 
json序列化:json序列化能将对象转换为 JSON 格式或从 JSON 格式转换为对象。例如把一个Student对象转换为JSON字符串{"name":"李四", "age":29} ),反序列化(将JSON字符串 {"name":"李四", "age":29} 转换为Student对象) 
6.4 设置key和value的序列化方式
Redis数据序列化方式有:

其中,默认的序列化方式为jdk的序列化,将key和value转换为二进制字节数组。
6.4.1 设置为String序列化方式
①修改Controller
@RestController
public class RedisController {
    @Resource
    private RedisTemplate redisTemplate;
    @PostMapping("/student/{key}/{value}")
    public String setKey(@PathVariable String key,@PathVariable String value) {
        // 使用RedisTemplate ,在存取值之前,设置序列化
        //设置key的序列化方式为String
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置value的序列化方式为String
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set(key,value);
        return "添加:" + "key=" + key + ",value=" + value;
    }
    @GetMapping("/student/{key}")
    public String getValue(@PathVariable String key) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        return (String)valueOperations.get(key);
    }
}
②发送POST请求:http://localhost:9090/student/email/123@qq.com

③查看redis库:

6.4.2 设置为JSON序列化方式
①设置Idea自动生成序列化版本号

②创建实体类,实现序列化接口,在实体类中自动生成序列化版本号

③修改Controller,设置value为JSON序列化方式
@Resource
private RedisTemplate redisTemplate;
@PostMapping("/student")
public String addJson() {
    //创建一个Student对象
    Student student = new Student();
    student.setName("Tom");
    student.setAge(40);
    student.setEmail("tom@163.com");
    //设置key的序列化方式为String
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    //设置value的序列化方式为JSON
    redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
    //添加操作
    redisTemplate.opsForValue().set("myStudent",student);
    return "添加:"+student.toString();
}
@GetMapping("/student")
public String getValue() {
    //设置key的序列化方式为String
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    //设置value的序列化方式为JSON
    redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Student.class));
    ValueOperations valueOperations = redisTemplate.opsForValue();
    return valueOperations.get("myStudent").toString();
}
④发送post请求: http://localhost:9090/student (序列化)

⑤查看redis库

⑥发送get请求: http://localhost:9090/student 反序列化

第7章 Spring Boot集成Dubbo
7.1 创建父模块
创建一个普通maven模块作为父模块,模块名:16-dubbo-parent
进行如下操作:
1)删除src目录
2)修改pom.xml,指定打包方式为pom
3)继承spring-boot-starter-parent模块
4)添加dubbo和zookeeper起步依赖
<?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>
    <!--继承Spring Boot父模块-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.1</version>
        <relativePath/> 
    </parent>
    <groupId>com.tsccg</groupId>
    <artifactId>16-dubbo-parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging><!--指定打包方式为pom-->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--添加dubbo起步依赖-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>
        <!--添加zookeeper依赖-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>2.7.8</version>
            <type>pom</type>
            <!--dubbo起步依赖中已经包含了log4j依赖,排除zookeeper依赖中包含的重复依赖-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
7.2 创建公共接口模块
创建一个普通maven模块,无父模块,在此模块中只定义公共的接口和实体类。
模块名:common-interface
GAV坐标:
<groupId>com.tsccg</groupId>
<artifactId>common-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
创建实体类Student,实现序列化接口
package com.tsccg.pojo;
import java.io.Serializable;
public class Student implements Serializable {
    private String name;
    private Integer age;
    private String email;
	//get set
    //toString
}
创建公共接口 StudentService,定义方法
package com.tsccg.pojo.com.tsccg.service;
import com.tsccg.pojo.Student;
public interface StudentService {
    //获取Studnet数据
    Student getStudent();
}
7.3 创建服务消费者模块
创建普通maven模块:server-consumer,继承父模块 16-dubbo-parent
然后进行如下操作:
①在pom.xml中添加依赖
1)声明Spring Boot的web起步依赖
2)添加公共接口模块依赖
<dependencies>
    <!--声明web起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--添加公共接口模块依赖-->
    <dependency>
        <groupId>com.tsccg</groupId>
        <artifactId>common-interface</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>
②在resources目录下创建application.properties核心配置文件,配置dubbo
#服务暴露接口
server.port=8081
##配置dubbo
#服务名称
spring.application.name=server-consumer
#扫描dubbo注解所在包
dubbo.scan.base-packages=com.tsccg.service
#指定注册中心地址,此处用的是本地的zookeeper
dubbo.registry.address=zookeeper://localhost:2181
③在java目录下创建com.tsccg.Application 启动类
添加开启Dubbo的注解:@EnableDubbo
package com.tsccg;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo//开启dubbo
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}
④创建Controller
使用@DubboReference注解远程注入StudentService对象
package com.tsccg.controller;
import com.tsccg.pojo.Student;
import com.tsccg.pojo.com.tsccg.service.StudentService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StudentController {
    @DubboReference(version = "1.0")//dubbo远程注入
    private StudentService  studentService;
    @GetMapping("/student")
    public String getStudent() {
        //调用服务方法
        Student student = studentService.getStudent();
        return student.toString();
    }
}
7.4 创建服务提供者模块
同服务消费者,创建一个普通maven模块:server-provider,继承父模块
进行如下操作:
①添加依赖
<dependencies>
    <!--声明web起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--公共接口依赖-->
    <dependency>
        <groupId>com.tsccg</groupId>
        <artifactId>common-interface</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>
②创建核心配置文件:application.properties,配置dubbo
#暴露服务接口
server.port=8080
##配置dubbo
#服务名称
spring.application.name=server-provider
#扫描dubbo注解所在包
dubbo.scan.base-packages=com.tsccg.service
#指定注册中心地址
dubbo.registry.address=zookeeper://localhost:2181
③创建启动类:Application,添加开启dubbo注解
package com.tsccg;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo//开启dubbo
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
④创建服务实现类
package com.tsccg.service.impl;
import com.tsccg.pojo.Student;
import com.tsccg.pojo.com.tsccg.service.StudentService;
import org.apache.dubbo.config.annotation.DubboService;
//使用Dubbo提供的@DubboService注解,指定接口class为StudentService.class
@DubboService(interfaceClass = StudentService.class,version = "1.0")
public class StudentServiceImpl implements StudentService{
    @Override
    public Student getStudent() {
        //创建一个Student对象
        Student student = new Student();
        student.setName("杰瑞");
        student.setAge(30);
        student.setEmail("jerry@qq.com");
        return student;
    }
}
7.5 测试
①开启zookeeper

②先后运行服务提供者和服务消费者应用

③在浏览器中发送请求: http://localhost:8081/student

第8章 Spring Boot打包方式
Spring Boot 可以打包为 war 或 jar 文件。 以两种方式发布应用。
8.1 打war包
步骤:
1.创建一个Spring Boot项目,添加Web起步依赖
2.修改pom.xml
1)添加内嵌 Tomcat 对 jsp 的解析包依赖
<!--使用jsp-->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
2)指定jsp编译目录
<build>
    <!--jsp 文件必须编译到指定的 META-INF/resources 目录下-->
    <resources>
        <resource>
            <!--源文件位置-->
            <directory>src/main/webapp</directory>
            <!--指定编译到 META-INF/resource,该目录不能随便写-->
            <targetPath>META-INF/resources</targetPath>
            <!--指定要把哪些文件编译进去,**表示 webapp 目录及子目录,*.*表示所有文件-->
            <includes>
                <include>**/*.*</include>
            </includes>
        </resource>
        <!--把src/main/resources下面的所有文件,都包含到classes目录-->
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.*</include>
            </includes>
        </resource>
    </resources>
</build>
3)指定打包方式为war

4)指定打包后生成war包的名称
在build标签内添加如下语句:
<!--指定最终打包后的包名-->
<finalName>myBoot</finalName>
5)完整的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.6.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.tsccg</groupId>
    <artifactId>17-package-war</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging><!--指定打包类型为war-->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--添加jsp依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
    </dependencies>
    <build>
        <!--jsp 文件必须编译到指定的 META-INF/resources 目录下-->
        <resources>
            <resource>
                <!--源文件位置-->
                <directory>src/main/webapp</directory>
                <!--指定编译到 META-INF/resource,该目录不能随便写-->
                <targetPath>META-INF/resources</targetPath>
                <!--指定要把哪些文件编译进去,**表示 webapp 目录及子目录,*.*表示所有文件-->
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <!--把src/main/resources下面的所有文件,都包含到classes目录-->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
        <!--指定最终打包后的包名-->
        <finalName>myBoot</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
3.创建webapp并引入项目,在其中创建index.jsp作为视图

4.配置视图解析器
在核心配置文件 application.properties中添加如下内容:
#前缀
spring.mvc.view.prefix=/
#后缀
spring.mvc.view.suffix=.jsp
5.创建Controller
package com.tsccg.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class WarController {
    @RequestMapping("/doSome")
    public String doSome(Model model) {
        model.addAttribute("data","war包");
        return "index";
    }
}
6.测试,开启应用,在浏览器中访问 http://localhost:8080/doSome

7.让启动类继承SpringBootServletInitializer
只有继承了此类,重写其configure方法后,生成的war包才能单独部署到外部的服务器中。
SpringBootServletInitializer就是原有的web.xml文件的替代。使用了嵌入式Servlet,默认不支持jsp。
package com.tsccg;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }
}
8.部署至外部独立服务器
1)将生成的war包部署到外部的Tomcat服务器中
通过maven执行clean---->package后,将target 目录下的 war 文件拷贝到 tomcat 服务器 webapps 目录中

2)启动Tomcat

3)在浏览器中访问 http://localhost:8080/myBoot/doSome

多出的/myBoot是由于我们开启tomcat后,会自动将war文件解压缩,我们的项目文件都在解压缩的文件夹中。

8.2 打jar包
以前面打war包的例子为基础进行修改。
1.修改pom.xml
1)指定打包方式为jar
默认打包类型就是jar,删除原先指定的war包语句即可

2)指定springboot-maven-plugin版本
打包jar,有jsp文件时,必须指定maven-plugin插件的版本是 1.4.2.RELEASE
<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!--指定maven-plugin插件的版本为1.4.2.RELEASE-->
        <version>1.4.2.RELEASE</version>
    </plugin>
</plugins>
3)修改Controller
@RequestMapping("/doOther")
public String doOther(Model model) {
    model.addAttribute("data","jar包");
    return "index";
}
4)修改主启动类不继承SpringBootServletInitializer
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
2.执行打包,通过cmd窗口执行命令运行jar包,启动内置的Tomcat
1)通过maven执行clean-->package,生成myBoot.jar
2)进入myBoot.jar包所在目录,打开cmd窗口

3)运行启动命令,启动内置的Tomcat
java -jar myBoot.jar

4)在浏览器中发送请求 http://localhost:8080/doOther

8.3 war包部署与jar包部署的区别
war包必须部署到独立的外部服务器上,占用资源较多。但独立的服务器功能较多,这种方式能更好的利用服务器。
jar包可以通过内置的tomcat单独运行,占用资源少。但内置的服务器功能较少,性能不如war包方式。
8.4 Spring Boot项目部署和运行方式总结
8.4.1 开发阶段
在IDEA中直接运行主启动类的main方法。
8.4.2 上线部署阶段
1.打jar包
在IDEA中通过maven插件将项目打成jar包,可用java -jar xxx.jar命令启动内置的Tomcat。
上线部署到Linux系统上时,可以将该命令封装到一个Shell脚本中,步骤如下:
①在jar包同级目录里创建一个shell脚本,编写如下内容
run.sh:
#!/bin/sh
java -jar xxx.jar
②赋予权限
chmod 777 run.sh
③启动shell脚本
./run.sh
2.打war包
在IDEA中通过maven插件将项目打成war包,单独部署到tomcat等服务器的发布目录下运行


 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号