这篇文章先是总结SpringBoot,当然我很多源码都没有仔细的去抠,而且这样一个成熟的框架想都不用想就知道源码很复杂,先学框架怎么用。接着就用SpringBoot完成一个较为简单的项目:员工管理系统。
目录
端口号被占用怎么办
在后面讲解了8080端口被占用后我们要怎样修改端口号,但是有时候我发现跑了一个项目以后,即便都关闭了,这个端口仍然被占用。修改端口号是一个方法,还有一个方法(可选):
1、以管理员身份打开cmd
2、输入netstat -ano
3、找到TCP中的本地地址尾号为8081的地址,记住对应的PID号
4、taskkill /pid PID号 -f
这样就可以杀死端口了
什么是SpringBoot
SpringBoot是一个开源框架,为了解决企业级应用开发的复杂性而创建的。
Spring是如何简化java开发的?
1、基于pojo的情况及和最小侵入性编程
2、通过IOC,依赖注入DI和面向接口实现松耦合
3、基于切面AOP和惯例进行声明式编程
4、通过切面和模板减少样式代码
而SpringBoot基于Spring开发,本身不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序,不能说是替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。
SpringBoot以约定大于配置为核心思想,帮助我们进行了很多的设置,同时集成了大量的第三方库配置(例如Redis、MongoDB等),几乎可以零配置的开箱即用。
SpringBoot的主要有点:
1、为所有Spring开发者更快入门
2、开箱即用,提供各种默认的配置来简化项目的配置
3、内嵌式容器简化web项目、
4、没有冗余代码生成和xml配置的要求
什么是微服务
一种架构风格,要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合;可以通过http的方式进行互通。
第一个SpringBoot程序
官方提供了一个快速生成的网站,idea继承了这个网站。
idea创建一个SpringBoot项目很简单,File-New Project-Spring Initializr即可进行创建,可以在创建过程中就选择web的框架支持。
把这些选中的文件都删除了,就变成了我们熟悉的模样了。
初始的目录结构展开如下:
1、Springboot01HelloworldApplication类是程序的主入口,因此这里的代码我们不要做修改。
2、application.properties是SpringBoot的核心配置文件。
3、Springboot01HelloworldApplicationTests类就是我们的测试文件。
如果我们在新建项目的时候没有添加web的框架支持,那么我们只需要在pom中添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
不需要声明web框架的版本,默认继承父依赖就好了。
回顾一下SSM写业务的流程,无非就是那四个包:
(1)controller
(2)dao(mapper)
(3)service
(4)pojo
注意包新建到和Springboot01HelloworldApplication同级的目录
新建完包以后,直接在controller上新建HelloController类:
package com.wang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
//调用业务
return "hello,World";
}
}
调通一下,发现程序终止了,查看程序发现自己的端口号异常,这是因为8080端口被我的某个进程占用了,SpringBoot默认的端口为8080冲突了,解决这个问题只需要在applicationcontext.properties中加上语句,切换一个没有被进程占用的端口号就可以解决了:
server.port=8081
接着就可以顺利调通SpringBoot,访问网站得到:
我们可以在resources下新建banner.txt,那么application.properties会接管这里的图案,图案我们可以自己设计,然后跑程序就可以出现这样的图案了:
主启动类是如何运行的
主启动类也就是指SpringApplication,这个类主要做了四件事情:
1、推断应用是普通的项目还是web项目
2、查找并加载所有的可用初始化器,设置到initializers属性中
3、找出所有的应用程序监听器,设置到listeners属性中
4、推断并设置main方法的定义类,找到运行的主类
yaml语法详解
实际上,SpringBoot官方本身是不建议使用application.properties的配置的,我们可以直接把这个文件删除,把文件改为:application.yaml。当然了,不删也行。
把两个配置文件的语法结构比较一下:
(1)application.properties:key=value
(2)application.yaml:key: value
之一“:”后面还有一个空格,否则语法报错。
配置文件的用途就是用来修改SpringBoot自动装配的默认值,因为SpringBoot在底层都给我们配置好了。
以前的配置文件,大多数使用xml来配置,比如一个简单的端口配置,对比一下yaml和xml:
<server>
<port>8080</port>
</server>
server:
port: 8080
看起来,yaml更为轻巧。
yaml的语法比起peoperties也简单很多:
1、yaml存储键值对
name: wangxiongjun
2、yaml可以用很简单的语句存对象:
(1)普通写法
student:
name: wangxiongjun
age: 3
(2)行内写法:
student: {name: wangxiongjun,age: 3}
这个语法就和js很像了。
3、用yaml存数组:
(1)普通写法:
pets:
- cat
- dog
- pig
(2)行内写法:
pets: [cat,dog,pig]
注意点
yaml对空格的很敏感,因此我们的对象写法需要十分的严格,比如:
student:
name: wangxiongjun
age: 3
如果我们这样写,就相当于写了三个对象,student对象值为空罢了
再比如:
student:
name: wangxiongjun
age: 3
这会被yaml解析成student对象拥有一个name的属性,name下面又包含了age的属性。
当然,这些只是yaml的语法,yaml的强大之处在于可以注入到我们的配置类中
给属性赋值的几种方式
1、yaml可以给实体类进行赋值
这些实体类无非就是:对象、数组、键值对、列表这样的东西
来测试一下这一过程:
(1)新建一个pojo包,并新建实体类Dog:
package com.wang.pojo;
import org.springframework.stereotype.Component;
@Component
public class Dog {
private String name;
private Integer age;
public Dog(){
}
public Dog(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)新建Person类:
package com.wang.pojo;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Component
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object>maps;
private List<Object>lists;
private Dog dog;
public Person() {
}
public Person(String name, Integer age, Boolean happy, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
this.name = name;
this.age = age;
this.happy = happy;
this.birth = birth;
this.maps = maps;
this.lists = lists;
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getHappy() {
return happy;
}
public void setHappy(Boolean happy) {
this.happy = happy;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public List<Object> getLists() {
return lists;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", happy=" + happy +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}
}
这里也可以自己导入lombok的依赖来利用注解实现有参构造和无参构造
记得都要加上@Component让两个实体类都作为Spring的组件被扫描
回顾一下Spring对参数进行赋值的方式,利用注解@Value:
@Value("旺财")
private String name;
@Value("3")
private Integer age;
打开测试类,编写代码:
@Autowired
private Dog dog;
@Test
void contextLoads() {
System.out.println(dog);
}
测试运行
现在我们使用yaml对Person进行赋值:
person:
name: wangxiongjun
age: 20
happy: false
birth: 2022/11/15
maps: {k1: v1,k2: v2}
lists:
- sing
- dance
- rap
- basketball
dog:
name: 旺财
age: 3
我们怎么使用yaml赋值的东西呢,只需要在Person中添加注解@ConfigurationProperties:
利用这个语法就可以使用yaml赋值的东西了,使用这个注解以后上面会爆红,但是无所谓,处不处理都没关系。
测试运行,成功:
首页
在resources下,除了之前存在的两个包,我们还要新建两个包,我们都可以在包下面放资源,资源的优先级为:
resources>static>public,我们平时默认的包环境是static。
源码中给出了访问网页的方法,SpringBoot会在resouces目录下寻找index.html文件,在resources下的任意一个包放这个index.html都是可以的(除了templates),我在public包下新建index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
</body>
</html>
运行一下看是否顺利调通:
我们的这个.HTML文件无论放在static还是resources亦或是public目录下都是可以成功跑通的,除了在templates会报错:
要使用这个模板包,我们需要导入pom依赖:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
拓展SpringMVC
如果想要DIY一些定制化的功能,那么只需要写@Configuration这样一个组件,然后把它交给SpringBoot,SpringBoot会帮助我们自动装配。
SpringBoot在自动装配很多组件的时候,先看容器里面有没有用户自己配置的(如果用户自己配置就是上面的@Bean),如果有就用用户自己配置的,如果没有就让SpringBoot自动配置;如果有些组件可以存在多个,比如上面例子的视图解析器,那就会将用户配置的和自己默认的组合起来。
我们要做的就是编写一个@Configuration的注解类,并且类型为WebMvcConfigurer,还不能标注@EnableWebMvc注解。
新建包config,新建类MyMvcConfig:
package com.wang.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Locale;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//ViewResolver实现了视图解析器接口的类,我们就可以把它看做是视图解析器
@Bean
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//自定义一个自己的视图解析器MyViewResolver
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}
}
读者可以自己调通尝试。
注意不要加上@EnableWebMvc注解,用了这个以后,SpringBoot将不会接管MVC。
SpringBoot实战——员工管理系统
需要提示一下这个实战也只是一个熟悉式的实战,也算是复习了很多前端的东西吧。。。连一个数据库都没有建,而且整个项目的漏洞估计也不少,所以是很不成熟的项目。
准备工作
我们把项目整体变成只有config包和controller包,其他文件内容都删干净。
接着使用bootstrap的一个模板,这个模板的资源已上传:
BootStrap模板
但是这个资源上传了以后貌似要钱,也不知道为啥,如果有需要的小伙伴可以直接私聊我,我想办法私发给你。
赋值所有的.html页面到templates里面, 把全部的资源css、img、js都放到静态文件夹static下,如下:
根据开发的流程,我们尽量先从底层入手,那么我们就可以先写数据库,可以先不详细写数据库,简单的伪造一下数据。
1、在com.wang下新建pojo并且新建部门类和员工类:
Department.java:
package com.wang.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String department;
}
注解的实现记得先在pom里面导入lombok。
Employee.java:
package com.wang.config;
import com.wang.pojo.Department;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;//0代表女性,1代表男性
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
this.birth = new Date();
}
}
我们想要默认的产生日期,也即是dao层不需要传参数让他自动生成,那么我们的构造就不能传入这个参数,那么用lombok来进行构造行不通了,自己写一个就好了,不要传入Date的参数。
2、dao层(或是mapper层):
(1)DepartmentDao:
package com.wang.dao;
import com.wang.pojo.Department;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
//部门dao
@Repository//被Spring托管
public class DepartmentDao {
//模拟数据库中的数据
private static Map<Integer, Department> departments=null;
static{
departments=new HashMap<Integer, Department>();//创建一个部门表
departments.put(101,new Department(101,"教学部"));
departments.put(102,new Department(102,"市场部"));
departments.put(103,new Department(103,"教研部"));
departments.put(104,new Department(104,"运营部"));
departments.put(105,new Department(105,"后勤部"));
}
//获得所有部门信息
public Collection<Department> getDepartment(){
return departments.values();
}
//通过id返回部门
public Department getDepartmentById(Integer id){
return departments.get(id);
}
}
(2)EmployeeDao:
package com.wang.dao;
import com.wang.config.Employee;
import com.wang.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Repository//被Spring托管
public class EmployeeDao {
//模拟数据库中的数据
private static Map<Integer, Employee> employees=null;
//员工有所属的部门,这就是一个外表
@Autowired
private DepartmentDao departmentDao;
static{
employees=new HashMap<Integer, Employee>();//创建一个部门表
employees.put(1001,new Employee(1001,"A","2646654595@qq.com",1,new Department(101,"教学部")));
employees.put(1002,new Employee(1002,"B","511846898@qq.com",1,new Department(102,"市场部")));
employees.put(1003,new Employee(1003,"C","511846898123@qq.com",1,new Department(103,"教研部")));
employees.put(1004,new Employee(1004,"D","708837851@qq.com",0,new Department(104,"运营部")));
employees.put(1005,new Employee(1005,"E","3398242986@qq.com",0,new Department(105,"后勤部")));
}
//主键自增
private static Integer initId=1006;
//增加一个员工
public void save(Employee employee){
if(employee.getId()==null)employee.setId(initId++);
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
employees.put(employee.getId(),employee);
}
//查询全部员工信息
public Collection<Employee> getAll(){
return employees.values();
}
//通过id查询员工
public Employee getEmployeeById(Integer id){
return employees.get(id);
}
//根据id删除员工
public void deleteEmployById(Integer id){
employees.remove(id);
}
}
首页实现
我们希望的是在localhost:8081后面加上/或者是/index.html最终都能够访问到我们想要的登录界面。两种方法实现:
(1)拓展SpringMVC
在MyMvcConfig中编写:
package com.wang.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
这里的意思就是在访问这个网址以后定位到index视图层。
(2)在controller中新建IndexController:
package com.wang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping({"/","/index.html"})
public String index(){
return "index";
}
}
两种方式都可以调通一下,这里我在拓展SpringMVC上写。
如果发现这里的样式很丑,长这样,这不是我们想要的样式,也就是静态资源里面的CSS样式没有加载出来,我们就注意一下把路径改对了。我之前没有把这个模板的整个asserts包拷贝过来,所以路径错误,导致静态资源样式没有被加载进来
修改index.html中的href,把所有的asserts/都删除。
同理,如果和我一样这样复制包的话,其他的html文件都记得把使用了静态资源的路径的asserts/都删除了。
接着我们可以配置一下网页的首页访问网址,比如我希望在每次网页的前端的都加上一个/wang,也就是说变成localhost:8081/wang才能访问首页,只需要在配置中加上:
server:
port: 8081
servlet:
context-path: /wang
修改一下server.servlet.context-path即可。
页面国际化
什么是页面国际化,看我们的首页没有办法,可以自由切换中英文,实现这个功能就是页面国际化。
我们的页面点击中文或者英文并没有反应,这是因为我们没有实现国际化。
注意,在进行国际化之前我们必然要考虑中文乱码的问题,打开Settings-Editor-File Encoding,并且把字符集改为UTF-8的
在resources下新建包i18n即国际化的缩写,在i18n下新建login.properties以及login_zh_CN.properties,我们发现idea会自动的帮我们合并到一起。
这个自动生成的包我们可以配置,能够搜索英文的配置名称。
这其中,第一个配置文件表示的是默认页面,第二个配置文件表示英文的页面,第三个配置文件表示的是中文的页面。
那么第一个和第三个配置文件写法:
login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
第二个配置文件写法:
login.btn=Sign in
login.password=Password
login.remember=Remember me
login.tip=Please sign in
login.username=Username
但是我们还需要让SpringBoot识别这些,在application中配置:
# 我们的配置文件放在的真实位置
spring:
messages:
basename: i18n.login
接下来我们要使用thymeleaf模板了,springboot识别到这里的默认配置文件以后,还需要html文件能够找到这里的文本位置,那么我们就需要在html的对应各个控件的位置加上:
<... th:text="login.tip">
类似这样的,当然了,th我们也得先定义在html文件开头:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
最后默认打开就会是中文的版本。
这个代码小修小补的就不放上去了,给个截图读者可以自己去实现一下:
最后重新打开页面:
那么我们如何自动切换语言呢?
首先我们需要在中文和English中加上href跳转:
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
这就需要实现LocaleResolver这个接口了,这个接口的含义就是解析地区,我们只需要在config下加上MyLocaleResolver去实现这个接口即可:
package com.wang.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 request) {
String language = request.getParameter("l");
Locale locale = Locale.getDefault();//如果没有就是用默认的
//如果请求的链接携带了国际化的参数,就要判断它
if(!StringUtils.isEmpty(language)){
//zh_CN
String[] split = language.split("_");
//国家,地区
Locale locale1 = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
接着我们就直接在MyMvcConfig进行bean注入即可:
//自定义的国际化组件就生效了
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
最后点击English,成功实现跳转:
登录功能实现
记得给index的提交表单中的action增加指定一个链接为:
th:action="@{/user/login}
我们只需要在controller中新增方法即可:
package com.wang.controller;
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.thymeleaf.util.StringUtils;
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model){
//具体的业务,这里只是一个模拟
if(!StringUtils.isEmpty(username)&&password.equals("123456")){
return "dashboard";
}else{
model.addAttribute("msg","用户名或密码错误");
return "index";
}
}
}
可能跳转过去以后的CSS样式不见了,这个和thymeleaf有关系,具体啥原因不太清楚,也研究不太来。我们只需要把样式的格式修改为这样的格式就好了:
th:href="@{/css/bootstrap.min.css}
只要账号不为空,且密码为123456,我们就能够成功跳转:
那我们怎样增加回显呢?所谓回显就是账号密码发生错误的时候给用户的一个反馈。只需要在index.html中增加:
<p style="color: red" th:text="${msg}"></p>
当账号或者密码输入有误时,会返回index界面:
但是有可能msg会报错,和controller有关系,解决方法看这个:
Thymeleaf中的th:text=“${msg}“报错——解决方法
咱也不知道咋解决的,反正我的idea按照String的返回类型来做是无法实现的,会报500错误。
登录拦截器
我们在MyMvcConfig中的addViewControllers增加一句:
registry.addViewController("/main.html").setViewName("dashboard");
我们也在LoginController中把登录跳转写成重定向到这个页面的方式:
return "redirect:/main.html";
最后我们发现,我们竟然可以直接访问到这个页面,显然不和规范,因此需要登录拦截器。
config中写方法LoginHandlerInterceptor,实现HandlerInterceptor接口:
package com.wang.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 {
//登录成功之后,允许放行,应该有用户的session
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser==null){//没有登录
request.setAttribute("msg","没有权限,请先登录");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else{
return true;
}
}
}
我们还需要注意的是,并不是所有的跳转我们都要进行拦截,比如输入账号密码以后要进行跳转,只要密码正确,我们就应该要允许跳转,也就是我们要允许初始的页面跳转到登录后的页面这样的设定,除此之外,我们还必须要允许CSS样式和img等的跳转允许,因为我们要满足跳转过去以后还保留正确的样式。
只需要在MyMvcConfig配置中重写方法:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html","/","/user/login","/css/*","/js/**","/img/**");
}
展示员工列表
我们的页面现在有这几个问题:
1、我们应当点击员工列表的时候能够从dashboard页面跳转到list页面。
2、我们跳转过去的页面还应当保留有顶边栏和侧边栏。
那么实际上我们可以修改dashboard的html代码,使得顶边栏和侧边栏成为一个fragment,然后我们在跳转到list页面的时候利用语句:
<div th:insert="~{dashboard::topbar}"></div>
以及
<div th:insert="~{dashboard::sidebar}"></div>
这样我们就可以实现跳转的时候还保留有顶边栏和侧边栏。
3、但是上面的代码质量太差,我们可以要提高代码的可复用性,可以专门写一个html页面commons.html,分别保存两边一样的东西,也就是这里的顶边栏和侧边栏,接着我们在dashboard.html以及list.html中分别写语句:
<div th:replace="~{commons/commons::topbar}"></div>
以及
<div th:replace="~{commons/commons::sidebar}"></div>
这样我们就不需要跳转一个页面之后频繁的插入,而是使用了替换。
这里利用了vue的思想
4、我们跳转过去以后,应该要保证我们点哪个侧边,那么跳转过后对应的侧边栏就应该要高亮。
我们只需要在dashboard中传递参数给list即可。
dashboard.html:
<div th:replace="~{commons/commons::sidebar(active='main.html')}"></div>
list.html
<div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>
5、跳转过后,我们需要能够显示我们的员工列表,这是一个动态的过程,因此模板可以大胆删除静态的thead以及tbody里面的内容,修改为:
<thead>
<tr>
<th>id</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birth</th>
</tr>
</thead>
<tbody>
<tr th:each="emp:${emps}">
<td>[[${emp.getId()}]]</td>
<td>[[${emp.getLastName()}]]</td>
<td th:text="${emp.getEmail()}"></td>
<td th:text="${emp.getGender()==0?'女':'男'}"></td>
<td th:text="${emp.department.getDepartment()}"></td>
<td th:text="${emp.getBirth()}"></td>
</tr>
</tbody>
这里用了两种方法来得到列表元素,自取即可。
最后把页面改的好看一点点,如下:
增加员工实现
增加这个实现无非就是:提交请求,再跳转到添加页面,添加员工成功后返回首页即可。
提交请求我们只需要写一个按钮跳转到一个页面,这个页面我们就可以写一个表单,这个表单中要注意的是下拉框,我们的下拉框用th:each可以遍历到1~5,但是我们展示出来还是要按照部门的名字进行显示的,因此我们需要能够收取到这个部门名字。
这就需要到EmployeeController中:
@GetMapping("/emp")
public String toAddPage(Model model){
//查出所有部门的信息
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "emp/add";
}
然后我们只需要这样写这个下拉框即可:
<select class="form-control" name="department">
<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>
接着我们需要给这个表单写一个post请求:
<form th:action="@{/emp}" method="post">
这个表单的action同样可以写成是@{/emp},这是因为我们到达add.html使用的方法是get,那么表单的请求我们用的是post,看起来好像都是用的一样的映射,但是其实是不一样的方式,这样是不会报错的。
当我们提交完请求以后,我们只需要重定向到首页即可。
@PostMapping("/emp")
public String addEmp(Employee employee){
//添加的操作
employeeDao.save(employee);//调用底层的业务方法保存员工的信息
return "redirect:/emps";
}
当然我们要注意的是直接就这样,重定向以后可能会报400的错误,就要注意排查,错误提示是没有接收到部门的请求,会造成这个的原因是下拉框的参数value是指代的部门的id,那么我们应该给这个下拉框的地址定位到id属性,让dao层自动根据id查询部门。这只需做个简单的修改即可。
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>
</div>
这样我们就能实现新增员工后的重定向页面跳转了。
最后做一个小修小补,我们在配置文件中增加时间日期的格式化,让用户新增的时候用的日期是我们平时使用的日期也就是yyyy-MM-dd的日期格式:
spring中的mvc就是我们配置的日期格式化了,大功告成!
修改员工信息
我们首先要实现当我们点击编辑按钮的时候能够跳转到员工的修改页面,这个修改页面update.html的格式和之前的新增页面大致相同,但是我们需要增加一个回显的功能,也就是要带着原来的数据跳转到这个修改页面去,回显功能的实现需要依赖emp传输过程的value或者selected等等,具体看下面的部分代码:
EmployeeController:
//跳转到员工的修改页面
@GetMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id")Integer id,Model model){
//查出原来的数据
Employee employee=employeeDao.getEmployeeById(id);
model.addAttribute("emp",employee);
//查出所有的部门信息
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "emp/update";
}
注意我们要查出所有的部门信息,是因为我们的部门的选择是一个下拉框,和add.html一样,我们都需要能够把下拉框的数据都显示出来。
update.html:
<form th:action="@{/emp}" method="post">
<input type="hidden" name="id" th:value="${emp.getId()}">
<div class="form-group">
<label>LastName</label>
<input type="text" th:value="${emp.getLastName()}" name="lastName" class="form-control" placeholder="请输入员工姓名">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" th:value="${emp.getEmail()}" name="email" class="form-control" placeholder="请输入员工邮箱账号">
</div>
<div class="form-group">
<label>Gender</label><br>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender==1}" class="form-check-input" type="radio" name="gender" value="1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender==0}" class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>department</label>
<select class="form-control" name="department.id">
<option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}"
th:value="${dept.getId()}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="yyyy-MM-dd">
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
当我们修改的时候,由于程序中写了id自增,所以我们修改后会自动识别为新的员工且id增加了,因此我们加入了隐藏域hidden。
我们为了要能够实现跳转,还需要写一个post请求,如出一辙的操作了属于是:
@PostMapping("/updateEmp")
public String updateEmp(Employee employee){
employeeDao.save(employee);
return "redirect:emps";
}
删除、404处理以及注销操作
删除好解决,和前面一样,这是一个getmapping,我们只需要调用dao的删除方法然后重定向到emps即可。
而404操作页面就不多说了,只要网址访问错误,跳转到404.html就好了。
注销操作也很容易,直接上代码。
删除操作:
@GetMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id")Integer id,Model model){
employeeDao.deleteEmployById(id);
return "redirect:/emps";
}
注销操作:
@RequestMapping("/user/logout")
public String logout(HttpSession httpSession){
httpSession.invalidate();
return "redirect:/index.html";
}
大功告成,一个CRUD项目做完了。
过段时间发一下项目资源,可以自取,也可以单独找我要一下~