MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。是将业务逻辑、数据、显示分离的方法来组织代码。MVC主要作用是降低了视图与业务逻辑间的双向偶合。MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。
运行步骤:
1、用户发起恩恩请求URL到达前端控制器
2、前端控制器请求处理器映射器查询Handler
3、处理器映射器返回给前端控制器,返回处理器执行链(HanderExecutionChain)(包含多个处理拦截器和一个Handler实例)
4、前端控制器请求处理器适配器执行Handler。
5、处理器适配器根据适配规则找到特定的处理器(后端controller层URL所绑定的方法),由处理器来执行Handler
6、处理器执行结束后返回给处理器适配器一个ModelAndView对象,该对象包含数据(Model)和逻辑视图名
7、处理器适配器将ModelAndView对象返回给前端控制器
8、前端控制器请求视图解析器解析视图地址,找到真正的视图
9、视图解析器将真正视图对象返回到前端控制器
10、将数据渲染到视图上
11、将渲染的页面响应给请求用户
前端控制器为DispatcherServlet
不需要进行开发
前端控制器是整个用户请求的入口和完成各组件业务转发
所有组件都是直接和前端控制器交互,减少组件间的耦合性
处理器映射为HandlerMapping
不需要进行开发
来存储URL和Handler之间的映射关系,
由前端控制器来判断请求URL是否存在,并返回包含Handler的处理器执行链
处理器适配器为HadlerAdapter
不需要进行开发
按照特定的规则(HandlerAdapter要求的规则)去执行,Handler通过适配器找到真正的执行器,是适配器模式的使用
按照HandlerAdapter的要求开发,Handler是针对用户具体的业务逻辑做响应的处理,Handler涉及到就用户的具体的业务逻辑需要自行开发Handler
视图解析器为ViewResolver
不需要进行开发
解析视图,根据逻辑视图名找到真正的视图,视图解析器负责解析View视图即页面的具体的地址位置,jsp、pdf、freeMark等都能完成解析
视图为View
需要进行开发
View是一个接口,支持不同的View类型(jsp、pdf、freeMark...),例如jsp是提供了一个jstl
视图是展示给用户的页面,不同的业务展示不同的页面
展示用户列表页面(开发的是处理器(获取用户信息)视图列表页面(JSP))
org.springframework
spring-webmvc
4.1.7.RELEASE
org.springframework
spring-web
4.1.7.RELEASE
javax.servlet
javax.servlet-api
3.1.0
在web.xml配置文件下引入前端控制器的配置
springMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
springMVC
/
需要在web.xml配置文件中引入前端控制器的实现类:org.springframework.web.servlet.DispatcherServlet,并将SPringMVC的配置添加在web.xml文件中
springMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
除此之外也有其他的映射方式
springMVC
/
在视图中可以配置访问url的前缀后缀
映射器、处理器等需要给定SPringMVC特定的配置文件,这里新建了spring-mvc.xml
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
数据展示
用户id
用户名
${user.id}
${user.name}
这里用到了标签,有可能两个jstl报错标红,导致foreach没法用,解决办法是手动在pom.xml中添加这两个依赖。
jstl
jstl
1.2
taglibs
standard
1.1.2
RequestMapping注解用于实现jsp与url之间的映射。这里的setViewName如果配置了前缀和后缀,就不用把路径写全了,就只需要写一个userlist
@Controller
public class UserController {
/**
* 执行器
* @return
*/
@RequestMapping("/userlist")
public ModelAndView userList(){
//返回用户列表
ArrayList users = new ArrayList <>();
User u1= new User(1, "tom");
User u2 = new User(2, "jack");
User u3 = new User(3, "rose");
users.add(u1);
users.add(u2 );
users.add(u3 );
//封装ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//封装数据
modelAndView.addObject("users",users);
//封装逻辑视图名
modelAndView.setViewName("/WEB-INF/jsp/userlist.jsp");
return modelAndView;
}
}
在pom.xml中添加jetty的插件
org.mortbay.jetty
maven-jetty-plugin
6.1.24
8080
30000
/
只后配置目录,在命令行加入jetty:run命令完成即可运行,然后就能运行成功了。
我还是习惯用Tomcat,可以去官网下载需要的Tomcat版本。IDEA配置Tomcat可以参考https://www.cnblogs.com/weixinyu98/p/9822048.html
如果Tomcat出现乱码的情况,去tomcat的安装目录下的conf的logging.properties,在最后一行添加下列命令,如我的就是E:\apache-tomcat-7.0.108-windows-x64\apache-tomcat-7.0.108\conf就能解决。
java.util.logging.ConsoleHandler.encoding = GBK
如果啥都配好了还是运行不起来,检查端口占用,要么kell掉tomcat使用的端口,要么就直接给tomcat改个不容易占用的端口号。
运行成功如下:
在浏览器进入到http://localhost:8088/springMVC_war/userlist,显示如下
不知道为啥,我在jetty设置了默认跳转,它死活不跳,tomcat就能跳,至此SpringDemo运行成功。
前端控制器的实现类org.springframework.web.servlet.DispatcherServlet的执行过程:
所有的请求都会请求到doService方法上
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//省略无关代码
try {
doDispatch(request, response);
}
finally {
//
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//mappedHandler是处理器映射器返回的HandlerExecutionChain类型的对象
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//第二步:前端控制器调用处理器查找Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//调用处理器适配器执行Handler,得到执行结果ModelAndViewView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//视图渲染,将数据渲染到视图中
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
springMVC中处理器和适配器的使用可以使用配置和注解方式,配置处理器适配器,springMVC提供了org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter类实现,通过源码查看,该适配器支持的Handler必须是Controller接口的实现类,即需要实现COntroller接口,SimpleControllerHandlerAdapter适配器才能支持执行
package com.tulun.controller;
import com.tulun.controller.bean.User;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
/**
* Description :
* Created by Resumebb
* Date :2021/4/18
*/
public class UserController1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ArrayList users = new ArrayList<>();
User u1 = new User(1,"张三");
User u2 = new User(2,"李四");
User u3 = new User(3,"王五");
users.add(u1);
users.add(u2);
users.add(u3);
//返回ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("users",users);
modelAndView.setViewName("userlist");
return modelAndView;
}
}
基于该配置形式是,一个Controller实现类只能针对特定的一个URL做处理,即一个Handler只能一个类来处理,多个Handler(即针对不同的URL)是需要不同的Controller实现类来处理,且将实现类都要交给容器管理
注意:在基于非注解的处理器中使用的两个框架类:
为了区别userlist.jsp,将handler映射到userlist1.jsp上,jsp内容无异。
网页显示:
基于配置方式的url只能对应一个handler,但是基于注解的可以对应多个url
package com.tulun.controller;
import com.tulun.controller.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
/**
* Description :
* Created by Resumebb
* Date :2021/4/17
*/
@Controller
public class UserController {
@RequestMapping("/test")
public @ResponseBody
String test(){
return "hello Tulun";
}
/**
* 执行器
* @return
*/
@RequestMapping("/userlist")
public ModelAndView userList(){
//返回用户列表
ArrayList users = new ArrayList <>();
User tulun = new User(1, "tom");
User java = new User(2, "jack");
User user = new User(3, "rose");
users.add(tulun);
users.add(java);
users.add(user);
//封装ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//封装数据
modelAndView.addObject("users",users);
//封装逻辑视图名 /WEB-INF/jsp/userlist.jsp
modelAndView.setViewName("userlist");
return modelAndView;
}
@RequestMapping("/userlist1")
public ModelAndView test1(){
//返回用户列表
ArrayList users = new ArrayList <>();
User tulun = new User(1, "张三");
User java = new User(2, "李四");
User user = new User(3, "王五");
users.add(tulun);
users.add(java);
users.add(user);
//封装ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
//封装数据
modelAndView.addObject("users",users);
//封装逻辑视图名 /WEB-INF/jsp/userlist.jsp
modelAndView.setViewName("userlist1");
return modelAndView;
}
}
http://localhost:8088/springMVC_war/test:
http://localhost:8088/springMVC_war/userlist:
http://localhost:8088/springMVC_war/userlist1: