SpringBoot+MyBatis+Thymeleaf注册登录
好吧我承认,我用Php Web的时候也是做的注册登录,现在写javaweb还是注册登录,如果是仅仅做一个简单的注册登录不考虑安全性等问题,那其实拿来做练手是很不错的。
下面要完成的是这几项。
-
准备
-
View
-
get/post
-
注册登录分析
-
注册登录实现
-
总结
1.准备
用JavaWeb来写的话比Php肯定是复杂的多,特别是对于我第一次接触JavaWeb的小白,所以我们这篇文章不得不长篇大论的写,如果你喜欢研究的话,不如耐心的看下来,如果您是大神的话,还请您能够指出其中的错误。
准备就是我们打算接着使用先前建立好的工程,我们新建几个控制器来做注册登录。
那么在写代码之前我们要学会一些基本的知识。
2.View
MVC想必大家应该很熟悉,特别是我们用的SpringBoot框架,更应该知道它是基于MVC架构的。
上面我们已经写过C,这次我们来研究一下View。
先贴一段Controller的代码
package com.example.demo.Web;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
@RequestMapping("/My")
public class MyController {
@RequestMapping(value = "/index")
public ModelAndView hello(Model model) {
model.addAttribute("name", "苏凌");
return new ModelAndView("hello");
}
@RequestMapping("/")
public String normal()
{
return "hello";
}
}
这里就是写好了两个接口,访问http://localhost:8080/My/的时候是打印hello,访问http://localhost:8080/My/index时候截图如下
那么这个界面从哪来的?
你可能已经知道了
new ModelAndView("hello")
这行代码应该就是打印的内容吧。
对,没错,通过ModelAndView方法,我们可以找到hello代表的网页,然后把网页里面的信息展示出来,同时我们可以通过model传递数据,那么我们此时就做到了模型视图分离。
那么我们用到的就是模板技术(Jsp开发应该不属于模板系列)。
Tip: 目前Spring官方已经不推荐使用JSP来开发WEB了,而是推荐使用如下几种模板引擎来开发:
- Thymeleaf(Spring官方推荐)
- FreeMarker
- Velocity
- Groovy
- Mustache
顾名思义,采用模板我们可以更高效,准确的实现页面,也做到了对于后期维护的前提。我们可以选择自己喜欢的模板,大同小异
Thymeleaf
那么这篇文章里,我们使用官方推荐模板,关于这款模板的好处大家可以自行搜索,在这里不占用篇幅。
第一步:下载
通过修改pom.xml添加Thymeleaf的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Note:注意加在<dependencies></dependencies>标签里
下载需要挺长时间,如果等不及或者多次不成功可以试试修改mvn源。
我们下载完最好检验一下有没有成功(我踩了一天的坑,就是因为没有下载成功,pom写的没毛病,就是lib下载不完整……坑的一批),打开mvn project,看看有没有Thymeleaf。
有的话继续,没有的话慢慢下载……暂时没啥好方法。
第二步,小小配置
在application.properties中添加一行,小小的配置关闭缓存。
#关闭缓存
spring.thymeleaf.cache=false
第三步,编写模板文件
Note:文件需要放在src/main/resouces/templates/目录下,文件后缀.html
上面的例子中我写的模板文件是hello.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<!--${name}可以打印一个叫name的变量-->
<p th:text="'你好!' + ${name} + '!'" ></p>
</body>
</html>
模板语法不在这里多介绍,暂时先Copy代码,我们这篇文章理解一下流程。
第四步,编写Controller
那么Controller就是我们上面贴的那个,我们只看所需要的接口
package com.example.demo.Web;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
@RequestMapping("/My")
public class MyController {
@RequestMapping(value = "/index")
public ModelAndView hello(Model model) {
model.addAttribute("name", "苏凌");
return new ModelAndView("hello");
}
}
我们使用model传入了name变量的值,然后用ModelAndView显示了页面。
这里还有一种操作,就是下面这种直接返回html名字的写法,但是要求必须使用@Controller注解。
Thymeleaf先说到这里,在后面我们可能会尝试一下Jsp原始写法。
3.get/post
1.准备
那么研究Web,必须先来研究get/post请求。
【文章参考:http://blog.csdn.net/u010399316/article/details/52913299】
我们最常用的两种方式。
比如说我设计这样一个链接。
http://localhost:8080/register?name=surine&pwd=123 通过get请求,传递用户名和密码来完成注册,
然后设计这样一个链接。
http://localhost:8080/login 通过post请求,传递用户名和密码来完成登录。
2.Get
在SpringBoot中,我们的Get这样实现,通过method来设置方法
/**
* 通过get请求去注册
* @param name
* @param pwd
* @return 状态信息
*/
//method :设置一下get或者post
//@RequestParam的注解已经很清楚了,接受名字为name的参数,required 设置是否必须。
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String register(@RequestParam(value = "name", required = true) String name,
@RequestParam(value = "pwd", required = true) String pwd) {
//register_check是自定义的一个注册方法我们可以在里面写注册操作,返回状态码
return register_check(name, pwd);
}
//先写一个简单的register_check方法
private String register_check(String name, String pwd) {
if(name.equals("surine")&&pwd.equals("123456")){
return "注册成功";
}else{
return "注册失败";
}
}
我们浏览器测试一下
首先我们不给参数看啥情况,下面报错很明显,缺少参数,这是因为我们设置了必须要参数的原因
账号密码错误
账号密码正确
细心的小朋友站出来:哪家的注册还判断你的账号密码……
我:测试嘛!这么认真干嘛!
3.Post
我们先写一个Post的Controller,额,竟然跟Get一样,除了改了下method
/**
* 通过get请求去登陆
*
* @param name
* @param pwd
* @return
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@RequestParam(value = "name", required = true) String name,
@RequestParam(value = "pwd", required = true) String pwd) {
return login_check(name, pwd);
}
private String login_check(String name, String pwd) {
if(name.equals("surine")&&pwd.equals("123456")){
return "登录成功";
}else{
return "登录失败";
}
}
那么我们跑起来用Postman来测试一下。
输入地址,表单,最后得到结果。
看样子没有什么异常,暂时先掌握这些,为我们后面的开发做铺垫。
其余的方法感兴趣可以自行研究。
4.注册登录分析
(Note:注意,我们在下面写的代码可能与上面有所不同,具体为什么这么写我会在下面做出解释,但是上面的练习也应该跑一遍,参考:https://www.jianshu.com/p/212f2682c10b)
这里面提到的一些生词,在一会都会详细解释,现在就记住我们要写的项目有这么些东西就行了。
1.原型分析
预期要做成下面这个样子,
打开首页有登录注册两个链接,点击登录链接,跳转到登录,登录成功失败会有相应提示,同理注册也是。
2.流程分析
最终的文件结构如下图所示。
这里我们有5个重要部分
Controller上面我们讲了,作为接受页面数据的工具,Dao是操作数据库的工具,Domin是放的实体类,Service主要是操作数据检查合法性,通过Service来发出指令,templates也就是我们的页面模板了。
注册(登录与其相同):
- 前端页面(templates)把用户的注册信息传递给Controller
- Conteoller把注册信息交给Service去处理
- Service里面和Dao层一起处理业务逻辑(结合数据库判断数据合法性)
- Service把处理结果返回给Controller
- Controller在把结果传递给前端页面,这是用户就能看见注册结果了
C->V->M->V->C 的流程不多说了。
3.设计原则
可能你会觉得我一个模板,一个Controller,然后用Mapper(上篇文章讲过)就可以做一个注册登录,搞得这么复杂干嘛……
这样写体现了MVC模型的特点,通过对业务的一些分层,减少层次之间耦合,使每一个类的功能单一化,方便以后维护,但对于新手来说,一下子接受不了,
4.数据库设计
数据库不多说了,自己建立就行了,不会搞数据库的童鞋,还是先去搞搞数据库吧。
5.注册登录实现
下面我们开始Coding,在这期间遇到了不少问题,但是又没做什么解决自己就好了……真的是要人命,然后我会在遇到问题或者容易出错的地方打Tips,童靴们自己注意下。
1.配置项
首先我们做一下配置,把这之前的一起说了。
Thymeleaf模板:
#修改pom.xml文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
#修改application.properties文件
#为了保证项目的运行,增加这些
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.servlet.content-type=text/html
#关闭缓存
spring.thymeleaf.cache=false
Application:作为SpringBoot的入口文件也有需要配置的地方
添加mapper扫描的包路径,为数据库连接做准备
@SpringBootApplication
//Mapper扫描(这里的值就是dao目录的值,按照我刚贴的目录结构来做就行)
@MapperScan("com.example.demo.dao")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2.实体类
刚才说过,domin目录下面放实体类,我们第一步先把实体类做出来,为整体的流程做协调,根据我们的数据库设计字段,来设计实体类。
Tip: 没有什么特殊需求,一定要保持数据库字段跟domin实体类字段一致,甚至是数据类型,省的出现各种各样的问题。
我的User.class
package com.example.demo.domin;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3.数据库操作
前面也说过,Dao目录下面放的各种dao文件是我们的数据库操作接口(抽象数据类),例如我们的UserDao内容如下,两个操作,增加用户,查询用户。
所以在以后的项目中,如果需求大的话,我们可以编写更多功能。
package com.example.demo.dao;
//这个注解代表这是一个mybatis的操作数据库的类
@Repository
public interface UserDao {
// 根据username获得一个User类
@Select("select * from user where username=#{name}")
User getOneUser(String name);
//插入一个User
@Insert("insert into user (username,password) values(#{username},#{password})")
boolean setOneUser(User user);
}
Tip:注意字段对应 比如#{name}最后会被String name 的name所替换
4.API接口(Controller)
这里是重点,前端后端从这里划分(不知道这个说法正不正确,个人见解),我们根据需求分析,发现需要实现的功能主要有三个,注册登录注销,那么我们的Controller如下。(UserService 先让他标红,我们将在下面建立)
package com.example.demo.controller;
@Controller
@EnableAutoConfiguration
public class IndexController {
//自动注入userService,用来处理业务
@Autowired
private UserService userService;
/**
* 域名访问重定向
* 作用:输入域名后重定向到index(首页)
* */
@RequestMapping("")
public String index(HttpServletResponse response) {
//重定向到 /index
return response.encodeRedirectURL("/index");
}
/**
* 首页API
* 作用:显示首页
* */
@RequestMapping("/index")
public String home(Model model) {
//对应到templates文件夹下面的index
return "index";
}
/**
* 注册API
* @method:post
* @param user(从View层传回来的user对象)
* @return 重定向
* */
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String registerPost(Model model,
//这里和模板中的th:object="${user}"对应起来
@ModelAttribute(value = "user") User user,
HttpServletResponse response) {
//我们可以用Sout这种最原始打印方式来检查数据的传输
System.out.println("Controller信息:"+user.getUsername());
System.out.println("Controller密码:"+user.getPassword());
//使用userService处理业务
String result = userService.register(user);
//将结果放入model中,在模板中可以取到model中的值
//这里就是交互的一个重要地方,我们可以在模板中通过这些属性值访问到数据
model.addAttribute("result", result);
//开始重定向,携带数据过去了。
return response.encodeRedirectURL("/index");
}
/**
* 登录API
* @method:post
* @param user(从View层传回来的user对象)
* @return 重定向
* */
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginPost(Model model,
@ModelAttribute(value = "user") User user,
HttpServletResponse response,
HttpSession session) {
String result = userService.login(user);
if (result.equals("登陆成功")) {
//session是作为用户登录信息保存的存在
session.setAttribute("user",user);
}
model.addAttribute("result", result);
return response.encodeRedirectURL("/index");
}
/**
* 注销API
* @method:get
* @return 首页
* */
@RequestMapping(value = "/loginOut", method = RequestMethod.GET)
public String loginOut(HttpSession session) {
//从session中删除user属性,用户退出登录
session.removeAttribute("user");
return "index";
}
}
相信就算是小白,我们在前面的学习中也接触到接口的写法了,这里仅仅增加了几个重定向的方法,还有post的写法有一点点不太一样(运用了模板之后)
Tip:不会使用单元测试或调试的话可以用System.out.print这种最原始的方法来检查数据哦
5.页面模板
通过需求分析我们一共要设计三个页面,源码分别如下。
<!--index.html-->
<!--模板源码应该很好理解,不理解的话去看看教程就OK-->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<!--这里result会报错,不要担心这是idea的bug,不影响你的项目->
<h1> <span th:text="${result}"></span></h1>
<h1>欢迎 : <span th:text="${session.user}?${session.user.getUsername()}:'(您未登录)'" ></span> </h1>
<a th:href="@{/register}">点击注册</a>
<a th:href="@{/login}" th:if="${session.user==null}">点击登录</a>
<a th:href="@{/loginOut}" th:unless="${session.user==null}">退出登陆</a>
</body>
</html>
<!--login.html-->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登陆</title>
</head>
<body>
<h1>欢迎登陆</h1>
<!--这个地方user也会报错,不用担心-->
<!--注意下面name,和id都写上就OK-->
<form th:action="@{/login}" th:object="${user}" method="post">
<label for="username">username:</label>
<input type="text" name="username" id="username" />
<label for="password">password:</label>
<input type="text" name="password" id="password" />
<input type="submit" value="submit">
</form>
</body>
</html>
<!--register.html-->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>欢迎注册</title>
</head>
<body>
<h1>欢迎注册</h1>
<form th:action="@{/register}" th:object="${user}" method="post">
<label for="username">username:</label>
<input type="text" id="username" name="username"/>
<label for="password">password:</label>
<input type="text" id="password" name="password"/>
<input type="submit" value="submit">
</form>
</body>
</html>
TIp:页面的重点放在input的一些属性上,我们输入的值最终会被映射到user(domin)里面
6.Service
写了好长时间,算是把页面,数据库,接口都写了,那么剩下最后一个纽带就是service了。
package com.example.demo.service;
import com.example.demo.dao.UserDao;
import com.example.demo.domin.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//自动注入一个userDao
@Autowired
private UserDao userDao;
//用户注册逻辑
public String register(User user) {
System.out.println(user.getUsername());
User x = userDao.getOneUser(user.getUsername());
//判断用户是否存在
if (x == null) {
userDao.setOneUser(user);
return "注册成功";
}
else {
return x.getUsername()+"已被使用";
}
}
//用户登陆逻辑
public String login(User user) {
//通过用户名获取用户
User dbUser = userDao.getOneUser(user.getUsername());
//若获取失败
if (dbUser == null) {
return "该用户不存在";
}
//获取成功后,将获取用户的密码和传入密码对比
else if (!dbUser.getPassword().equals(user.getPassword())){
return "密码错误";
}
else {
//若密码也相同则登陆成功
//让传入用户的属性和数据库保持一致
user.setId(dbUser.getId());
return "登陆成功";
}
}
}
仔细阅读service的源码,你就会发现,它其实就是与dao联系起来做数据合法性检验的。
Tip:这里也是可以使用sout来测试数据
7.运行
到这里我们的所有代码都已经写完了,如果你的工程还缺了什么跑不起来,可以在下面评论哦。
运行的时候浏览器输入localhost:8080就可以到首页,经我这边测试注册登录注销都没问题,而且登录了之后下次再访问就直接是登录状态了(session没有设置过期时间,所以直到下一个用户登录一直有效)
Tip:善于观察浏览器报错信息或者Idea里也有,通过检查报错来解决问题,不要遇到问题就去对照别人的源码看哪里不一样,同样的代码可能不适合你。
6.总结
感觉还是Php写起来简单一些,毕竟脚本语言本身就是轻巧。
那么JavaWeb用了SpringBoot框架后,也从一定程度上减少了代码的复杂度,逻辑还算清楚。
唯一不爽的是数据库竟然要自己写sql语句……
不过自由定制毕竟是可以满足大量需求的,那么如果你真的不喜欢sql语句,可以试试用 Hibernate来替换MyBatis。
我们目前实现的是一个注册登录功能,那么其实你再做别的接口也是一样的,只是在它的业务上有一些不同,那么希望这篇文章能对你有所帮助。
个人刚接触JavaWeb,有好多东西也是现查现学,因为为了实习才准备的JavaWeb,所以现在连本书都没有,好尴尬。
最后,代码虐我千百遍,我对代码如初恋。
续
这里特意加出一个部分,我们来讨论一下Jsp的写法和Css/js静态页面(仅讨论JSP与Css/js,不讨论thymeleaf)的解决方案。
SpringBoot与Jsp
前面我们说到Spring官方非常推荐开发者使用ThymeLeaf来开发,而不推荐使用Jsp。那我们还是要了解一下Jsp是怎么写的。
1.导入依赖
再pom.xml里面导入依赖
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
记得导完看看有没有哦,这里我先前一直不知道要导入这个,说实话我真的没找到一个像样的文章,也不知道是我理解能力太差了还是大家都写得很烂,自我感觉良好…… 我不知道我写的是不是很烂,但是我把我做的每一步都说出来了。如果还有问题你可以私信或留言。
参考:https://stackoverflow.com/questions/29782915/spring-boot-jsp-404
如果不导入就会出现这位小哥遇到的错误,也就是我一直遇到的错误
我是参考第一个红框和第三个红框来解决的,也就是上面的代码。
Tip:遇到问题不要百度,要么google要么直接StackOverFlow,百度出来的?要是中国程序员都这么糟糕的话……简直不敢想象。
2.配置application.properties
文件中添加这两行
# 页面默认前缀目录
spring.mvc.view.prefix=/WEB-INF/jsp/
# 响应页面默认后缀
spring.mvc.view.suffix=.jsp
3.配置Application
主要增加一个继承
4.新建文件夹,创建jsp
webapp/WEB-INF/jsp/文件
举个例子,indexjsp.jsp内容
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring Boot Sample</title>
</head>
<body>
Time: ${time}
<br>
Message: ${message}
</body>
</html>
5.Controller
@RequestMapping(value = "/jsp/index",method = RequestMethod.GET)
public String jsp_index(Map<String, Object> model){
model.put("time", new Date());
model.put("message", "你好");
return "indexjsp";
}
6.访问
访问之前有个小Tip
Tip:注意删掉Thymeleaf在工程中的依赖和配置,这两样就行。
要注意Thymeleaf和jsp是两种并列的技术,为了解决同一个问题,不要试图去融合使用他们,那样只会让你的项目更乱,所以我们使用的时候按照需求选一个就行,我不确定两个共存会不会出现致命错误,但是为了增加系统的稳定性,尽量不要两个一起使用。
地址:localhost:8080/jsp/index
Jsp与CSS
在Jsp中使用静态资源也没什么难处。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Spring Boot Sample</title>
<!--Import Google Icon Font-->
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!--Import materialize.css-->
<link type="text/css" rel="stylesheet" href="../../static/css/materialize.css" media="screen,projection"/>
</head>
<body>
<script type="text/javascript" src="../../static/js/jquery-3.2.1.js"></script>
<script type="text/javascript" src="../../static/js/materialize.js"></script>
Time: ${time}
<br>
Message: ${message}
<button class="btn waves-effect waves-light" type="submit" name="action">提交
<i class="material-icons right">send</i>
</button>
</body>
</html>
上面这个代码还是indexjsp.jsp,我们在其中引入了materialize和jquery(materialize需要),然后使用了materialize中的一个组件。
重点在我们建立的文件夹,把materialize放在static下面
效果:
不会用materialize的童鞋参考中文网http://www.materializecss.cn/buttons.html。哈哈不罗嗦了,好长一篇文章,最后给一个jsp的注册登录版本源码。