目录
引出
1.如何把mybatis也集成进spring中,有哪些步骤;
2.前端发送json对象,后端怎么接收;
3.前端传路径变量的rest风格,后端怎么接收处理;
4.spring的拦截的配置和使用;
入门案例:登陆和注册 & 用户信息分页 之 固定的步骤:
(1)建普通项目+配置pom.xml文件
<!-- 继承一个父-->
<parent>
<groupId>org.springframework.boot</groupId>
<version>2.3.0.RELEASE</version>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 做web项目的包-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 前端模板引擎,功能类似于jsp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 分页工具,匹配springBoot的jar包-->
<!-- 后面加spring-boot-starter,表示按照spring重新改写的版本-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<!-- mybatis的包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 工具包-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
</dependencies>
(2)写主启动类 + application.yml文件
主启动类
package com.tianju;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* springMVC的主启动类
* 1.是主启动类;@SpringBootApplication
* 2.启动:SpringApplication.run(Main.class);
*/
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}
application.yml文件,注意如果有mybatis包,则必须配置一下,不然会报错
server:
port: 80
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123
【bug】pom.xml文件导了mybatis的包,但是application.yml文件没有配置
项目启动失败,报错信息如下:
报错信息:
Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
(3)static静态资源 + templates前端页面
(4)先用form表单尝试一下【有坑】关于日期传输和接收
报错信息:
Failed to convert from type [java.lang.String] to type [java.util.Date] for value ‘2023-06-08’
解决方案:controller层接收参数上加注解
@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
注册
<form action="/user/register" method="post">
用户名:<input type="text" name="username">
密码:<input type="text" name="username">
性别:
<select name="sex">
<option value="male">男</option>
<option value="female">女</option>
</select>
生日:
<input type="date" name="birthday">
<input type="submit" value="提交">
</form>
</body>
</html>
实体类
package com.tianju.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.text.Format;
import java.util.Date;
/**
* user实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String username;
private String password;
private String sex;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
}
controller层代码
package com.tianju.controller;
import com.tianju.entity.ResData;
import com.tianju.entity.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Date;
/**
* 用于处理用户相关请求的controller
* 1.在容器中;@Controller
* 2.访问路径;一级目录,@RequestMapping("/user")
*/
@Controller
@RequestMapping("/user")
public class UserController {
// 首先到登陆页面,响应一个页面
@RequestMapping("/registerPage")
public String registerPage(){
// 返回string类型
return "/user/register";
}
// 在登陆页面,用户点击登陆按钮,处理请求
@RequestMapping("register")
@ResponseBody
public ResData register(String username, String password, String sex,
@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday){
System.out.println(birthday);
User user = new User(username,password,sex,birthday);
System.out.println(user);
return new ResData(200, "ok", null);
}
}
(5)用vue发送axios请求【主流】@RequestBody接收
要点:
- 前端传参用Json对象传;
- 后端接收需要加上@RequestBody注解;
- 实体类中规定日期的格式, @JsonFormat(pattern = “yyyy-MM-dd”)
前端发送对象的方式:
(1) user对象逐个赋值,发送post请求
let user = {}
user.username = this.username;
user.password = this.password;
user.sex = this.sex;
user.birthday = this.birthday;
axios.post("/user/register",user)
(2) 直接创建好user对象,发送post请求
let user = {
"username":this.username,
"password":this.password,
"sex":this.sex,
"birthday":this.birthday,
}
axios.post("/user/register",user)
后端接收要加上@RequestBody,在类上加 @JsonFormat(pattern = “yyyy-MM-dd”)
// 在登陆页面,用户点击登陆按钮,处理请求
@RequestMapping("register")
@ResponseBody
// 如果前端用json对象发,后端需要加上@RequestBody
public ResData register(@RequestBody User user){
System.out.println(user);
return new ResData(200, "ok", null);
}
集成mybatis:
之前的模式下:
在spring中集成mybatis
1. 配置文件application.xml文件,加入mybatis相关
server:
port: 80
# 1.连接数据库——对应之前 xml文件的数据库
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/javaweb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: 123
# mybatis其他配置
mybatis:
# 2.给实体类起别名,首字母小写
type-aliases-package: com.tianju.entity
configuration:
# 3.开启驼峰命名
map-underscore-to-camel-case: true
# 4.让日志生效
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 5.扫描sql的位置
mapper-locations: classpath:/mapper/*Mapper.xml
2. 注册功能的实现
(1)核心sql语句,获取新增数据的id:useGeneratedKeys=“true” keyProperty=“id”
<!-- TODO:如何知道新增的人的id-->
<insert id="add" useGeneratedKeys="true" keyProperty="id">
INSERT INTO com_user(username,password,sex,birthday)
VALUES (#{username},#{password},#{sex},#{birthday})
</insert>
前端导包:
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js"></script>
<script src="/bootstrap/js/bootstrap.js"></script>
<script src="/js/vue.min-v2.5.16.js"></script>
<script src="/js/axios.min.js"></script>
(2)controll层代码
要点:
- 前端用json对象发,后端需要加上@RequestBody User user;
- 响应页面,不带数据,返回值为string
@Autowired
private IUserService userService;
// 首先到注册页面,响应一个页面
@RequestMapping("/registerPage")
public String registerPage(){
// 返回string类型
return "/user/register";
}
// 在注册页面,用户点击注册按钮,处理请求
@RequestMapping("register")
@ResponseBody
// 如果前端用json对象发,后端需要加上@RequestBody
public ResData register(@RequestBody User user){
System.out.println(user);
System.out.println(EntityUtils.isAllNotNull(user));
// 1.判断前端输入不为null或空,除了自增的id
boolean allNotNull = EntityUtils.isAllNotNull(user);
if (!allNotNull){
System.out.println("输入有空");
return new ResData(1001,"输入为空",null);
}
// 1.判断两个输入的密码是否一致
if (!user.getPassword().equals(user.getRePassword())){
return new ResData(1001,"两次密码不一致",null);
}
// 2.判断是否有重名的
User userDB = userService.queryByUsername(user.getUsername());
if (userDB!=null){
System.out.println(userDB);
return new ResData(1002,"用户名重复",null);
}
// 3.插入数据库中
// 密码加密存储
user.setPassword(SecureUtil.md5(user.getRePassword()));
Integer addFlag = userService.add(user);
System.out.println(user);
if (addFlag<1){
return new ResData(3001,"系统繁忙,请稍后",null);
}
return new ResData(200, "ok", null);
}
(3)工具类1:判断一个对象除了自增id外,其他不为null,如果字符串,且不为空
EntityUtils.java文件
package com.tianju.util;
import java.lang.reflect.Field;
/**
* 传入一个对象,判断对象里面的每个属性是否为null,或空;
* 如果有null或空,返回false;否则,返回true;
*/
public class EntityUtils {
public static boolean isAllNotNull(Object obj) {
Class<?> aClass = obj.getClass();
// 获取所有的files
Field[] fields = aClass.getDeclaredFields();
for(Field field:fields){
// 如果第一个是id,id是数据库自增的,前端不输入,就跳过
if (field.getName().contains("id")){
continue;
}
field.setAccessible(true);
Object value = null;
try {
value = field.get(obj);
}
catch (Exception e) {
throw new RuntimeException(e);
}
if (value==null || "".equals(value)){
return false;
}
}
return true;
}
}
(3)工具类2:判断多个输入是否有空,支持Integer,String,Date类型
StringUtils.java文件
package com.tianju.util;
import java.util.Date;
/**
* 判读输入是否为空,支持可变长度参数
*/
public class StringUtils {
/**
* 最初始版本,只能判断string类型是否为null,空字符串
*/
public static Boolean isBlank(String str){
if(str==null || str.trim().equals("")){
return true;
}
return false;
}
/**
* 升级版本,
* 可以判断String,Integer,Date类型
* 是不是null,string判断是不是空
* @param objs 可变长度参数
* @return
*/
public static Boolean isBlank(Object... objs){
for (Object obj:objs){
// System.out.println(obj);
if (obj==null){
return true;
}
// 如果是字符串,判断是null,和 空字符串
if (String.class.equals(obj.getClass())){
String str = (String) obj;
if(str==null || str.trim().equals("")){
return true;
}
// Integer 和 Date类型判断是不是null
}else {
if (obj==null){
return true;
}
}
}
return false;
}
public static void main(String[] args) {
Object s = null;
String str = (String) s;
// System.out.println(str==null);
Integer i = null;
Boolean blank = isBlank(str,i,2);
System.out.println(blank);
System.out.println(isBlank("er",3,new Date()));
System.out.println("UserId".contains("id"));
}
}
(4)前端页面:给后端发送json对象{“username”:this.username,“password”:this.password}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js"></script>
<script src="/bootstrap/js/bootstrap.js"></script>
<script src="/js/vue.min-v2.5.16.js"></script>
<script src="/js/axios.min.js"></script>
</head>
<body>
<div id="app">
Vue登陆页面<br>
用户名:<input type="text" v-model="username"><br>
输入密码:<input type="text" v-model="password"><br>
<button @click="loginBtn">提交</button>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
username:"",
password:"",
},
methods:{
loginBtn(){
axios.post("/user/login",{"username":this.username,"password":this.password})
.then(response=>{
let resp = response.data;
console.log(resp)
if (resp.code==200){
alert(resp.msg)
// 跳转到index页面,也要过controller
location.href = "/user/listPage"
}else {
alert(resp.msg)
}
})
}
},
created(){
}
})
</script>
</body>
</html>
3.登陆功能的实现
(1)controller层代码
// 到登陆页面
@RequestMapping("/loginPage")
public String loginPage(){
// 返回string类型
return "/user/login";
}
// 处理用户输入的用户名和密码
@RequestMapping("/login")
@ResponseBody
public ResData login(@RequestBody User user, HttpSession session){
System.out.println(user);
// 1.输入不为空
if (StringUtils.isBlank(user.getUsername(),user.getPassword())){
return new ResData(1001, "用户名|密码为空", null);
}
// 2.判断用户名,密码是否正确
User userDB = userService.queryByUsername(user.getUsername());
if (userDB==null || userDB.getPassword().equals(SecureUtil.md5(user.getPassword()))){
return new ResData(1001, "用户名|密码错误", null);
}
// 3.登陆成功,保存到session
session.setAttribute("user", userDB);
return new ResData(200, "OK", null);
}
(2)前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js"></script>
<script src="/bootstrap/js/bootstrap.js"></script>
<script src="/js/vue.min-v2.5.16.js"></script>
<script src="/js/axios.min.js"></script>
</head>
<body>
<div id="app">
Vue登陆页面<br>
用户名:<input type="text" v-model="username"><br>
输入密码:<input type="text" v-model="password"><br>
<button @click="loginBtn">提交</button>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
username:"",
password:"",
},
methods:{
loginBtn(){
axios.post("/user/login",{"username":this.username,"password":this.password})
.then(response=>{
let resp = response.data;
console.log(resp)
if (resp.code==200){
alert(resp.msg)
// 跳转到index页面,也要过controller
location.href = "/user/listPage"
}else {
alert(resp.msg)
}
})
}
},
created(){
}
})
</script>
</body>
</html>
4.用户信息分页展示 路径变量传参”/list/{pageNum}/{pageSize}”,@PathVariable(“pageNum”)
(1)controller层代码
要点:
- 共享值用ModelAndView,前端获取 [[${username}]]
- 路径变量传参”/list/{pageNum}/{pageSize}”,@PathVariable(“pageNum”);
// 到list页面
// 1.直接到页面
// @RequestMapping("/listPage")
public String listPage(){
// 返回string类型
return "/user/list";
}
// 2.共享值
@RequestMapping("/listPage")
public ModelAndView listPageMV(){
ModelAndView mv = new ModelAndView("/user/list"); // 到哪个页面
mv.addObject("username", "peter"); // 共享的值
return mv;
}
// 处理list页面的请求,pageNum,pageSize
@RequestMapping("/list/{pageNum}/{pageSize}")
@ResponseBody
public ResData userList(@PathVariable("pageNum") Integer pageNum,
// TODO:可以多个注解吗?答案:用路径变量必须都传,所以下面无效
// @RequestParam(value = "pageSize",defaultValue = "3")
@PathVariable("pageSize") Integer pageSize){
PageInfo<User> pageInfo = userService.queryList(pageNum, pageSize);
System.out.println(pageInfo);
return new ResData(200, "OK", pageInfo);
}
(2)前端页面:[[${username}]]
<!DOCTYPE html>
<html lang="en">
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>列表</title>
<link rel="stylesheet" href="/bootstrap/css/bootstrap.css">
<script src="/js/jquery-3.5.1.js"></script>
<script src="/bootstrap/js/bootstrap.js"></script>
<script src="/js/vue.min-v2.5.16.js"></script>
<script src="/js/axios.min.js"></script>
</head>
<body>
<div id="app">
列表页面[[${username}]]<br>
<div>
<!-- 进行搜索-->
<input type="text" v-model="pageSize">
<button @click="searchBtn">提交</button>
</div>
<!-- pageNum,pageSize,name,date,-->
<table class="table-condensed table-hover table-striped table-responsive table-cell table-row-cell table-view table-bordered">
<tr>
<th>id</th>
<th>username</th>
<th>gender</th>
<th>birthday</th>
</tr>
<tr v-for="user in pageInfo.list">
<td>{{user.id}}</td>
<td>{{user.username}}</td>
<td>{{user.sex}}</td>
<td>{{user.birthday}}</td>
</tr>
</table>
<div>
<!-- 首页,尾页,上下页,跳转到-->
<button v-show="!pageInfo.isFirstPage" @click="toFirstPage">首页</button>
<button v-show="pageInfo.hasNextPage" @click="toNextPage">下页</button>
<button v-show="pageInfo.hasPreviousPage" @click="toPreviousPage">上页</button>
<button v-show="!pageInfo.isLastPage" @click="toLastPage">尾页</button>
</div>
</div>
<script>
let app = new Vue({
el:"#app",
data:{
pageInfo:{},
pageSize:3,
},
methods:{
// 设置pageNum 和 pageSize
queryList(pageNum,pageSize){
axios.post("/user/list/"+pageNum+"/"+pageSize)
.then(response=>{
let resp = response.data;
console.log(resp)
this.pageInfo = resp.data;
})
},
searchBtn(){
this.queryList(1,this.pageSize)
},
// 首页,尾页,下页,上页
toFirstPage(){
this.queryList(1,this.pageSize)
},
toNextPage(){
this.queryList(this.pageInfo.pageNum+1,this.pageSize)
},
toPreviousPage(){
this.queryList(this.pageInfo.pageNum-1,this.pageSize)
},
toLastPage(){
this.queryList(this.pageInfo.pages,this.pageSize)
},
},
created(){
this.queryList(1,3);
}
})
</script>
</body>
</html>
拦截器
拦截器是基于增强方法做的
拦截谁,在配置类中配置;——对应SpringMvcConfig.java文件
拦下来做什么;——interceptor/LoginAuthInterceptor.java文件
1.拦截谁addInterceptors,配置类(@Configuration + implements WebMvcConfigurer )中
要点:
- 在soring的容器中,@Configuration
- 是spring的配置类 implements WebMvcConfigurer
package com.tianju.config;
import com.tianju.inteceptor.LoginAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* spring的配置类
* 1.在容器,@Configuration
* 2.是spring的配置类,
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
LoginAuthInterceptor loginAuthInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginAuthInterceptor)
.addPathPatterns("/**") // 拦截器 /** 表示子孙目录
.excludePathPatterns(
"/user/loginPage","/user/login",
"/user/registerPage","/user/register",
"/js/**","/css/**","/bootstrap/**"
); // 在拦截的基础上,放行谁
}
}
2.拦下来做什么—response.sendRedirect(“/user/loginPage”);,拦截器(@Component + implements HandlerInterceptor)
要点:
- 在容器中,@Component
- 是拦截器,implements HandlerInterceptor
- 拦下来做什么,登陆了,放行,返回true;没有登陆,重定向去登陆页面,返回false;
package com.tianju.inteceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 拦截器相关,拦下来做什么
* 1.在容器中,@Component
* 2.是拦截器,implements HandlerInterceptor
*/
@Component
public class LoginAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 如果登陆了,就放行
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user!=null){
return true;
}else {
// 没有登陆,去登陆页面
response.sendRedirect("/user/loginPage");
return false;
}
}
}
【bug】302重定向,ERR_TOO_MANY_REDIRECTS,如果配置类中的,excludePathPatterns忘记加第一个反斜杠 /
报错:ERR_TOO_MANY_REDIRECTS
原因:.excludePathPatterns里面的路径反斜杠没加
总结
1.集成mybatis,在application.yml配置文件中进行配置;
2.前端传参用Json对象传,后端接收需要加上@RequestBody注解,实体类中规定日期的格式, @JsonFormat(pattern = “yyyy-MM-dd”);
3.获取新增数据的id:useGeneratedKeys=“true” keyProperty=“id”;
4.路径变量传参”/list/{pageNum}/{pageSize}“,后端接收用@PathVariable(“pageNum”);
5.后端共享值用ModelAndView,前端获取用[[${username}]];
6.spring配置类:在soring的容器中,@Configuration;是spring的配置类 implements WebMvcConfigurer;
7.拦截器(@Component + implements HandlerInterceptor),拦下来做什么—response.sendRedirect(”/user/loginPage”);
8.拦截器在Spring配置中使用addInterceptors方法;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/165127.html