JavaWeb基础:SpringBoot

文章发布时间:

最后更新时间:

文章总字数:
4.9k

预计阅读时间:
19 分钟

JavaWeb基础:SpringBoot

本着既不最新也不最旧的原则,这里先研究研究SpringBoot2,而不是1或者3(示例代码有些许不同,而且3只支持JDK 17+),SpringBoot2要求JDK 1.8+。

SpringBoot简介

Spring Framework 基础组件代码很少,但是配置依然很困难,而 Spring Boot 框架可以用于简化 Spring 框架从搭建到开发的过程。

Spring Boot 强调只需要很少的配置文件,所以在开发生产级 Spring 应用中,让开发变得更加高效和简易。应用开箱即用,只要通过一个指令,包括命令行 java -jar 、SpringApplication 应用启动类 、 Spring Boot Maven 插件等,就可以启动应用了。

SpringMVC

参考文章:
SpringMVC【入门篇】 - 知乎 (zhihu.com)纯注解版SpringMVC(无web.xml配置文件)项目创建_springmvc无web.xml-CSDN博客
使用注解开发SpringMVC详细配置教程_springmvc 纯注解开发-CSDN博客

为了更好入门学习 Spring Boot ,我们首先看一下如何使用 SpringMVC 框架搭建 web 应用。SpringMVC 就是一个 Spring 内置的 MVC 框架(和之前接触过的 Struts 一样),采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。SpringMVC 底层就是 Servlet,SpringMVC 就是对 Servlet 进行深层次的封装。

SpringMVC处理逻辑

顺便回顾一下 MVC 设计模式:
Model-View-Controller,即控制器(Controller),模型(Model),视图(View)

使用 MVC 模式的好处是,Controller 专注于业务处理,它的处理结果就是 Model 。Model可以是一个 JavaBean,也可以是一个包含多个对象的 Map ,Controller 只负责把 Model 传递给 View,View 只负责把 Model 给“渲染”出来,这样,三者职责明确,且开发更简单,因为开发 Controller 时无需关注页面,开发 View 时无需关心如何创建 Model。

借张网图看一下 SpringMVC 的执行流程,和 Struts 差不多。
image.png

涉及的六个组件的分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1、前端控制器DispatcherServlet(不需要程序员开发,但是需要程序员进行配置)由框架提供,在web.xml中配置。
作用:接收请求,响应结果,相当于转发器,中央处理器。

2、处理器映射器HandlerMapping(不需要程序员开发)由框架提供。
作用:根据请求的url查找Handler(处理器/Controller),可以通过XML和注解方式来映射。

3、处理器适配器HandlerAdapter(不需要程序员开发)由框架提供。
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler中的方法。

4、处理器Handler(也称之为Controller,需要程序员开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
作用:接受用户请求信息,调用业务方法处理请求,也称之为后端控制器。

5、视图解析器ViewResolver(不需要程序员开发)由框架提供。
作用:进行视图解析,把逻辑视图解析成真正的物理视图。
SpringMVC框架支持多种View视图技术,包括:jstlView、freemarkerView、ThymeleafView等。

6、视图View(需要程序员开发)
作用:把数据展现给用户的页面
View是一个接口,实现类支持不同的View技术(jsp、freemarker、pdf等)

总结来说,程序员需要干三件事:

  • 配置前端控制器DispatcherServlet
  • 开发后端处理器Handler
  • 开发视图View

第一个SpringMVC项目

这里采用的方式是不使用注解,后面会使用注解再实现一遍。

首先需要导入依赖,导入的是 SpringMVC 的核心包和 Servlet 依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>

然后需要定义控制器类:在 java 源代码目录下创建,这里有一个小问题,我想新建 java 类的时候并没有找到 java 选项,解决方式很简单,将 resources 目录标记为源代码根目录。
由于我们这里采用的是无注解控制器,故需要实现 SpringMVC 提供的 Controller 接口,具体代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package cn.pazuris;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class UserController implements Controller {

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
mv.addObject("username","Pazuris");
mv.setViewName("/WEB-INF/hello.jsp");
return mv;
}
}
  1. 实现 SpringMVC 的 Controller 接口。
  2. 在控制器类中,实现handleRequest()方法,该方法将处理请求并返回一个ModelAndView对象。
  3. 在配置文件中进行配置,配置 bean ,并且指定 URL 映射。

其中,在handleRequest方法中,可以编写具体的业务逻辑来处理请求。

  1. 获取请求参数:使用HttpServletRequest对象的方法,如getParameter()getParameterValues()等,来获取请求中的参数值。
  2. 处理数据:根据请求参数进行业务处理,例如查询数据库、调用其他服务等。
  3. 设置模型数据:使用ModelAndView对象的addObject()方法,将处理结果存储在模型中,以便在视图中访问。
  4. 返回视图名称:使用ModelAndView对象的构造函数或setViewName()方法,设置要返回的视图名称。
  5. 返回重定向或转发:除了返回视图名称外,还可以返回重定向或转发的URL。同样可以使用setViewName()方法
    handleRequest()方法的返回类型为ModelAndView,它包含了视图名称和模型数据的信息,可以用于指导Spring MVC框架的视图解析和渲染过程。

新建一个 SpringMVC.xml 里面配置一下:
<bean name="/hello.do" class="cn.pazuris.UserController"/>
当用户访问 /hello.do 的时候就会使用这个控制器处理。

hello.jsp 内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String contextPath = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
%>
<html>
<head>
<base href="<%=basePath%>>">
<title>Title</title>
</head>
<%@page isELIgnored="false" %>
<body>
<h2>Hello!,${username}</h2>

</body>
</html>

${}语法是 JSP 的 EL 表达式(Expression Language,表达式语言)的一部分,用于在JSP 页面中访问和操作数据。在 Spring MVC 中,EL 表达式可以用于访问模型数据。
具体来说,当处理请求并返回ModelAndView对象时,Spring MVC 会将模型数据存储在ModelMap对象中,然后将ModelMap对象放入ModelAndView对象中。在View页面中,可以使用${}表达式来引用ModelMap中的属性。

<%@page isELIgnored="false" %>是因为我们的 web.xml 里面使用了 JSP 1.2 规范(即 DTD 定义),EL表达式的解析默认情况下是关闭的(新一点的版本默认是开启的),如果不手动开启就无法解析表达式,具体参考这篇文章:
SpringMVC中JSP页面不显示EL表达式的原因_jsp中el表达式不显示-CSDN博客

最后通过 web.xml 配置一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE web-app PUBLIC  
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 创建前端控制器的时候读取SpringMVC配置文件启动ioc容器 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
<!-- Tomcat启动就创建此对象 -->
<load-on-startup>1</load-on-startup>

</servlet>

<!-- 配置拦截路径url,所有以.do结尾的请求都会被前端控制器拦截处理 -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

</web-app>

启动 tomcat 即可访问 /hello.do:
image.png
虽然实现了,但是写 Spring.xml 要指定访问的路径和控制器比较麻烦,使用注解来解决!

注解实现项目

说实话,个人感觉注解简便在不用在 SpringMVC.xml 里面实例化一堆 bean,可以自动扫描,而将 SpringMVC.xml 和 web.xml 省掉,写在配置类里面好像也没什么特别的,代码都差不多,从 xml 转为了 java 罢了。故这里先看看控制器怎么注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package cn.pazuris;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UserController {
@RequestMapping("/hello.do")
public ModelAndView hello(){
ModelAndView mv = new ModelAndView();
mv.setViewName("/WEB-INF/hello.jsp");
mv.addObject("username","Pazuris");
return mv;
}
}

可以看到这里是通过 @Controller 开启了控制器注解,并且通过 @RequestMapping 确定了路径(还可以在类上面加这个注解,表示父级目录)。这样写了之后 SpringMVC.xml 里面就只需要一句代码,开启自动扫描即可:
<context:component-scan base-package="cn.pazuris" />
这个 base-package 就是放控制器的位置。

另外控制器如果直接返回了字符串的话,需要在配置文件中加一个视图解析器用于将这个字符串对应到相应的视图。

1
2
3
4
5
6
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前缀-->
<property name="prefix" value="/WEB-INF/"/>
<!--后缀-->
<property name="suffix" value=".jsp"/>
</bean>

可以看到视图解析器就是给你的字符串加了前缀和后缀,对应到视图文件。

再看一下如果要省掉 SpringMVC.xml ,换成一个配置类应该怎么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan("cn.pazuris")
public class AppConfig {
@Bean
public InternalResourceViewResolver viewResolver() {
// TODO Auto-generated method stub
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}

}

省不省都行,写在配置文件里反而还清晰一点,代码可读性更高。至于 web.xml 就更没什么好省的了,用原来的写法就好。

使用Rest开发Web应用

使用REST - 廖雪峰的官方网站 (liaoxuefeng.com)
在 Web 应用中除了使用 MVC 模式给用户显示页面外,还有一类 API 接口,我们称之为 REST,通常输入输出都是 JSON ,便于第三方调用或者使用页面 JavaScript 与之交互。由于不需要展示页面,就不用写视图文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/api")
public class ApiController {

@GetMapping("/users")
public List<User> getAllUsers() {
// 返回所有用户的逻辑
}

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
// 创建用户的逻辑
}

// 其他端点...
}

SpringBoot 内置了 jackson,就不用额外添加依赖了。

SpringBoot2特性

SpringBoot2常用的特性如下:

  • SpringApplication 应用类
  • 自动配置
  • 外化配置
  • 内嵌容器
  • Starter 组件

SpringApplication

SpringApplication 是 Spring Boot 应用启动类,在 main() 方法中调用 SpringApplication.run() 静态方法,即可运行一个 Spring Boot 应用,快速构建和部署应用程序:创建应用程序上下文,执行各种初始化操作并且启动嵌入式 Web 服务器。

1
2
3
4
5
6
7
8
9
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

自动配置

SpringBoot 的自动配置使用条件化配置(Conditional Configuration)的机制。它会根据应用程序的环境、类路径上的依赖和其他条件来决定是否应用某个配置。其通过提供一组默认属性来进行自动配置(不进行任何配置的情况下可以直接启动一个 SpringBoot 应用),这些配置的默认值可以在application.propertiesapplication.yml配置文件中进行自定义。

外化配置

SpringBoot 提供了外部化配置的机制,使得应用程序的配置可以从外部源进行加载,而不仅仅是硬编码在应用程序代码中。这样可以实现应用程序配置的灵活性和可扩展性,同时也方便了不同环境下的配置切换和管理。外化配置特性如下

  • 配置文件格式:主要可以通过两种格式的文件进行配置:属性文件(如.properties)或YAML文件(如.yml.yaml
  • 配置文件位置:一般会在classpath下的/config目录和应用程序的根目录中搜索配置文件。此外,还可以通过spring.config.location属性指定额外的配置文件位置。
  • 属性优先级:支持多种配置属性的来源,并按照一定的优先级顺序进行加载。首先加载内嵌在应用程序中的默认属性,然后再加载外部配置文件中的属性。高优先级的属性覆盖低优先级的属性。
  • 属性占位符:配置文件中可以使用属性占位符,用于引用其他属性的值。${key}来引用 key 的值。
  • 环境相关配置:基于Environment接口提供了环境相关的配置。可以根据不同的环境(如开发、测试、生产等)加载不同的配置文件,并使用不同的属性值。可以通过spring.profiles.active属性来指定当前的活动配置文件。
  • 命令行参数和系统属性:支持通过命令行参数或系统属性传递配置信息。可以通过命令行参数--name=value的形式或通过-Dname=value的形式设置系统属性。这样可以在启动应用程序时动态地传递配置信息。

.properties文件和.yml文件语法

.properties 文件是基于键值对的格式,每行表示一个属性,可以等号也可以冒号。

1
2
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb

.yml文件采用了一种更为简洁和易读的格式,基于缩进和层级结构。

1
2
3
4
5
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb

.properties文件采用了简单的键值对格式,适合于简单的配置场景。而.yml文件采用了更为结构化和易读的格式,适合于复杂的配置场景,并支持层级关系和列表等特性。

内嵌容器

内嵌容器使得将应用程序打包成独立的可执行 JAR 文件成为可能,无需依赖外部的Web服务器(也就是说对方电脑上没有安装独立的 Web 服务器的时候也可以直接启动运行 SpringBoot 项目,监听指定端口,处理 HTTP 请求,这也太强了吧)。同时,内嵌容器还提供了简单的配置和管理方式,使得开发、测试和部署应用程序变得更加方便。

在使用内嵌容器时,只需添加对应的容器 Starter 组件依赖,即可配置并使用对应内嵌容器实例。默认情况下是自动启动了内嵌容器 Tomcat,并且自动设置了默认端口为 8080。

Starter组件

在构建Spring Boot应用程序时,可以通过在构建工具(如Maven或Gradle)的配置文件中添加对应的Starter依赖,来引入所需的功能和组件,Starter组件的命名规则是以spring-boot-starter-为前缀,后跟特定的功能或场景名称。一添加马上就能用,非常方便。(其实就是把原来一项一项在Maven配置的依赖集成了一下,集成成各个功能包,更容易管理)
比如下面这个集成了 SpringMVC 的依赖,并且还内置了 tomcat 服务器

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

SpringBoot入门工程

这里分析一下最基本的 web 工程怎么构建,由于 SpringBoot 真的开箱即用,IDEA 新建项目的时候直接选择 Spring 项目,创建好项目之后基本的都自带了,只需要我们写控制器,加资源文件就行(或者按照自己的需要改一下 application.properties)。

先看一下目录结构(自己测试的时候加了个 html 和控制器)
image.png

可以看到甚至已经写好 run 了,你只需要运行一下就可以启动项目。

在 java 源文件夹下面加一个 HelloController 处理一下访问路径的逻辑,这里关于控制器绑定视图,我们有四种选择:

  1. 使用 RestController 注解而非 Controller 注解直接返回数据不用视图
  2. 使用 ModelAndView 手动绑定视图,并返回 ModelAndView 对象
  3. 使用 thymeleaf (需要添加依赖)解析为 templates 文件夹下对应的 html(可以自定义名字和路径)
  4. 使用 jasper (需要添加依赖)解析为自定义路径下的 jsp 文件

第一第二种就不用多说了,看看第三第四种相应的配置:

html页面配置

先看第三种,这个 thymeleaf 是一个模版引擎,可以将数据动态渲染到 Web 页面中(基于 HTML ),深入了解可以看这篇文章:
https://juejin.cn/post/6844903670568452110?searchId=202309131647055F239A672CFD19351FC4

加个依赖:

1
2
3
4
<dependency>  
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

之前也说过了,在 SpringBoot 中添加依赖,你完全不用管依赖的版本,SpringBoot 会帮你自动选择适配的。

然后在 templates 下面加 html 文件,默认情况就是到这里找,控制器的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.pazuris.springbootdemo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}

然后直接启动应用,访问 /hello 即可看到 hello.html 的内容。

jsp页面配置

https://blog.csdn.net/austo/article/details/80430464

这个访问 jsp 就是最经典的 SpringMVC 了,在 SpringBoot 中使用 SpringMVC 非常简单,配置文件全都不用写,都给你默认好了(视图解析器要自己写,当然也可以直接在 properties 文件里面加),但是不建议在 SpringBoot 里面使用 jsp 页面,效率太低,最好使用 html 页面。

先自行在 main 下面创建一个 webapp 目录(注意这个名称是固定的,但是里面的目录可以自定义,亲测使用别的名称会404),里面放个 jsp 文件,通过项目结构将 webapp 定义为 web 资源文件夹 ,需要看到文件夹左下角有个小蓝点。

然后引入 jasper 依赖(记得把 thymeleaf 依赖注释掉):

1
2
3
4
<dependency>  
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>

最后在 application.properties 里面加上视图解析器:

1
2
spring.mvc.view.prefix=/  
spring.mvc.view.suffix=.jsp

run 启动应用访问路径即可,不需要像网上说的非要通过 maven 的 spring-boot:run 。

使用maven打包应用

最后再说一点,打包成 jar ,SpringBoot自带一个简单的spring-boot-maven-plugin插件可以用来打包,只需要在pom.xml中加入以下配置:

1
2
3
4
5
6
7
8
9
10
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

根目录执行mvn clean package打包成 jar ,然后使用 java -jar 命令就可以直接运行这个应用,并不需要 tomcat 或者其他服务器。

SpringBoot 还支持超多的 Starter 组件(如 log4j),这就涉及到 JavaWeb 更深更广的部分了,以后遇到可以再分析。