- 环境:JDK13、IDEA、SpringBoot2.5.3、maven3.8.1
目录
@PathVariable(路径变量)和 @RequestHeader(获取请求头)
@RequestParam(获取前端返回的参数)和@Cookie(获取Cookie)
八、Web原生组件注入(Servlet,Filter,Listener)
一、辅助插件
1、lombok
可以生成get,set方法,有参无参构造器,日志等的注解,引入依赖后去在idea的插件商店中搜索lombok并安装即可
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> <scope>provided</scope> </dependency>
2、spring-boot-devtools
热部署插件,改变项目代码不需要重新部署也可以生效,只需重新编译项目
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>2.5.2</version> </dependency>
3、Spring Initailizr(项目初始化向导)
在idea中new project按照提示选择需要的组件创建一个工程,自动分配目录
二、配置文件
SpringBoot项目给属性赋值的方式
1、yaml文件赋值
基本语法
- key: value;kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- '#'表示注释
- 字符串无需加引号,如果要加,''与""表示字符串内容 会被 转义/不转义
1、创建两个实体类Person和Dog
package com.kuang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Dog {
private String name;
private Integer age;
}
package com.kuang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
@AllArgsConstructor
@Data
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
注意:Person类中的@ConfigurationProperties,与配置文件绑定,如果属性值中有 \n 被单引号包围,则原样输出,双引号包围输出换行
此注解即可以引用yaml文件中对应person开头的属性的值,以此为属性赋值,下面是yaml文件内容,yaml文件语法对空格要求极为严格,以下为:对peson对象的各个属性进行赋值,yaml中对象person的首字母必须小写,属性名与实体类要么一致,要么遵循松散绑定
person:
name: Young
age: 22
happy: true
maps: {k1: v1,k2: v2}
lists:
- 1
- 2
- 你好
dog:
name: 旺财
age: 3
birth: 2021/7/22
测试:
package com.kuang;
import com.kuang.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
结果:
Person(name=Young, age=22, happy=true, birth=Thu Jul 22 00:00:00 GMT+08:00 2021, maps={k1=v1, k2=v2}, lists=[1, 2, 你好], dog=Dog(name=旺财, age=3))
当然这只是一种方式,使用@Value注解与properties文件亦可
三、Web开发
1、SpringMVC自动配置概览
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
- Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.
- 内容协商视图解析器和BeanName视图解析器
- Support for serving static resources, including support for WebJars (covered later in this document)).
- 静态资源(包括webjars)
- Automatic registration of
Converter
,GenericConverter
, andFormatter
beans.
- 自动注册
Converter,GenericConverter,Formatter
- Support for
HttpMessageConverters
(covered later in this document).
- 支持
HttpMessageConverters
(后来我们配合内容协商理解原理)
- Automatic registration of
MessageCodesResolver
(covered later in this document).
- 自动注册
MessageCodesResolver
(国际化用)
- Static
index.html
support.
- 静态index.html 页支持
- Custom
Favicon
support (covered later in this document).
- 自定义
Favicon
- Automatic use of a
ConfigurableWebBindingInitializer
bean (covered later in this document).
- 自动使用
ConfigurableWebBindingInitializer
,(DataBinder负责将请求数据绑定到JavaBean上)
2、静态资源
默认情况下,Spring Boot 提供来自类路径中名为
/static
(或/public
或/resources
或/META-INF/resources
)的目录作为静态资源目录,默认情况下,资源映射在 上
/**
,但您可以使用spring.mvc.static-path-pattern
属性对其进行调整。例如,将所有资源重新分配到/resources/**
可以实现如下:
spring.mvc.static-path-pattern=/resources/**
即为:
spring:
mvc:
static-path-pattern: /res/**
表示res文件夹下所有资源都为静态资源
注意:如果controller与静态资源重合,请求会先找controller解决,如果没有找到对应的controller,则找静态资源,若找不到静态资源,则404
3、欢迎页
它首先在配置的静态内容位置中查找index.xml文件。如果没有找到,它就会寻找一个index
模板。如果找到其中之一,它会自动用作应用程序的欢迎页面。
两种方式设置欢迎页
- 默认静态资源路径下 index.html
- ccontroller能处理/index
- 可以配置静态资源路径
- 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
spring:
# mvc:
# static-path-pattern: /res/** 这个会导致welcome page功能失效
resources:
static-locations: [classpath:/haha/]
4、常用参数注解
@PathVariable(路径变量)和 @RequestHeader(获取请求头)
可用于restful风格
/*
* @PathVariable
* 参数map默认可以返回请求参数的键值对形式
*/
@GetMapping("/a/{user}/{pwd}")
@ResponseBody
public String a(@PathVariable("user") String name,
@PathVariable("pwd") String password ,
@PathVariable Map<String,String> map ){
return name+"|"+password+"|"+map;
}
结果:
我们在次注解的底层代码中,可以找到这样一段注释
* <p>If the method parameter is {@link java.util.Map Map<String, String>}
* then the map is populated with all path variable names and values.
也就是说,当有一个参数为Map<String,String>类型时,自动用变量名和值填充映射
/*
* @PathVariable
*/
@GetMapping("/a/{user}/{pwd}")
public Map<String,Object> a(@PathVariable("user") String name,
@PathVariable("pwd") String password ,
@PathVariable Map<String,String> map,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String,String> header){
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("name",name);
hashMap.put("password",password);
hashMap.put("map",map);
hashMap.put("user-agent",userAgent);
hashMap.put("header",header);
return hashMap;
}
测试:
@RequestParam(获取前端返回的参数)和@Cookie(获取Cookie)
@RequestMapping("/b")
public Map<String,Object> hello(@RequestParam("hobby") String hobby,
@RequestParam Map<String,String> params,
@CookieValue("JSESSIONID") String JSESSIONID,
@CookieValue("JSESSIONID") Cookie cookie){
Map<String,Object> hashMap = new HashMap<>();
hashMap.put("hobby",hobby);
hashMap.put("params",params);
hashMap.put("JSESSIONID",JSESSIONID);
hashMap.put("cookie",cookie);
System.out.println(cookie.getName()+"---->"+cookie.getValue());
return hashMap;
}
测试:
@RequstBody(获取请求体【post请求才有】)
@PostMapping("/save")
public Map post(@RequestBody String body){
Map<String,Object> hashMap = new HashMap<>();
hashMap.put("requestBody",body);
return hashMap;
}
表单如下
结果:
四、视图解析与模板引擎
视图解析:SpringBoot默认不支持 JSP,需要引入第三方模板引擎技术实现页面渲染
1、模板引擎-Thymeleaf
thymeleaf简介
Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.
现代化、服务端Java模板引擎
基本语法
1、表达式
表达式名字 | 语法 | 用途 |
变量取值 | ${...} | 获取请求域、session域、对象等值 |
选择变量 | *{...} | 获取上下文对象值 |
消息 | #{...} | 获取国际化等值 |
链接 | @{...} | 生成链接 |
片段表达式 | ~{...} | jsp:include 作用,引入公共页面片段 |
2、字面量
文本值: 'one text' , 'Another one!' ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,.... 变量不能有空格
3、文本操作
字符串拼接: +
变量替换: |The name is ${name}|
4、数学运算
运算符: + , - , * , / , %
5、布尔运算
运算符: and , or
一元运算: ! , not
6、比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )
7、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
开发步骤
1、引入启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2、直接开发即可
编写一个controller
package com.young.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ViewTestController {
@GetMapping("/young")
public String young(Model model){
//model的数据会被放在request域中
model.addAttribute("msg","你好young!");
model.addAttribute("link","http://www.4399.com");
return "success";
}
}
编写跳转的网页success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${msg}">哈哈</h1>
<a href="http://www.atguigu.com" th:href="${link}">去4399【1】</a>
<a href="http://www.atguigu.com" th:href="@{link}">去4399【2】</a>
</body>
</html>
测试
点击‘去4399【1】’
成功跳转
点击‘去4399【2】’
404错误,将按钮改成:
<a href="http://www.atguigu.com" th:href="@{http://www.4399.com}">去4399【2】</a>
可以跳转成功,以此区分@{}与${},前者是直接取值,后者是动态取值
页面跳转
利用重定向防止登录表单信息的重复提交
package com.young.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class IndexController {
//去登录页
@GetMapping({"/","/login"})
public String loginPage(){
return "login";
}
//处理登录请求
@PostMapping("/login")
public String login(String username, String pwd){
//重定向,防止表单重复提交
return "redirect:main.html";
}
@GetMapping("/main.html")
public String main(){
//刷新只会刷新main页面,而不会重复刷新login请求
return "main";
}
}
注意:SpringBoot中请求必须经过视图解析器,所有需要两个请求方法完成重定向
检查用户是否登录
//处理登录请求
@PostMapping("/login")
public String login(User user, HttpSession session,Model model){
if (!user.getUserName().isEmpty() && "root".equals(user.getPwd())){
//重定向,防止表单重复提交
session.setAttribute("loginUser",user);
return "redirect:main.html";
}
model.addAttribute("msg","用户名或密码错误!");
return "login";
}
@GetMapping("/main.html")
public String main(HttpSession session, Model model){
//是否登录,拦截器
User user = (User) session.getAttribute("loginUser");
if (user != null){
//刷新只会刷新main页面,而不会重复刷新login请求
return "main";
}else {
model.addAttribute("msg","信息已过期,重新登路");
return "login";
}
}
抽取公共部分
可以在抽取出的common页面(示例)的某标签中使用 th:fragment="名称"或者id为一个片段取名称,然后在要引入的页面内使用一下三种方式引用
注意:div标签为示例,common为公共页面名称(省略html后缀),两个冒号后面的参数为片段名
<div th:insert="common :: 名称/#id"></div>
效果:head中有属性th:fragment,直接将head标签整个插入到页面中
<div><head>
<!--common-->
<link href="/css/style.css" rel="stylesheet">
<link href="/css/style-responsive.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="/js/html5shiv.js"></script>
<script src="/js/respond.min.js"></script>
<![endif]-->
</head></div>
<div th:replace="common :: 名称/#id"></div>
效果:head中有属性th:fragment,直接替换掉了div标签
<head>
<!--common-->
<link href="/css/style.css" rel="stylesheet">
<link href="/css/style-responsive.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="/js/html5shiv.js"></script>
<script src="/js/respond.min.js"></script>
<![endif]-->
</head>
<div th:include="common :: 名称/#id"></div>
效果:head中有属性th:fragment,head标签没了,直接将head的内容插入
<div>
<!--common-->
<link href="/css/style.css" rel="stylesheet">
<link href="/css/style-responsive.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="/js/html5shiv.js"></script>
<script src="/js/respond.min.js"></script>
<![endif]-->
</div>
表格的遍历
th:each方法
在请求中放入一些User对象返回给前端
@RequestMapping("/dynamic_table")
public String dynamic_table(Model model){
List<User> users = Arrays.asList(new User("root1", "123"), new User("root2", "123"),
new User("root3", "123"), new User("root4", "123"));
model.addAttribute("users",users);
return "table/dynamic_table";
}
如下方式进行遍历
<tr class="gradeX" th:each="user:${users}">
<td ></td>
<td th:text="${user.userName}"></td>
<td th:text="${user.pwd}"></td>
</tr>
其中,user为得到的users的单个对象名(自定义),用法与jsp中差不多,此外,还有如下用法
当我们想要对遍历出的对象计数输出在前端页面时,写法如下:
如图,可以在user对象后面加逗号,任意定义一个名字,用${}调用,可以调用其方法,我们调用count计数输出
<tr class="gradeX" th:each="user,sta:${users}">
<td th:text="${sta.count}"></td>
<td th:text="${user.userName}"></td>
<td th:text="${user.pwd}"></td>
</tr>
成功
五、 拦截器
写一个类实现HandlerInterceptor接口,并实现接口中的三个方法
/**
* 登录检查
* 1、配置好拦截器要拦截哪些请求
* 2、把这些配置放在容器中
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页
request.setAttribute("msg","请先登录");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
/**
* 目标方法执行完成以后
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
/**
* 页面渲染以后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
}
在SpringMVC中,配置拦截器在配置文件中,这里可以用如下方法配置:
创建一个Config类,实现WebMvcConfigurer,关于mvc的配置都需要继承此类,实现类中的addInterceptor方法注册拦截器,并且注明拦截规则
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
}
}
六、文件上传
前端代码如下:
<div class="form-group">
<label for="exampleInputFile">头像</label>
<input name="headImg" type="file" id="exampleInputFile">
</div>
<div class="form-group">
<label for="exampleInputFile">生活照</label>
<input name="photos" type="file" multiple>
</div>
当要选择多个图片时,加上属性multiple
@RequestMapping(value = "/upload",method = RequestMethod.POST)
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
@RequestPart("headImg") MultipartFile headImg,
@RequestPart("photos") MultipartFile[] photos) throws IOException {
if (!headImg.isEmpty()){
//如果上传来的照片非空,保存到文件服务器,OOS服务器
headImg.transferTo(new File("E:\\SpringBoot\\" + headImg.getOriginalFilename()));
}
if (photos.length > 0){
//有长度说明有图片
for (MultipartFile photo : photos) {
if (!photo.isEmpty()){
//非空
photo.transferTo(new File("E:\\SpringBoot\\"+photo.getOriginalFilename()));
}
}
}
return "main";
}
@RequestPart注解配合图片使用,MultipartFile类型可以接受并提供封装好的方法处理图片
调用 transferTo 方法可以将图片存在指定位置,getOriginalFilename方法可以得到本地图片名
七、错误处理
1、默认规则
- 默认情况下,Spring Boot提供
/error
处理所有错误的映射 - 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
- 要对其进行自定义,添加
View
解析为error
- 要完全替换默认行为,可以实现
ErrorController
并注册该类型的Bean定义,或添加ErrorAttributes类型的组件
以使用现有机制但替换其内容。 - error/下的4xx,5xx页面会被自动解析;
八、Web原生组件注入(Servlet,Filter,Listener)
1、注解注入方式
①Servlet
必须使用@WebServlet注解和@ServletCompentScan注解配合
package com.young.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("6666");
}
}
package com.young;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan(basePackages = "com.young.servlet")
@SpringBootApplication
public class BootWeb05Application {
public static void main(String[] args) {
SpringApplication.run(BootWeb05Application.class, args);
}
}
@WebServlet(urlPatterns = "/my"):注册servlet类并且指定链接(无视拦截器)
@ServletComponentScan(basePackages = "com.young.servlet"):标在主类上进行包扫描
②Filter
package com.young.servlet;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
@WebFilter(urlPatterns = "")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
@Override
public void destroy() {
}
}
@WebFilter(urlPatterns = " "):指定过滤资源路径
③Listener
同样使用@WebListener注解
2、使用RegistrationBean
ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my02");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
return new ServletListenerRegistrationBean(mySwervletContextListener);
}
}