什么是MVC
MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。是将业务逻辑、数据、显示分离的方法来组织代码。MVC主要作用是降低了视图与业务逻辑间的双向偶合。MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异。
- Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
- View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
- Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作。
SpringMVC
运行步骤:
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
视图是展示给用户的页面,不同的业务展示不同的页面
SpringMVC的使用Demo
展示用户列表页面(开发的是处理器(获取用户信息)视图列表页面(JSP))
1.导入依赖
<!--web依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
2.配置前端控制器
在web.xml配置文件下引入前端控制器的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!--前端控制器-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--将springMVC的配置文件进行配置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
需要在web.xml配置文件中引入前端控制器的实现类:org.springframework.web.servlet.DispatcherServlet,并将SPringMVC的配置添加在web.xml文件中
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--将springMVC的配置文件进行配置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
除此之外也有其他的映射方式
<servlet-mapping>
<!--
第一种:*.action或者*.do,访问形式以.action或.do结果的URL由DispatcherServlet进行解析
第二种:/ ,所有访问的URL都有DispatcherServlet进行解析,对于静态文件(css,js)的解析是配置不让DispatcherServlet进行解析
实现RESTFul风格的URL
第三种:/*,使用这个配置,会转发到jsp的页面,DispatcherServlet任然会解析jsp地址,不能根据jsp找到Handler,会抛出异常
-->
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
在视图中可以配置访问url的前缀后缀
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="WEB-INF"></property>
<property name="suffix" value=".jsp"></property>
</bean>
3.配置映射器、处理器等
映射器、处理器等需要给定SPringMVC特定的配置文件,这里新建了spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!--SPringMVC需要扫描注解-->
<context:component-scan base-package="com.tulun.controller"/>
<!--配置处理器映射器、处理器适配器-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
</bean>
</beans>
4.开发视图(UserList.jsp)
<%@ 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" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>数据展示</title>
</head>
<body>
<table align="center" border="1">
<thead>
<tr>
<td>用户id</td>
<td>用户名</td>
</tr>
</thead>
<tbody>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
</tr>
</c:forEach>
</tbody>
</table>
</form>
</body>
</html>
这里用到了标签,有可能两个jstl报错标红,导致foreach没法用,解决办法是手动在pom.xml中添加这两个依赖。
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
5.开发Handler
RequestMapping注解用于实现jsp与url之间的映射。这里的setViewName如果配置了前缀和后缀,就不用把路径写全了,就只需要写一个userlist
@Controller
public class UserController {
/**
* 执行器
* @return
*/
@RequestMapping("/userlist")
public ModelAndView userList(){
//返回用户列表
ArrayList <User> 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;
}
}
6.将服务部署到Jetty容器
在pom.xml中添加jetty的插件
<!--Jetty福服务器插件-->
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.24</version>
<configuration>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>30000</maxIdleTime>
</connector>
</connectors>
<contextPath>/</contextPath>
</configuration>
</plugin>
只后配置目录,在命令行加入jetty:run命令完成即可运行,然后就能运行成功了。
7.将服务部署到Tomcat容器
我还是习惯用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适配器才能支持执行
spring-mvc1.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!--基于配置形式来配置处理器映射器、处理器适配器和视图解析器-->
<!--处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--配置Handler实例,将实例交给容器管理-->
<bean name="/userlist1" class="com.tulun.controller.UserController1"/>
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--jsp页面前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--jsp后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
handler开发
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<User> 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实现类来处理,且将实现类都要交给容器管理
注意:在基于非注解的处理器中使用的两个框架类:
- org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
- org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
为了区别userlist.jsp,将handler映射到userlist1.jsp上,jsp内容无异。
部署结果
网页显示:
基于注解形式处理适配器处理器
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!--<!–配置处理器映射器、处理器适配器–>-->
<!--<mvc:annotation-driven/>-->
<!--处理器映射器在spring3.1之前使用-->
<!--org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping。-->
<!--处理器映射器在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping。-->
<!--处理器适配器在spring3.1之前使用-->
<!--org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter。-->
<!--处理器适配器在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter。-->
<!--处理器映射器-->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>-->
<!--处理器适配器-->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>-->
<!--SPringMVC需要扫描注解-->
<context:component-scan base-package="com.tulun.controller"/>
<!--配置处理器映射器、处理器适配器-->
<mvc:annotation-driven/>
<!--视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--jsp页面前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--jsp后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
handler开发
基于配置方式的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 <User> 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 <User> 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:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)