Spring基础(Data Access数据库)——Spring+SpringMVC & 集成mybatis & 拦截器

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。Spring基础(Data Access数据库)——Spring+SpringMVC & 集成mybatis & 拦截器,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

目录

引出


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

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!