SpringBoot 项目Demo
1.创建项目
在idea中创建项目,勾选web模块和模板引擎Thymeleaf
2.项目层次
在com.gip下建包,因为是demop,所以server层就省去了
导入前端页面模板(这里采用bootstrap官网模板),自己写也可以(无非就是登陆界面加表单)
3.项目功能
index.html:登录界面
dashboar.html:表单界面,用于展示数据
404.html:错误处理页面
登录界面效果:(后续需要修改)
项目是展示员工数据的,所以要创建员工实体类,由于不同员工所属不同部门,所以还需要一个部门类,这里用到了一对多关系映射。登录系统的用户也可以创建一个实体类(这里就省去了)主要目的是展示下员工数据就行
4.创建对应的实体类
package com.gip.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;//0代表女 1代表男
private Date birth;
private Department department;
}
复制代码
package com.gip.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String DepartmentName;
}
复制代码
需要使用lombook插件
导入
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
复制代码
5.添加数据
这里在dao层伪造下数据
package com.gip.dao;
import com.gip.pojo.Department;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class DepartmentDao {
//使用HashMap<>来存伪造数据
private static Map<Integer,Department> departments = null;
static {
departments=new HashMap<Integer,Department>();
departments.put(1,new Department(1,"市场部"));
departments.put(2,new Department(2,"行政部"));
departments.put(3,new Department(3,"技术部"));
departments.put(4,new Department(4,"运营部"));
departments.put(5,new Department(5,"后勤部"));
}
//获取所有部门信息
public Collection<Department> getAllDepartments(){
return departments.values();
}
//查询部门信息,通过id查询
public Department findDepartmentById(int id){
return departments.get(id);
}
}
复制代码
package com.gip.dao;
import com.gip.pojo.Department;
import com.gip.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Repository
public class EmployeeDao {
@Autowired
private DepartmentDao departmentDao;
//使用Map来存数据
private static Map<Integer, Employee> employees=null;
static {
employees=new HashMap<Integer, Employee>();
employees.put(1001,new Employee(1001,"gip1","7882@qq.com",1,new Date(),new Department(1,"市场部")));
employees.put(1002,new Employee(1002,"gip2","7882@qq.com",0,new Date(),new Department(2,"行政部")));
employees.put(1003,new Employee(1003,"gip3","7882@qq.com",1,new Date(),new Department(3,"技术部")));
employees.put(1004,new Employee(1004,"gip4","7882@qq.com",0,new Date(),new Department(4,"运营部")));
employees.put(1005,new Employee(1005,"gip5","7882@qq.com",1,new Date(),new Department(5,"后勤部")));
};
//模拟主键自己增加
private static Integer initId = 1006;
//添加员工
public void save(Employee employee){
if(employee.getId()==null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.findDepartmentById(employee.getDepartment().getId()));
employees.put(employee.getId(),employee);
}
//查询全部员工
public Collection<Employee> getAllEmployee(){
return employees.values();
}
//通过id查找员工
public Employee findEmployeeById(int id){
return employees.get(id);
}
//通过id删除员工
public void deleteEmployeeById(int id){
employees.remove(id);
}
}
复制代码
6.测试方法
在test目录下建立com.gip.dao包目的是为了测试文件与测试的类的位置相对应
7.登录控制
首先启动springboot模板引擎的支持开启
添加starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
复制代码
application.properties中添加配置
#关闭模板引擎的缓存
spring.thymeleaf.cache=false
复制代码
在html中添加命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
复制代码
使用方法
www.thymeleaf.org/doc/tutoria…
写一个跳转方法
package com.gip.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/")
public String login(){
return "index";
}
}
复制代码
运行项目,浏览器输入http://localhost:8080/
项目一打开能直接进入index界面
登录界面
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" th:href="@{css/favicon.ico}">
<title>Signin Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link th:href="@{css/bootstrap.min.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link th:href="@{css/signin.css}" rel="stylesheet">
</head>
<body class="text-center">
<form class="form-signin" th:action="@{/login}">
<img class="mb-4" th:src="@{css/icon.jpg}" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="inputEmail" class="sr-only">User</label>
<input type="text" id="inputEmail" class="form-control" placeholder="User" required autofocus name="user">
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" placeholder="Password" required name="pwd">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
</form>
</body>
</html>
复制代码
8.数据展示
展示界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>show数据</title>
<style type="text/css">
table.gridtable {
font-family: verdana,arial,sans-serif;
font-size:11px;
color:#333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
margin: auto;
text-align: center;
}
table.gridtable th {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
}
table.gridtable td {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
}
</style>
<!-- Bootstrap core CSS -->
<link th:href="@{css/bootstrap.min.css}" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div>
<div class="text-primary" th:text="'当前登录用户是:'+${session.user}"></div>
<a class="btn btn-primary" th:href="@{/add}">添加员工</a>
<div style="text-align: center"><h3>展示员工数据</h3></div>
<table class="gridtable">
<tr>
<th>员工编号</th><th>员工姓名</th><th>性别</th><th>部门</th>
</tr>
<tr th:each="emp:${eData}">
<td th:text="${emp.getId()}">Text 1A</td><td th:text="${emp.getLastName()}">Text 1B</td><td th:text="${emp.getGender()==0?'女':'男'}">Text 1C</td><td th:text="${emp.getDepartment().getDepartmentName()}">Text 1C</td>
</tr>
</table>
</div>
</body>
</html>
复制代码
contoller层代码
package com.gip.controller;
import com.gip.dao.DepartmentDao;
import com.gip.dao.EmployeeDao;
import com.gip.pojo.Employee;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(@RequestParam("user") String user, @RequestParam("pwd") String pwd, Model model, HttpSession session){
System.out.println(user);
System.out.println(pwd);
session.setAttribute("user",user);
// model.addAttribute("user",user);
model.addAttribute("eData",new EmployeeDao().getAllEmployee());
return "show";
}
@RequestMapping("/add")
public String gotoAdd(Model model){
model.addAttribute("Departments",new DepartmentDao().getAllDepartments());
return "add";
}
@RequestMapping("/addEmployee")
public String addEmployee(Employee employee){
System.out.println(employee);
return "show";
}
}
复制代码
9.数据添加
添加用户
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>show数据</title>
<style>
.addFrom {
width: 50%;
margin: auto;
}
</style>
<!-- Bootstrap core CSS -->
<link th:href="@{css/bootstrap.min.css}" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div>
<a class="btn btn-primary" th:href="@{/add}">添加员工</a>
<a class="btn btn-primary">展示员工</a>
<div style="text-align: center"><h3>添加员工数据</h3></div>
<form class="addFrom" th:action="@{/addEmployee}" method="post">
<div class="form-group">
<label for="exampleFormControlInput1">User ID</label>
<input type="text" class="form-control" id="exampleFormControlInput1" placeholder="User ID" name="id">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">User Name</label>
<input type="text" class="form-control" id="exampleFormControlInput2" placeholder="User Name"
name="lastName">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Select Department</label>
<select class="form-control" id="exampleFormControlSelect1" style="height: 35px" name="department.id">
<option th:each="department:${Departments}" th:text="${department.getDepartmentName()}"
th:value="${department.getId()}">1
</option>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Select Gender</label>
<select class="form-control" id="exampleFormControlSelect2" style="height: 35px" name="gender">
<option value="0">女</option>
<option value="1">男</option>
</select>
</div>
<div class="form-group">
<input type="submit" class="btn-primary btn">
</div>
</form>
</div>
</body>
</html>
复制代码
顺带一提:idea格式化快捷键ctrl+alt+L
注意:
这里为什么值传递id,因为后台接收对象是employee
这个对象里面嵌套了一个部门对象,idea无法在一个表单中做对象映射(所以要传递部门对象的一个属性,如果要传递整个部门对象要再做一次封装,或在嵌套一个表单实现!)
传递对象打印如下
测试下:
10.国际化
springboot为我们提供了国际化的操作。
在resources目录下创建i18n文件夹,创建login资源包
i18n是国际化单词的缩写,首字母i,尾字母n,中间隔了18个字母
提示后面login_zh_CN会自动归类到资源包下,zh_CN代表:中文,中国;en_US代表英文:美国
快捷操作:
使用方法:
springboot配置中指定国际化所在的文件(springboot默认国际化文件是messages)
spring.messages.basename=i18n/login
复制代码
不指定效果如下:
可以发现使用th:语法加上#
修饰符,{}
来使用国际化元素
正确效果如下:
使用到的元素都会高亮哦
- 提示:如果只是页面修改,项目只用重新build一下就行,不需要重新启动
- 快捷键:Ctrl+F9
如何切换国际化语言呢?
一般情况是默认跟随浏览器语言的,或者是识别浏览器发出的请求头,一般请求头就带有语言的标识。springboot默认已经实现了该功能,如果想自行切换可以使用链接的方式发送请求,后台自行编写解析locale的类来处理请求。
建立一个在config包下MyLocaleResolver.java类要实现LocaleResolver接口
package com.gip.config;
import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String l = httpServletRequest.getParameter("l");
Locale locale=Locale.getDefault();
if(!StringUtils.isEmpty(l)){
String[] split = l.split("_");
locale=new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
复制代码
然后将其注册到配置类中(用了@Configuration的类)
这个localeResolver名字是固定的,生成的bean就叫localeResolver才可以生效(可能是这样springboot才能识别到,或者覆盖添加)
之后,我们在登录界面添加两行切换的a链接
<a class="btn btn-primary" th:href="@{/(l='zh_CN')}">中文</a>
<a class="btn btn-primary" th:href="@{/(l='en_US')}">英文</a>
复制代码
模板语法:@{}
里面写的就是链接的地址,如果想要带参数直接写(参数名=参数值)
想带多个参数这样写!!!(逗号隔开就行):th:href="https://juejin.cn/post/@{/(l='zh_CN',z='hh')}"
ok到页面测试
11.数据删除
controller中添加删除方法
@RequestMapping("/delete/{id}")
public String deleteEmployee(@PathVariable("id") int id){
employeeDao.deleteEmployeeById(id);
return "redirect:/show";
}
复制代码
注意使用resultful风格时候如果{参数名}
和方法接收的形参名
不一致的话
要使用@PathVariable
注解对应上去
在页面上新增a
链接可以请求到删除方法
<td><a class="btn btn-danger" th:href="@{'/delete/'+${emp.getId()}}">删除</a></td>
复制代码
注意拼接格式:th:href="https://juejin.cn/post/@{'/delete/'+${emp.getId()}}"
12.登录拦截器
首先在config包下新建LoginHandlerInterceptor类
package com.gip.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("user");
if(user==null){
request.setAttribute("msg","没有登录,请先登录!");
request.getRequestDispatcher("/").forward(request,response);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
复制代码
先取session
中用户信息,不为空代表已登录了,为空的话就转发到首页(登录界面),并且request携带参数msg作为提示信息供前台接收。
在自己配置类中,重写方法addInterceptors
添加拦截器,过滤的请求地址,以及排除过滤的地址
注意:要排除过滤静态资源请求
可以去配置文件看这个# spring 静态资源扫描路径 spring.resources.static-locations=classpath:/static/
之后就可以写成:
/static/**
新增一个地址映射
registry.addViewController("/main").setViewName("show");
复制代码
未登录状态,输入/main,发现拦截器生效,页面被转发到首页
13.修改数据
同理
controller中新增修改方法
@RequestMapping("goUpdate")
public String goUpdate(int id,Model model) {
Employee employee = employeeDao.findEmployeeById(id);
model.addAttribute("employee",employee);
model.addAttribute("Departments", new DepartmentDao().getAllDepartments());
return "update";
}
@RequestMapping("/updateEmployee")
public String updateEmployee(Employee employee) {
employeeDao.save(employee);
return "redirect:/show";
}
复制代码
跳转到修改界面,携带id
<td><a class="btn btn-danger" th:href="@{/goUpdate(id=${emp.getId()})}">修改</a></td>
复制代码
update界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>update数据</title>
<style>
.addFrom {
width: 50%;
margin: auto;
}
</style>
<!-- Bootstrap core CSS -->
<link th:href="@{css/bootstrap.min.css}" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div>
<a class="btn btn-primary" th:href="@{/add}">添加员工</a>
<a class="btn btn-primary">展示员工</a>
<div style="text-align: center"><h3>修改员工数据</h3></div>
<form class="addFrom" th:action="@{/updateEmployee}" method="post">
<div class="form-group">
<label for="exampleFormControlInput1">User ID</label>
<input type="hidden" readonly class="form-control" placeholder="User ID" name="id"
th:value="${employee.getId()}">
<input type="text" readonly class="form-control" id="exampleFormControlInput1" placeholder="User ID"
name="id" th:value="${employee.getId()}">
</div>
<div class="form-group">
<label for="exampleFormControlInput1">User Name</label>
<input type="text" class="form-control" id="exampleFormControlInput2" placeholder="User Name"
th:value="${employee.getLastName()}"
name="lastName">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Select Department</label>
<select class="form-control" id="exampleFormControlSelect1" style="height: 35px" name="department.id">
<option th:each="department:${Departments}" th:text="${department.getDepartmentName()}"
th:value="${department.getId()}"
th:selected="${employee.getDepartment().getId()==department.getId()}">1
</option>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Select Gender</label>
<select class="form-control" id="exampleFormControlSelect2" style="height: 35px" name="gender">
<option value="0" th:selected="${employee.getGender()==0}">女</option>
<option value="1" th:selected="${employee.getGender()==1}">男</option>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlInput1">Birth</label>
<input type="text" class="form-control" id="exampleFormControlInput3"
th:value="${#dates.format(employee.getBirth(),'yyyy-MM-dd')}"
name="birth">
</div>
<div class="form-group">
<input type="submit" class="btn-primary btn">
</div>
</form>
</div>
</body>
</html>
复制代码
显示id的控件input,如何让其不被修改?
使用disabled不可取:因为这样完全禁用,前台是不会传递这个控件的值到后台去
所以使用readonly
属性即可!(只读,不可选择修改)
或者添加一个隐藏域传递id
type=”hidden”
<input type="hidden" readonly class="form-control" placeholder="User ID" name="id"
th:value="${employee.getId()}">
<input type="text" readonly class="form-control" id="exampleFormControlInput1" placeholder="User ID"
name="id" th:value="${employee.getId()}">
复制代码
Listen,坑点来了
日期拿到前台要处理下格式
格式如下:
th:value="${#dates.format(employee.getBirth(),'yyyy-MM-dd')}"
复制代码
如何要修改的话
方法1:
记得添加配置
spring.mvc.format.date=yyyy-MM-dd
复制代码
因为springboot默认的格式是yyyy/MM/dd
/
分割的!
想改成-
分割要设置
方法2:
在实体类中
添加注解 @DateTimeFormat(pattern="yyyy-MM-dd")
和加个配置
#spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
#spring.jackson.time-zone=GMT+8
复制代码
都要写否则报错,这个配置是如果json传递的话,能按照格式解析!
可以用这个@JsonFormat
注解代替
如果前端传入的参数是 json 那我们就要加上@JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”), pattern = “yyyy-MM-dd HH:mm:ss”表示你要接收时间类型,timezone = “GMT+8”(因为时间相差8小时问题,所以要加上这个属性)
终于大功告成,测试一波!
修改前
修改后
顺带提一嘴:
这个false一定要写,否则写其他路径会解析出错,其他路径会导致报错!!!,搞死我(有些人觉得没必要,结果后面有坑)所以要写!
14.404处理
springboot提供了很好方式:
在templates下创建error下放html文件
html文件名就用响应码来命名:例如:404,500