目录
2.Spring Boot整合Spring MVC 的自动化配置功能特性
SpringMVC基本概念
SpringMVC可以理解为对Servlet封装的MVC框架,当前,Java Web技术底层由两种实现⽅式︓⼀种基于Servlet,⼀种基于Netty,前者由Spring MVC框架等,后者则有WebFlux。
Spring的模型-视图-控制器(MVC)框架是围绕⼀个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并⽀持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚⾄还能⽀持⽂件上传。
提示:以下是本篇文章正文内容,下面案例可供参考
SpringMVC执⾏流程:如图
作为Web MVC的实现,职责对应关系为︓DispatcherServlet -> 前端控件器, HandlerMapping与ViewResolver-> 应⽤控制器, Handler -> 页⾯控制器。
1. ⽤户请求 -> DispatcherServlet DispatcherServlet:本质上是⼀个Servlet,相当于⼀个中转站,所有的访问都会⾛到这个Servlet中,再根据配置进⾏中转到相应的Handler中进⾏处理,获取到数据和视图后,在使⽤相应视图做出响应。 |
2. DispatcherServlet -> HandlerMapping,HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含⼀个Handler处理器(页⾯控制器)对象、多个HandlerInterceptor拦截器)对象HandlerMapping︓本质上就是⼀段映射关系,将访问路径和对应的Handler存储为映射关系,在需要时供前端控制器查阅。 |
3. DispatcherServlet -> HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从⽽⽀持多种类型的处理器 HandlerAdapter︓本质上是⼀个适配器,可以根据要求找到对应的Handler来运⾏。前端控制器通过处理器映射器找到对应的Handler信息之后,将请求响应和对应的Handler信息交由处理器适配器处理,处理器适配器找到真正handler执⾏后,将结果即model和view返回给前端控制器。 |
4. DispatcherServlet -> 处理器功能处理⽅法的调⽤,HandlerAdapter会根据适配的结果调⽤真正的处理器的功能处理⽅法,完成功能处理,并返回⼀个ModelAndView对象(包含模型数据、逻辑视图名)。 |
5. DispatcherServlet -> ViewResolver,ViewResolver将把ModelAndView对象的逻辑视图名解析为具体的View ViewResolver︓本质上也是⼀种映射关系,可以将视图名称映射到真正的视图地址。前端控制器调⽤处理器适配完成后得到model和view,将view信息传给视图解析器得到真正的view。 |
6. DispatcherServlet -> View渲染,View会根据传进来的Model模型数据进⾏渲染 View︓本质上就是将handler处理器中返回的model数据嵌⼊到视图解析器解析后得到的页⾯中,向客户端做出响应。 |
7. DispatcherServlet -> ⽤户 |
一、Spring MVC 自动配置介绍
1.自动配置介绍
在Spring Boot项目中,一旦引入了Web依赖启动器spring-boot-starter-web,那么Spring Boot整合Spring MVC框架默认实现的一些XxxAutoConfiguration自动配置类就会自动生效,几乎可以在无任何额外配置的情况下进行Web开发。
2.Spring Boot整合Spring MVC 的自动化配置功能特性
- 内置了两个视图解析器:ContentNegotiatingViewResolver和BeanNameViewResolver;
- 支持静态资源以及WebJars;
- 自动注册了转换器和格式化器;
- 支持Http消息转换器;
- 自动注册了消息代码解析器;
- 支持静态项目首页index.html;
- 支持定制应用图标favicon.ico;
- 自动初始化Web数据绑定器ConfigurableWebBindingInitializer。
二、Spring MVC 功能拓展实现
1.搭建步骤
步骤如下:
2、功能拓展实现
3、效果测试
1、项目基础环境搭建(示例):
使用Spring Initializr方式创建Spring Boot项目,并在Dependencies依赖选择中选择Web依赖启动器和Thymeleaf依赖启动器:
项目初始化结构图如下:
2.功能拓展实现
1、注册视图管理器,创建一个实现WebMvcConfigurer接口的配置类MyMVCconfig,用于对MVC框架功能扩展。
@Configuration public class MyMVCconfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/toLoginPage").setViewName("login"); registry.addViewController("/login.html").setViewName("login"); } }
2.项目启动成功后,在浏览器上分别访问http://localhost:8080/toLoginPage和http://localhost:8080/login.html 。(如图:
3、注册自定义拦截器MyInterceptor,实现HandlerInterceptor 接口,在该类中编写如下方法
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); Object loginUser = request.getSession().getAttribute("loginUser"); if (uri.startsWith("/admin") && null == loginUser) { response.sendRedirect("/toLoginPage"); return false; } return true;}
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { request.setAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR)); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { }
4、在自定义配置类MyMVCconfig中,重写addInterceptors()方法注册自定义的拦截器
@Autowired private MyInterceptor myInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login.html"); }
3.效果测试
重启项目,启动成功后,在浏览器上访问http://localhost:8080/admin.
三.简单案例
页⾯跳转功能按照之前的例⼦创建项⽬,引⼊依赖(spring web, thymeleaf),在模板⾥创建test1.html,test2.html,之后在config⽂件夹⾥创建MyMvcConfig类。
MyMvcConfig类:
@Configuration
publicclassMyMvcConfigimplementsWebMvcConfigurer{
@Override
publicvoidaddViewControllers(ViewControllerRegistry registry){
registry.addViewController("/test1").setViewName("test1");
registry.addViewController("/test111").setViewName("test1");
registry.addViewController("/test2").setViewName("test2");
}
}
拦截器
- preHandle:控制器⽅法执⾏之前执⾏preHandle(),其boolean类型的返回值表⽰是否拦截或放⾏,返回true为放⾏,即调⽤控制器⽅法︔返回false表⽰拦截,即不调⽤控制器⽅法;应⽤场景:如⾝份认证,⾝份授权。
- postHandle:控制器⽅法执⾏之后执⾏postHandle(),返回ModelAndView之前执⾏应⽤场景:从modelAndView出发,将公⽤模型数据(如菜单导航)在这⾥传到视图,也可以在这⾥统⼀制定视图。
- afterComplation:处理完视图和模型数据,渲染视图完毕之后执⾏afterComplatio() 应⽤场景:统⼀⽇志处理,统⼀异常处理 preHandle-->控制器⽅法controller->>postHandle-->模型视图⽅法-->afterComplation 同上述例⼦,在config⽂件夹⾥创建MyMvcConfig类和MyInterceptor类。
MyMvcConfig类 :
@Configuration
publicclassMyInterceptorimplementsHandlerInterceptor{
@Override
publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse
response, Object handler)throws Exception {
System.out.println("preHandle");
returntrue;
}
@Override
publicvoidpostHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView)throws Exception {
System.out.println("postHandle");
}
@Override
publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex)throws Exception {
System.out.println("afterCompletion");
}
}
MyInterceptor类:
@Configuration
publicclassMyMvcConfigimplementsWebMvcConfigurer{
@Autowired
private MyInterceptor myInterceptor;
@Override
publicvoidaddInterceptors(InterceptorRegistry registry){
registry.addInterceptor(myInterceptor)
.addPathPatterns("/**");
}
}
访问页⾯查看控制台打印结果。
简单应⽤在之前案例的基础上使⽤拦截器。
⽤户登录:
@Controller
publicclassLoginController{
@GetMapping("/login")
public String login(){
//System.out.println("controller");
return"login";
}
@PostMapping("/login")
public String loginIn(@RequestParam("username") String username,
@RequestParam("password") String password, HttpSession session){
if (username.equals("xxx") && password.equals("123")) {
session.setAttribute("username","xxx");
return"redirect:/admin/article";
}
else
return"login";
}
}
<!DOCTYPE html>
<htmllang="en"xmlns:th="http://www.thymeleaf.org">
<head>
<metacharset="UTF-8">
<title>用户登录界面</title>
<!-- 新 Bootstrap5 核心 CSS 文件 -->
<linkrel="stylesheet"href="https://cdn.staticfile.org/twitter-
bootstrap/5.1.1/css/bootstrap.min.css">
<!-- popper.min.js 用于弹窗、提示、下拉菜单 -->
<scriptsrc="https://cdn.staticfile.org/popper.js/2.9.3/umd/popper.min.js">
</script>
<!-- 最新的 Bootstrap5 核心 JavaScript 文件 -->
<scriptsrc="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/js/bootstrap.min.js"></script>
</head>
<body>
<!-- 用户登录form表单 -->
<formth:action="@{/login}"method="post">
<divclass="mb-3">
<labelfor="username"class="form-label">用户名</label>
<inputtype="text"class="form-control"id="username"name="username">
</div>
<divclass="mb-3">
<labelfor="password"class="form-label">密码</label>
<inputtype="password"class="form-control"id="password"name="password">
</div>
<buttontype="submit"class="btn btn-primary">登录</button>
</form>
<p><spanth:text="${#dates.createNow()}"></span></p>
</body>
</html>
管理⽂章界⾯:
@Controller
@RequestMapping("/admin")
publicclassArticleController{
@Autowired
ArticleService articleService;
@GetMapping("/article")
public String article(Model model){
List<Article> articleList = articleService.findAll();
model.addAttribute("articles", articleList);
return"article";
}
}
<!DOCTYPE html>
<htmllang="en"xmlns:th="http://www.thymeleaf.org">
<head>
<metacharset="UTF-8">
<title>用户登录界面</title>
<!-- 新 Bootstrap5 核心 CSS 文件 -->
<linkrel="stylesheet"href="https://cdn.staticfile.org/twitter-
bootstrap/5.1.1/css/bootstrap.min.css">
<!-- popper.min.js 用于弹窗、提示、下拉菜单 -->
<scriptsrc="https://cdn.staticfile.org/popper.js/2.9.3/umd/popper.min.js">
</script>
<!-- 最新的 Bootstrap5 核心 JavaScript 文件 -->
<scriptsrc="https://cdn.staticfile.org/twitter-
bootstrap/5.1.1/js/bootstrap.min.js"></script>
</head>
<body>
<!-- 用户登录form表单 -->
<formth:action="@{/login}"method="post">
<divclass="mb-3">
<labelfor="username"class="form-label">用户名</label>
<inputtype="text"class="form-control"id="username"name="username">
</div>
<divclass="mb-3">
<labelfor="password"class="form-label">密码</label>
<inputtype="password"class="form-control"id="password"name="password">
</div>
<buttontype="submit"class="btn btn-primary">登录</button>
</form>
<p><spanth:text="${#dates.createNow()}"></span></p>
</body>
</html>
拦截器:
@Configuration
publicclassMyInterceptorimplementsHandlerInterceptor{
@Override
publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse
response, Object handler)throws Exception {
//URI:统一资源标识符,表示Web上每一种可用的资源,如HTML文档,图像,视频片段,程
序等都是由一个URI进行标识的(身份证号)
String uri = request.getRequestURI();
Object loginUser = request.getSession().getAttribute("username");
if (uri.startsWith("/admin") && null == loginUser) {
response.sendRedirect("/login");
returnfalse;
}
//System.out.println("preHandle");
returntrue;
}
@Override
publicvoidpostHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView)throws Exception {
//request.setAttribute("msg","后置拦截器传递");
//System.out.println("postHandle");
}
@Override
publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex)throws Exception {
//System.out.println("afterCompletion");
}
}
@Configuration
publicclassMyMvcConfigimplementsWebMvcConfigurer{
@Autowired
private MyInterceptor myInterceptor;
@Override
publicvoidaddInterceptors(InterceptorRegistry registry){
registry.addInterceptor(myInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}