准备工作

  1. 新建项目
  2. 导入组件
    1. lombok
    2. SpringWebMVC
    3. thymeleaf
  3. 导入静态资源
    1. JQuery
    2. bootstarp
    3. index,login,dashboard,error
  4. 新建资源路径
    1. 静态资源:static
    2. 公共访问资源路径:public
    3. 上传下载资源模版路径:resources
    4. 控制器:controller
    5. 数据持久层:dao/mapper
    6. 业务逻辑层:service
    7. 实体类:pojo/entity
  5. 新建实体类
    1. 部门类:Department
package com.demo.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private Integer id;
    private String departmentName;
}

2. 员工类:Employee
package com.demo.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private Integer id;

    private String lastName;

    private String email;

    private Integer gender; //1 male, 0 female

    private Department department;

    private Date birth;
}

  1. 新建持久层方法
    1. 部门:DepartmentDao
package com.demo.dao;

import com.demo.pojo.Department;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Repository
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 = Map.of(
//            1, new Department(1, "开发部"),
//            2, new Department(2, "测试部"),
//            3, new Department(3, "财务部"),
//            4, new Department(4, "销售部")
//        );
    }

    // 获得所有部门信息
    public Collection<Department> getDepartments() {
        return departments.values();
    }

    // 通过id得到部门
    public Department getDepartmentById(Integer id) {
        return departments.get(id);
    }


}

2. 员工:Employee
package com.demo.dao;

import com.demo.pojo.Department;
import com.demo.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Repository
public class EmployeeDao {

    private static Map<Integer, Employee> employeeMap = null;

    @Autowired
    private DepartmentDao departmentDao;

    // 模拟数据库中的数据,自增主键
    private static Integer initId = 1006;

    static {
        employeeMap = new HashMap<Integer, Employee>();
        employeeMap.put(1001, new Employee(1001, "Tom", "tom@163.com", 1, new Department(101,
                "开发部"), null));
        employeeMap.put(1002, new Employee(1002, "Jerry", "jerry@163.com", 1, new Department(101,
                "开发部"), null));
        employeeMap.put(1003, new Employee(1003, "Mike", "mike@163.com", 0, new Department(102,
                "开发部"), null));
        employeeMap.put(1004, new Employee(1004, "Mary", "mary@163.com", 0, new Department(102,
                "测试部"), null));
        employeeMap.put(1005, new Employee(1005, "Bob", "bob@163.com", 1, new Department(103,
                "财务部"), null));
    }



    // 增加员工,保存员工
    public void save(Employee employee) {
        if (employee.getId() == null) {
            employee.setId(initId++);
        }

        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
//        employee.setBirth(new Date());
        employeeMap.put(employee.getId(), employee);
    }

    // 查询所有员工
    public Collection<Employee> getAll() {
        return employeeMap.values();
    }

    // 通过id查询员工
    public Employee getEmployeeById(Integer id) {
        return employeeMap.get(id);
    }

    // 删除员工
    public void deleteEmployeeById(Integer id) {
        employeeMap.remove(id);
    }


}

报错的话注意下错误原因,有事lombok不好用,重新生成下Getter/Setter方法,检查下有参构造;

首页

模板:https://getbootstrap.com/docs/4.0/examples/dashboard/#

将index.html,导入到 templates下,启动,从gitee上找了个,显示这个样子,是配置了国际化…

启动页面跳转的方法有三种:

  1. 直接将index.html放到加载资源路径的根目录下,ps:当时那四个目录下,一般放到templates下就可以;
  2. 在Controller中配置页面跳转,{"/","/index.html"}两种方式访问都会到index
@RequestMapping({"/","/index.html"})
public String index() {
    return "index";
}
  1. 使用自己配置接管下SpringMVC;
@Configuration
public class MyConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}

配置thymeleaf

  1. 先把那些国际化显示的东西删一下,大概都是些静态值,根据页面显示内容;去i18n中去获取Properties;
  2. 页面大体正常;
  3. 关闭thymeleaf的模版引擎:spring.thymeleaf.enabled=false
    1. 后面使用前后端分离(Vue。React),不用thymeleaf,记得关掉;
    2. 返回json数据,通过前端处理;
    3. 关闭模版引擎后发现报错404,检查问题原因,
    4. 发现属性设置错了应该是关闭缓存,让直接加载,我直接把模版引擎给关闭了;换成spring.thymeleaf.cache=false,重启,页面正常显示;
  4. thymeleaf引用使用@{}来管理代码,使用@管理@{/}/可以适应项目的任何class目录可以万能匹配;
  5. 配置首页生效 server.servlet.context-path=/admin

所有HTTP请求的URL路径都会以/admin作为前缀

例如:原访问路径为/user/list,配置后需通过/admin/user/list访问

所有页面的静态资源使用thymeleaf接管,使用url的统一使用@接管;

国际化操作

国际化内容:https://springdoc.cn/spring-boot/features.html#features.internationalization

准备工作

  1. 先在IDEA中统一设置properties的编码问题!

  1. 建一个目录为i18n``--> international 的缩写
  2. 建一个login.propertieslogin_en_US.properties,login_zh_CN.properties,idea自动识别生成一个resources资源包;

配置国际化

  1. 安装idea的插件包之后,可以可视化显示
  2. MessageAutoConfiguration

SpringBoot已经自动配置好了管理我们国际化资源文件的组件ResourceBundleMessageSource

// 获取 properties 传递过来的值进行判断
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    if (StringUtils.hasText(properties.getBasename())) {
        // 设置国际化文件的基础名(去掉语言国家代码的)
        messageSource.setBasenames(
            StringUtils.commaDelimitedListToStringArray(
                                       StringUtils.trimAllWhitespace(properties.getBasename())));
    }
    if (properties.getEncoding() != null) {
        messageSource.setDefaultEncoding(properties.getEncoding().name());
    }
    messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
    Duration cacheDuration = properties.getCacheDuration();
    if (cacheDuration != null) {
        messageSource.setCacheMillis(cacheDuration.toMillis());
    }
    messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
    messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
    return messageSource;
}
  1. MessageSourceProperties
  2. 配置属性:application.properties

  1. 国际化消息使用#去取
  2. 测试页面显示
  3. 逐步完善,二次测试
  4. 请求使用的是中文,所以显示的时候是中文;

自定义配置页面国际化值 :配置国际化解析

  1. WebMvcAutoConfiguration,发现容器中 不存在localeResolver是才创建,
  2. 我们自己写一个localResolver,之后再在前端设置属性;
//可以在链接上携带区域信息
public class MyLocaleResolver implements LocaleResolver {

    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {

        String language = request.getParameter("l");
        Locale locale = Locale.getDefault(); // 如果没有获取到就使用系统默认的
        //如果请求链接不为空
        if (!StringUtils.isEmpty(language)){
            //分割请求参数
            String[] split = language.split("_");
            //国家,地区
            locale = new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
}
<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>
  1. 在自己前面定义的MyConfig将这个bean注入进去
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
  1. 重启测试

默认进入

按下【English】的a标签

按下【中文】的a标签,同理

总结

  1. 配置i18n文件;
  2. 如果需要有按钮自动切换需要自定义组件;
  3. 将自己写的组件配置大棚Spring容器中去;

登录功能

实现登录跳转

  1. 逻辑校验
@RequestMapping("/user/login")
// @ResponseBody
public String login(@RequestParam("username") String username, @RequestParam("password") String password , Model  model) {

    if (StringUtils.hasLength(username) || StringUtils.hasLength(password)){
        return "dashboard.html";
    } else {
        model.addAttribute("msg","用户名密码不能为空");
        return "index";
    }

}
  1. 把前端必须输入的required去掉,测试

  1. 输入为空的情况
<!-- 如果msg的值为空就不显示这条消息-->
<p style="color: red" th:if="${not #strings.isEmpty(msg)}" th:text="${msg}"></p>

实现登录拦截

  1. 写一个拦截器
public class LoginHandleInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 获取session,判断Session是否正常登录
        Object  username =  request.getSession( ).getAttribute("UserSession");
        if(username == null){
            request.setAttribute("msg","请先登录");
            request.getRequestDispatcher("/login").forward(request, response);
            return false;
        } else {
            return true;
        }
    }
}

  1. 配置到bean中进行注册
@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 注册一个拦截器,添加一个拦截器,拦截所有请求,除了excludePathPatterns指定的请求路径
    registry.addInterceptor(new LoginHandleInterceptor())
        .addPathPatterns("/**")
        .excludePathPatterns("/", "/index.html", "/user/login", "/css/*", "/js/*", "/fonts/*", "/img/*");
}
  1. Controller把Session设置进去
@RequestMapping("/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password , Model  model, HttpSession session) {
    if (StringUtils.hasLength(username) && StringUtils.hasLength(password)){
        session.setAttribute("UserSession",username);
        return "dashboard";
    } else {
        model.addAttribute("msg","用户名密码不能为空");
        return "index";
    }
}
  1. 测试,显示正常
  2. 注意事项:
    1. 请求地址是否一致
    2. 拦截的地址是不是有请求地址
    3. 是否有进行bean注入
    4. 检查请求的时候Form表单的action地址,拦截器之后返回的页面地址请求,拦截器去除之后跳转的地址;

数据查询

三件套:Dao,View,Controller

  1. 写一个Controller
    @Autowired
    EmployeeDao employeeDao;

    @Autowired
    DepartmentDao departmentDao;
    @RequestMapping("/emps")
    private String getList(Model model) {
        Collection<Employee> employeeDaoAll = employeeDao.getAll();

        model.addAttribute("emps", employeeDaoAll);

        return "emp/list";
    }
  1. 在View端接收,使用thymeleaf语法
<div class="table-responsive">
  <table class="table table-striped table-sm">
    <thead>
      <tr>
        <th>工号</th>
        <th>姓名</th>
        <th>邮箱</th>
        <th>性别</th>
        <th>部门</th>
        <th>出生日期</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr th:each="emp:${emps}">

        <td th:text="${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.getDepartmentName()}"></td>
        <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
        <td>
          <a class="btn btn-sm btn-primary" th:href="@{'/emp/'+${emp.getId()}}">编辑</a>
          <a class="btn btn-sm btn-danger" th:href="@{'/delemp/'+${emp.getId()}}">删除</a>
        </td>
      </tr>
    </tbody>
  </table>
</div>
  1. 页面显示

数据添加

  • 添加需要再添加页面查找下部门信息,需要和员工关联,所以需要先去取一下 部门的数据、
  • 依旧是三件套,多一个跳转到add页面的Controller;
  1. Controller
@RequestMapping("/emp")
private String toAddPage(Model model) {
    model.addAttribute("departments", departmentDao.getDepartments());
    return "emp/add";
}

@RequestMapping("/addEmp")
private String addEmp(Employee employee) {
    employeeDao.save(employee);
    return "redirect:emps";
}
  1. View

  <h2><a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a></h2>
<form th:action="@{/emp}" method="post">
  <div class="form-group">
    <label>工号</label>
    <input type="text" name="emp_code" class="form-control" placeholder="hxl">
  </div>
  <div class="form-group">
    <label>姓名</label>
    <input type="text" name="emp_name" class="form-control" placeholder="嗷嗷嗷">
  </div>
  <div class="form-group">
    <label>邮箱</label>
    <input type="email" name="email" class="form-control" placeholder="111@qq.com">
  </div>
  <div class="form-group">
    <label>性别</label><br>
    <div class="form-check form-check-inline">
      <input 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 class="form-check-input" type="radio" name="gender" value="0">
      <label class="form-check-label"></label>
    </div>
  </div>
  <div class="form-group">
    <label>部门编码</label>
    <!--我们在controller中接受的是一个 Employee 所以我们需要提交的是其中一个属性	-->
    <select class="form-control" name="dept_code">
      <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()()}"></option>
    </select>
  </div>
  <div class="form-group">
    <label>出生年月</label>
    <input type="date"  name="birth" class="form-control">
  </div>
  <button type="submit" class="btn btn-primary">添加</button>
</form>
  1. Dao数据处理
package com.demo.dao;

import com.demo.pojo.Department;
import com.demo.pojo.Employee;
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
public class EmployeeDao {

    private static Map<Integer, Employee> employeeMap = null;

    static {
        employeeMap = new HashMap<Integer, Employee>();
        employeeMap.put(1001, new Employee(1001, "Tom", "tom@163.com", 1, new Department(101,
                                                                                         "开发部"), null));
        employeeMap.put(1002, new Employee(1002, "Jerry", "jerry@163.com", 1, new Department(101,
                                                                                             "开发部"), null));
        employeeMap.put(1003, new Employee(1003, "Mike", "mike@163.com", 0, new Department(102,
                                                                                           "开发部"), null));
        employeeMap.put(1004, new Employee(1004, "Mary", "mary@163.com", 0, new Department(102,
                                                                                           "测试部"), null));
        employeeMap.put(1005, new Employee(1005, "Bob", "bob@163.com", 1, new Department(103,
                                                                                         "财务部"), null));
    }

    // 模拟数据库中的数据,自增主键
    private static Integer initId = 1006;

    @Autowired
    private DepartmentDao departmentDao;


    // 增加员工,保存员工
    public void save(Employee employee) {
        if (employee.getId() == null) {
            employee.setId(initId++);
        }

        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        //        employee.setBirth(new Date());
        employeeMap.put(employee.getId(), employee);
    }

    // 查询所有员工
    public Collection<Employee> getAll() {
        return employeeMap.values();
    }

    // 通过id查询员工
    public Employee getEmployeeById(Integer id) {
        return employeeMap.get(id);
    }

    // 删除员工
    public void deleteEmployeeById(Integer id) {
        employeeMap.remove(id);
    }


}
  1. 页面显示

  1. 出现一个bug点,使用日期选择器选择的日期的格式不是Date类型,是LocalDate类型,并且不能输入只能选择,于是变为text框,设置formatterplaceholder,后台也将只设置一致,最后显示内容如下:

  1. 又出现一个bug点,一直提示报错,显示NullPointExcepftion,页面到这里就404,断点发现Employee,一直是空,检查后发现传值的地方写的不对。把form表单中关于部门的内容改了下,传的是部门对象的id,这样就对应完了。
java.lang.NullPointerException: Cannot invoke "com.demo.pojo.Department.getId()" because the return value of "com.demo.pojo.Employee.getDepartment()" is null
<div class="form-group">
  <label>部门编码</label>
  <!--我们在controller中接受的是一个 Employee 所以我们需要提交的是其中一个属性 -->
  <select class="form-control" name="department.id">
    <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
  </select>
</div>

数据修改

修改使用的action是/emp/{emp.getId()},对应写一个请求 ,查询数据 并挑战到另一个add页面;

在add页面增加;

内容如下:

  1. Controller
    @GetMapping("/emp/{id}")
    private String toModifyPage(@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";
    }

    @PostMapping("/updateEmp")
    private String modifyEmp(Employee employee) {
        employeeDao.save(employee);
        return "redirect:emps";
    }
  1. view
<form method="post" th:action="@{/emp}">
  <!--						<div class="form-group">-->
  <!--							<label>工号</label>-->
  <!--							<input type="text" name="emp_code" class="form-control" placeholder="hxl">-->
  <!--						</div>-->
  <!--						<div class="form-group">-->
  <!--							<label>密码</label>-->
  <!--							<input type="password" name="pwd" class="form-control">-->
  <!--						</div>-->
  <div class="form-group">
    <label>姓名</label>
    <input class="form-control" name="emp_name" placeholder="请输入姓名" type="text">
  </div>
  <div class="form-group">
    <label>邮箱</label>
    <input class="form-control" name="email" placeholder="请输入姓名邮箱" type="email">
  </div>
  <div class="form-group">
    <label>性别</label><br>
    <div class="form-check form-check-inline">
      <input class="form-check-input" name="gender" type="radio" value="1">
      <label class="form-check-label"></label>
    </div>
    <div class="form-check form-check-inline">
      <input class="form-check-input" name="gender" type="radio" value="0">
      <label class="form-check-label"></label>
    </div>
  </div>
  <div class="form-group">
    <label>部门编码</label>
    <!--我们在controller中接受的是一个 Employee 所以我们需要提交的是其中一个属性	-->
    <select class="form-control" name="department.id">
      <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}"
        th:value="${dept.getId()}"></option>
    </select>
  </div>
  <div class="form-group">
    <label>出生年月</label>
    <input class="form-control" dataformatas="yyyy-MM-dd" name="birth" placeholder="1970/01/01 20:11:01"
      type="text">
  </div>
  <button class="btn btn-primary" type="submit">添加</button>
</form>
  1. 测试页面

Before

After

数据删除

使用 同上所述,删除不会打开新的页面,只会删除数据,数据删除完 重新加载明细页面显示数据

  1. Controller
@GetMapping("/delEmp/{id}")
private String deleteEmp(@PathVariable("id") Integer id) {
    employeeDao.deleteEmployeeById(id);
    return "redirect:emps";
}
  1. Model

    // 模拟的数据,对于模拟的Map,使用remove移除元素,static 是默认加载的数据,模拟原有数据

    private static Map<Integer, Employee> employeeMap = null;

    static {
        employeeMap = new HashMap<Integer, Employee>();
        employeeMap.put(1001, new Employee(1001, "Tom", "tom@163.com", 1, new Department(101,
                "开发部")));
        employeeMap.put(1002, new Employee(1002, "Jerry", "jerry@163.com", 1, new Department(101,
                "开发部")));
        employeeMap.put(1003, new Employee(1003, "Mike", "mike@163.com", 0, new Department(102,
                "开发部")));
        employeeMap.put(1004, new Employee(1004, "Mary", "mary@163.com", 0, new Department(102,
                "测试部")));
        employeeMap.put(1005, new Employee(1005, "Bob", "bob@163.com", 1, new Department(103,
                "财务部")));
    }


    // 删除员工
    public void deleteEmployeeById(Integer id) {
        employeeMap.remove(id);
    }
  1. 测试

Before:

After:

w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Method parameter 'id': Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; For input string: "emps"]

显示报错:检查Controller没有问题,突然想到 可能是/的问题,加上斜杠看看

    @GetMapping("/delEmp/{id}")
    private String deleteEmp(@PathVariable("id") Integer id) {
        employeeDao.deleteEmployeeById(id);
        System.out.println("删除员工id:"+id);
        return "redirect:/emps";
    }

页面显示:

404异常页面

在template下面建一个error文件夹目录,在这个目录里放进去404.html就可以了

详细见:Web

退出登录

1、在<font style="color:rgb(64, 72, 91);">commons.html</font>中修改注销按钮

<a class="nav-link" th:href="@{/user/logout}">注销</a>

2、在<font style="color:rgba(0, 0, 0, 0.8);background-color:rgb(247, 247, 249);">LoginController.java</font>中编写注销页面代码

@RequestMapping("/user/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/index.html";
}

清除Session的数据就把缓存清楚了,Session没有数据不论如何都会返回开始页面

写网站

  1. 前端搞定:页面长什么样子
  2. 设计数据库(数据库设计难点)
  3. 前端让他能够自动运行,独立化工程
  4. 数据接口如何对接:json,对象,all in one!
  5. 前后端联调测试

前端

  • 页面式样
    • index:首页
    • about:信息关联页面
    • blog:博客信息显示
    • post:提交页面
    • user:用户管理页面
  • 前端独立化
  • 模版
    • 开源模版
    • 熟系一套自己的模版
  • 组件
    • BootStrap
    • Layui
    • ElementUI

后端

  • 业务逻辑
  • 数据取得
  • 数据对接

数据库

  • 数据存储
  • 数据结构
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐