【Spring Boot】构建后台管理系统

网友投稿 240 2022-08-25

【Spring Boot】构建后台管理系统

文章目录

​​Spring Boot 构建后台管理系统​​

​​一、项目创建​​​​二、静态资源处理​​​​三、简单布局​​

​​1.项目结构​​​​2.任务目标​​​​3.具体实现​​

​​3.1 登录界面定制​​​​3.2 构建用户对象类​​​​3.3 Controller层​​​​3.4 表单修改​​

​​四、模板抽取​​

​​4.1 Thymeleaf 的使用​​​​4.2 网页基本布局​​

​​五、页面跳转​​​​六、数据渲染​​

​​6.1 表格内容的遍历​​

​​七、拦截器​​

​​7.1 HandlerInterceptor 接口​​​​7.2 配置容器注册拦截器​​​​7.3 拦截器原理​​

​​八、文件上传​​

​​8.1 文件提交表单​​​​8.2 提交信息测试​​​​8.3 表单文件存储​​​​8.4 文件上传自动配置原理​​

​​九.异常处理​​

​​9.1 错误处理​​

​​1、默认规则​​​​2、定制错误处理逻辑​​​​3、异常处理自动配置原理​​​​4、异常处理步骤流程​​

Spring Boot 构建后台管理系统

一、项目创建

使用Spring Initializr创建新项目,选中所需模块 thymeleaf、web-starter、devtools、lombok。

​​返回顶部​​

二、静态资源处理

自动配置好,我们只需要把所有静态资源放到 static 文件夹下

​​返回顶部​​

三、简单布局

1.项目结构

2.任务目标

主要目标就是实现简单的登录界面、用户登陆跳转到主界面

3.具体实现

3.1 登录界面定制

登陆界面:用户名、密码

3.2 构建用户对象类

package com.zyx.core.web.demo.bean;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;@Data@ToString@NoArgsConstructor@AllArgsConstructorpublic class User { private String username; private String password;}

3.3 Controller层

LoginPage() 方法给出登陆页面请求,最终转到login.html

Form() 方法实现页面表单提交请求处理:

User user封装登陆的用户对象信息HttpSession session将登陆的用户信息暂存在session中,用于页面跳转信息传递Model model将信息存贮在请求域中

主要的逻辑思路:

发送登陆请求实现登陆界面的显示前端代码实现登陆表单的信息提交,判断用户输入信息(这里暂时不和数据库连接,就默认username不为空,密码为123456即可!)登陆到主页面后,如果Form()直接给出 /main 请求,那么每一次刷新主页面,都是一次表单的重提交。为了避免表单的重复提交,我们可以在转到main.html之前进行用户的信息判断,利用session中存储的用户信息,是否为登录状态;若为登录状态则直接转到主页面;否则回退到登录界面重新登陆。编写 MainPage() 进行用户的登陆判断(相当于拦截器), 在 Form() 中用户登陆后,就将信息存在session中,并且重定向至 MainPage() 进行用户登陆的判断。

package com.zyx.core.web.demo.controler;import com.zyx.core.web.demo.bean.User;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import javax.servlet.class WebController { /** * 登陆 * @return */ @GetMapping(value = {"/","/login"}) public String LoginPage(){ return "login"; } /** * 表单提交 * @param user * @param session * @param model * @return */ @PostMapping(value = "/login") public String Form(User user, HttpSession session, Model model){ // 判断登陆信息的完整性 if ((StringUtils.hasLength(user.getUsername())) && (StringUtils.hasLength(user.getPassword())) && ("123456".equals(user.getPassword()))) { // 把登陆成功的用户保存起来 session.setAttribute("loginUser", user); // 登陆成功重定向到主页面 return "redirect:/main.html"; } else { // 提示信息 model.addAttribute("msg", "账号或密码有误~"); // 回到登录页 return "login"; } } /** * 主页面 * @param session * @param model * @return */ @GetMapping(value = "/main.html") public String MainPage(HttpSession session,Model model){ // 判断是否登陆 Object loginUser = session.getAttribute("loginUser"); // 通过session存储的用户对象信息判断 if (loginUser != null){ return "main"; } else { // 为空表示没有登陆,跳回登陆界面 model.addAttribute("msg","未登录!请先登录~"); return "login"; } }}

3.4 表单修改

​​返回顶部​​

四、模板抽取

4.1 Thymeleaf 的使用

th:insert/replace/include

th:insert is the simplest: it will simply insert the specified fragment as the body of its host tag.th:replace actually replaces its host tag with the specified fragment.th:include is similar to th:insert , but instead of inserting the fragment it only inserts the contents of this fragment.

© 2011 The Good Thymes Virtual Grocery
...
The Result is: ...
© 2011 The Good Thymes Virtual Grocery insert将整个带标签内容插入当前标签内部
© 2011 The Good Thymes Virtual Grocery replace使用引用的标签替换当前的外层标签
© 2011 The Good Thymes Virtual Grocery include使用引用标签中的文本内容填充当前标签

4.2 网页基本布局

如下图所示,基本的管理系统界面主要包含三部分左边导航栏、头部导航栏、中间主题内容部分,并且在不同的导航跳转界面中,左侧导航栏、头部导航栏部分基本保持不变。

所以我们可以将这两部分看做是公共部分,进行模板的抽取,当需要利用的时候我们就进行提取,主体部分可以根据具体的几面具体设置。

新建common.html存储公共界面信息。注意抽取的同时提取对应的 js、css资源文件。

抽取完后,接下来就需要引用了。

​​返回顶部​​

五、页面跳转

假设导航栏中的上面一部分,我们需要实现页面的跳转;如果按照正常写法,每个页面的跳转都需要重新编写导航栏部分,代码量就会显得很大,实现了模板的抽取后,只需要引用需要即可。并且需要修改只需要修改公共抽取页面一个地方的信息即可,接下来我们实现页面的跳转。

针对于表格页面,我们单独定义一个Controller,给出请求路径,实现跳转:

package com.zyx.core.web.demo.controler;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;@Controllerpublic class TableController { /** * basic_table * @return */ @GetMapping(value = "/basic_table") public String basic_table(){ return "table/basic_table"; } /** * dynamic_table * @return */ @GetMapping(value = "/dynamic_table") public String dynamic_table(){ return "table/dynamic_table"; } /** * editable_table * @return */ @GetMapping(value = "/editable_table") public String editable_table(){ return "table/editable_table"; } /** * pricing_table * @return */ @GetMapping(value = "/pricing_table") public String pricing_table(){ return "table/pricing_table"; } /** * responsive_table * @return */ @GetMapping(value = "/responsive_table") public String responsive_table(){ return "table/responsive_table"; }}

给出请求后,我们只需要在页面中的对应部分指定连接跳转请求即可,首先我们将 basic_table.html 实现抽取填充:

页面的跳转只需要在公共抽取部分修改一次即可(以basic_table为例):

效果展示:

​​返回顶部​​

六、数据渲染

6.1 表格内容的遍历

@GetMapping("/dynamic_table")public String dynamic_table(Model model){ // 表格内容的遍历 List users = Arrays.asList(new User("zhangsan", "123456"), new User("lisi", "123444"), new User("haha", "aaaaa"), new User("hehe ", "aaddd")); // 将user信息存储在请求域中 model.addAttribute("users",users); return "table/dynamic_table";}

#

用户名

密码

Trident

Internet

[[${user.password}]]

# 用户名 密码
Trident Internet [[${user.password}]]

​​返回顶部​​

七、拦截器

7.1 HandlerInterceptor 接口

拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。

定义拦截器要实现HandlerInterceptor接口,并实现该接口中提供的三个方法:

preHandle方法:进入Handler方法之前执行。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。postHandle方法:进入Handler方法之后,返回ModelAndView之前执行。可以看到该方法中有个modelAndView的形参。应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里同一指定视图。afterCompletion方法:执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等。

package com.zyx.core.web.demo.interceptor;import lombok.extern.slf4j.Slf4j;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.javax.servlet.javax.servlet.class LoginInterceptor implements HandlerInterceptor { /** * 目标方法执行之前 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); log.info("拦截的请求路劲是:"+requestURI); // 登陆检查 HttpSession session = request.getSession(); Object loginUser = session.getAttribute("loginUser"); if (loginUser!=null){ // 放行 return true; } // 拦截 request.setAttribute("msg","请先登陆!!!"); // 重定向至登陆界面 request.getRequestDispatcher("/").forward(request,response); return false; } /** * 目标方法执行完成后 * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle方法执行了"+modelAndView); } /** * 页面渲染完成后 * @param request * @param response * @param handler * @param ex * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("afterCompletion方法执行了,异常是:"+ex); }}

7.2 配置容器注册拦截器

package com.zyx.core.web.demo.config;import com.zyx.core.web.demo.interceptor.LoginInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * 1.编写一个拦截器实现HandlerInterceptor接口 * 2.将拦截器注册到容器中 (实现WebMvcConfigurer接口的addInterceptors方法) * 3.指定拦截规则 ---- /** 会拦截所有包括静态资源文件 */@Configuration // 配置容器public class AdminWebConfig implements WebMvcConfigurer { /** * 添加拦截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { // 注册拦截器 registry.addInterceptor(new LoginInterceptor()) // 添加拦截器 .addPathPatterns("/**") // 拦截所有路径 --- 静态资源也会被拦截;不加下面一行页面显示没有样式! .excludePathPatterns("/", "/login","/css/**","/fonts/**","/images/**","/js/**"); // 放行的路径 }}

当配置了拦截器后我们可以将之前的用戶登陆检验注释,并添加日志測試:

可以看出,在注释了==/main.html==中的测试后,再次运行直接登陆主页面,拦截器起到了作用,对登陆用户进行了判断,实施拦截!

然后我们进行登陆测试:

7.3 拦截器原理

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】

2、先来顺序执行 所有拦截器的 preHandle方法

3、如果任何一个拦截器返回false,直接跳出不执行目标方法(如上图)

4、所有拦截器都返回True,执行目标方法

5、倒序执行所有拦截器的postHandle方法。

6、前面的步骤有任何异常都会直接倒序触发 afterCompletion

7、页面成功渲染完成以后,也会倒序触发 afterCompletion

8.示意图

​​返回顶部​​

八、文件上传

8.1 文件提交表单

8.2 提交信息测试

package com.zyx.core.admin.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RequestPart;import org.springframework.web.multipart.MultipartFile;@Slf4j@Controllerpublic class FileUploadController { /** * MultipartFile 会自动封装 * * @param email * @param username * @param headerImage * @param photos * @return */ @PostMapping(value = "/upload") public String FileUpload(@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImage") MultipartFile headerImage, @RequestPart("photos") MultipartFile[] photos) { // 控制台输出 测试上传的信息 log.info("上传的信息:email={},username={},headerImage_size={},photos={}", email, username, headerImage.getSize(), photos); return "index"; }}

8.3 表单文件存储

package com.zyx.core.admin.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RequestPart;import org.springframework.web.multipart.MultipartFile;import java.io.File;@Slf4j@Controllerpublic class FileUploadController { /** * MultipartFile 会自动封装 * * @param email * @param username * @param headerImage * @param photos * @return */ @PostMapping(value = "/upload") public String FileUpload(@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImage") MultipartFile headerImage, @RequestPart("photos") MultipartFile[] photos) { // 测试上传的信息 log.info("上传的信息:email={},username={},headerImage_size={},photos={}", email, username, headerImage.getSize(), photos); // 单文件存储 if (headerImage != null) { try { // 获取文件名 String originalFilename = headerImage.getOriginalFilename(); // 存储文件 headerImage.transferTo(new File("G:\\Projects\\IdeaProjects\\MyWeb\\src\\main\\resources\\upload\\" + originalFilename)); } catch (Exception e) { e.printStackTrace(); } } // 多文件存储 if (photos.length>0){ for (MultipartFile photo:photos){ if (photo!=null){ try { // 获取文件名 String originalFilename = photo.getOriginalFilename(); // 存储文件 photo.transferTo(new File("G:\\Projects\\IdeaProjects\\MyWeb\\src\\main\\resources\\upload\\" + originalFilename)); } catch (Exception e) { e.printStackTrace(); } } } } return "index"; }}

8.4 文件上传自动配置原理

文件上传自动配置类-MultipartAutoConfiguration - MultipartProperties

自动配置好了StandardServletMultipartResolver 【文件上传解析器】原理步骤

1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求2、参数解析器来解析请求中的文件内容封装成MultipartFile3、将request中文件信息封装为一个Map;MultiValueMap

FileCopyUtils => 实现文件流的拷贝

@PostMapping("/upload")public String upload(@RequestParam("email") String email, @RequestParam("username") String username, @RequestPart("headerImg") MultipartFile headerImg, @RequestPart("photos") MultipartFile[] photos)

​​返回顶部​​

九.异常处理

9.1 错误处理

1、默认规则

2、定制错误处理逻辑

自定义错误页

error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页

@ControllerAdvice+@ExceptionHandler处理全局异常;底层是ExceptionHandlerExceptionResolver 支持的@ResponseStatus+自定义异常 ;底层是ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用response.sendError(statusCode, resolvedReason);tomcat发送的/errorSpring底层的异常,如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。

自定义实现 HandlerExceptionResolver 处理异常;可以作为默认的全局异常处理规则ErrorViewResolver实现自定义处理异常;

response.sendError 。error请求就会转给controller你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controllerbasicErrorController 要去的页面地址是ErrorViewResolver;

3、异常处理自动配置原理

ErrorMvcAutoConfiguration 自动配置异常处理规则

容器中的组件:类型:DefaultErrorAttributes ->id:errorAttributes

**容器中的组件:类型:**BasicErrorController --> id:basicErrorController(json+白页 适配响应)

处理默认/error 路径的请求;页面响应newModelAndView(“error”, model);容器中有组件 View->id是error;(响应默认错误页)容器中放组件BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。

**容器中的组件:**类型:**DefaultErrorViewResolver -> id:**conventionErrorViewResolver

如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面error/404、5xx.html

如果想要返回页面;就会找error视图【StaticView】。(默认是一个白页)

写出去json

4、异常处理步骤流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException

2、进入视图解析流程(页面渲染)

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;

1、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;2、默认没有任何人能处理异常,所以异常会被抛出

​​返回顶部​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:2021-01-22 neo4j-二进制安装
下一篇:【skLearn 降维算法】PCA
相关文章

 发表评论

暂时没有评论,来抢沙发吧~