• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

springBoot学习——spring+springMVC 和amp; 集成mybatis 和amp; 拦截器

武飞扬头像
Perley620
帮助1

引出


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方法;

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgacgfh
系列文章
更多 icon
同类精品
更多 icon
继续加载