Spring Boot学习笔记

Spring Boot学习笔记

视频出处:动力节点springboot视频教程

为什么要使用Spring Boot?

  1. 使用Spring和SpringMVC时需要用到大量的xml配置文件,配置各种对象,把使用的对象放入到Spring容器中才能使用对象,十分繁琐。同时,用Spring整合其他框架时,需要了解其他框架的配置规则。
  2. 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容器中。

优点:

  1. 避免繁琐的xml配置。
  2. 可以使用面向对象的方式,一个配置类可以继承另一个配置类,可以重写方法。

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配置容器

需要使用两个注解:

  1. @Configuration:放在类上,声明当前类为配置类。
  2. @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容器。

特性:

  1. Create stand-alone Spring applications
  2. Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
  3. Provide opinionated 'starter' dependencies to simplify your build configuration
  4. Automatically configure Spring and 3rd party libraries whenever possible
  5. Provide production-ready features such as metrics, health checks, and externalized configuration
  6. Absolutely no code generation and no requirement for XML configuration

翻译:

  1. 可以独立创建一个Spring应用程序
  2. 内嵌有Tomcat、Jetty或Undertow(无需部署WAR文件),可以单独启动一个web应用。
  3. 提供了starter初始依赖以简化项目的构建配置。如在ssm项目中,整合MyBatis框架需要在Spring配置文件中配置MyBatis的对象:DataSource数据源、SqlSessionFactory、Dao代理对象。而在Spring Boot项目中,在pom.xml中加入一个mybatis-spring-boot-starter依赖即可。
  4. 尽可能地自动配置Spring以及第三方库。就是把Spring和第三方库中的对象都创建好,放到容器中,开发人员可以直接使用。(如SpringMVC的中央调度器,MyBatis框架的对象)
  5. 提供了生产准备功能,如统计(运行时长等)、健康检查(监控项目是否正常运转)和外部化配置(类如属性配置文件)
  6. 绝对不会生成代码,无需xml配置文件

2.2 创建Spring Boot项目

2.2.1 方式1:使用Spring Boot提供的初始化器

国外地址:https://start.spring.io/

国内地址: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目录下创建statictemplates文件夹,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三个注解:

  1. @SpringBootConfiguration:内部包含@Configuration注解,作用是声明当前类为配置类,可当作配置文件。
  2. @EnableAutoConfiguration:开启自动配置,把一些java对象配置好,注入Spring容器中。
  3. @ComponentScan:组件扫描器,找到注解,根据注解的功能创建对象,给属性赋值等等。组件扫描器默认扫描的是 @ComponentScan 注解所在的类,类所在的包和子包。

2.5 Spring Boot核心配置文件

Spring Boot 的核心配置文件用于配置 Spring Boot 程序。

名字必须以 application 开始,后缀有两种格式:

  1. .properties

    student.name=张三
    student.age=21
    
  2. .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 给我们提供了两个接口来帮助我们实现这种需求,使用任意一个都可以

  1. CommandLineRunner
  2. ApplicationRunner

它们的执行时机为容器启动完成的时候,这两个接口中都有一个 run 抽象方法,我们只需要实现这个方法即可。这两个接口的不同之处在于 :

  1. ApplicationRunner接口中的run方法参数为 ApplicationArguments
  2. 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组件

三个内容:

  1. 拦截器 HandlerInterceptor
  2. Servlet
  3. 过滤器 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;
    }
}

②注册拦截器

  1. 创建java类实现WebMvcConfigurer接口,实现其addInterceptors方法,用@Configuration注解修饰该类
  2. 将自定义的拦截器对象注册到项目中,设置拦截和放行的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数据库。

建表:

步骤分析:

  1. 创建Spring Boot项目,勾选Web、MyBatis、MySQL Driver起步依赖
  2. 在application.properties中配置数据库连接信息
  3. 创建实体类Student
  4. 创建Controller,接收浏览器请求,访问Service
  5. 创建Service接口及其实现类,调用Dao接口方法
  6. 创建Dao接口,添加查询方法
  7. 在Dao接口同级目录下创建对应Mapper文件,指定namespace,写sql语句
  8. 在pom.xml中指定把src/main/java目录中的xml文件包含到classpath中

其中,创建Dao代理对象的方式有两种:

  1. 在每个Dao接口上添加@Mapper
  2. 在启动类上添加@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目录下。

有两种分离方式:

  1. 第一种:在resources目录下创建com.tsccg.dao目录,使得编译后将mapper文件和Dao.class文件放在一起
  2. 第二种:在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提供的事务管理。

使用步骤:

  1. 在入口类上添加 @EnableTransactionManagement注解开启事务支持(默认开启,但最好添上)
  2. 在访问数据库的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命名规范化,统一化。

表现层资源状态转移 概念说明:

  1. 表现层:就是视图层,显示资源的,通过视图页面、jsp等显示操作资源的结果。
  2. 资源:服务器端的动静态资源文件,数据库表中的数据等都是资源。每个资源都是服务器上一个可命名的抽象概念,是以名词为核心来组织的,如用user表示用户在服务端数据库的信息。一个资源可由一个或多个url来标识,url既是资源的名称,也是Web上的地址。在浏览器等客户端上,可以通过资源的url与其进行交互。
  3. 资源状态:就是对于资源的表述,当我们通过浏览器访问一个视频、一段文字或一张图片时,对应资源的表述格式是不一样。
  4. 资源状态转移:资源状态转移说的是资源在客户端和服务端之间转移(请求资源-响应资源)的表述。通过转移和操作资源的表述来实现操作资源的目的。

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都是表示操作方式的动词。

它们分别对应四种基本操作:

  1. GET:获取资源
  2. POST:新建资源
  3. DELETE:删除资源
  4. 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		//更新用户信息

现在问题有两个问题:

  1. 浏览器只支持GET和POST方式的请求,如何发送DELETE和PUT请求呢?
  2. 如何获取拼接到url中的请求参数值呢?

5.3 发送DELETE和PUT请求

在SpringMVC中 有一个过滤器,支持将POST请求转换为DELETE、PUT请求。

过滤器:org.springframework.web.filter.HiddenHttpMethodFilter

核心方法:

从过滤器方法中得出,我们可以在页面上用表单方式发送POST请求,在携带的请求参数中添加一个名为_method的参数,值为DELETEPUT

如下:

<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的起步依赖在容器中创建两个对象:

  1. RedisTemplate
  2. 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的序列化,可读性好。

序列化与反序列化:

  1. 序列化:把对象转换为可传输的字节序列过程就叫序列化
  2. 反序列化:把字节序列还原为对象的过程就叫反序列化。

为什么要进行序列化?

​ 我们在实际项目开发中,使用的redis都是放在linux上的,而为了让数据对象可以从其它地方跨平台存放到linux系统上去,就必须将对象序列化。

​ 序列化最终的目的就是为了让对象可以跨平台存储,可以通过网络传输。而我们进行跨平台存储和网络传输的方式就是IO,IO支持的数据格式就是字节数组。我们必须在把对象转换为字节数组前就指定一种规则(序列化),那么我们从IO流读取数据的时候再以这种规则把对象还原。(反序列化)

序列化的常见方式:

​ 序列化只是一种拆装组装对象的规则,这种规则也多种多样。比如现在常用的序列化方式有:JDK(不支持跨语言)、JSON、XML、Hessian、Kryo(不支持跨语言)、Thrift、Protofbuff 等。

  1. jdk的序列化: 把java对象转为byte[], 二进制数据

  2. 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等服务器的发布目录下运行

posted @ 2021-12-21 00:48  TSCCG  阅读(169)  评论(0编辑  收藏  举报