SpringMVC学习篇
Posted SSS4362
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMVC学习篇相关的知识,希望对你有一定的参考价值。
1 MVC解释
M---model 模型,即数据,就是dao和bean
V----view 视图 网页 jsp 用来展示数据的
c-controller 控制器 不同的数据,展示在不同的页面上
2 springmvc的核心架构
1.1 图例展示
1.2 理解
springmvc的核心说白了就是servlet,然后把之前mvc架构中的model层拆分成了业务层(Service)和数据访问层(Dao,Data Access Object)
springmvc是spring的一个模块
3 spring mvc请求的流程图
4 springmvc实现后台数据的获取以及页面跳转
4.1 准备工作
4.1.1 pom.xml中导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.20</version>
</dependency>
4.1.2 pom.xml中设置打包方式为web工程
a 代码展示
<packaging>war</packaging>
b 使用说明
<!--设置打包方式,不设置的话默认就是java工程
有以下三个参数
jar 默认的 表示打包的是一个java工程
war web工程
pom 父工程
-->
4.1.3 在main文件夹下创建前端文件夹webapp文件夹
a 创建出来之后的效果图
b 补充
若没有出现蓝色小圆点,你需要检查以下两点
1 pom.xml是否配置了打包方式为war
2 pom.xml内容变动后是否点击了右侧刷新小图标
4.1.4 生成web.xml
a file-------Project Structure
b facets---->web(mvc)----->加号---->点击1.web.xml
c 点击ok按钮
d 再次点击ok按钮
e 点击ok按钮后 此时项目的结构如下所示
f 把WEB-INF 拖动到webapp下面
f.1 点击Refactor按钮
f.2 移动后项目结构图
4.2 创建springmvc的配置文件
4.2.1 resources—>new---->xml Configuration------>Spring Config
4.2.2 输入名称然后回车
4.2.3 创建成功时的项目结构图
4.3 在web.xml中配置mvc核心servlet
4.3.1 代码展示
<!--编码过滤器-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--初始化参数-->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置mvc控制器-->
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--classpath是类路径 resource路径下的 不能用-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/springMvc.xml</param-value>
</init-param>
<!--随容器启动加载 越小越先启动-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!--/不拦截jsp /*拦截所有jsp页面-->
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
4.3.2 代码分析
a servlet-name标签里面的内容任意取,只是要求Servlet标签和Servlet-mapping标签里面的值要保持一致
b servlet-class里面可以写dispatcherServlet,然后回车后就会自动补全的
c url-pattern里面只能是/,一定不能/*.因为想要jsp页面正常展示
d /*和/的区别
简单来说就是/* 会拦截所有资源(包含jsp页面)
会拦截除了jsp页面之外的所有资源,例如:html,css,静态资源
这时图片资源可能会出现404错误,这时就需要我们去进行放行的配置了
f 服务器默认是第一次被调用时的初始化,但是我们想springmvc在服务器一启动的时候就初始化,
因此就需要做如下配置
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/springmvc.xml</param-value>
</init-param>
其中param-name是固定写contextConfigLocation
param-value里面由两部分组成
第一部分是根据主配置文件的位置来的,因为笔者创建的主配置文件位于resource资源文件夹,隶属于src/main下面,
就是在src文件夹下的都要加classpath:,因此第一部分写的内容就是classpath:
第二部分是你所创建主配置文件(spring Config形式的)
h filter的配置是用于解决post请求乱码的问题
4.4 创建一个index.jsp页面,并在里面写一个简单的登录界面(表单提交)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="" method="get">
用户名:<input type="text" name="yhm">
密码:<input type="password" name="pwd">
<button>登录</button>
</form>
</body>
</html>
4.5 在java文件夹下创建com/controller文件夹
4.6 配置springMvc.xml
4.6.0 在springMvc.xml中配置组件扫描
<context:component-scan base-package="com.controller"></context:component-scan>
4.6.1 在springMvc.xml中开启mvc注解模式
<mvc:annotation-driven/>
4.6.2 在springMvc.xml中进行视图解析器的配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--使用注解,注入修改前缀和后缀-->
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
4.7 在controller文件夹下面创建UserController类
package com.controller;
import org.springframework.stereotype.Controller;
/**
* Controller是语义化的,用于控制器组件的生成
*/
@Controller
public class UserController
4.8 书写UserController类里面的具体内容
4.8.1 在UserController里面创建login1方法
a 代码展示
/*形参就是name属性*/
public String login1(String yhm, String pwd)
System.out.println("用户名:"+yhm);
System.out.println("密码:"+pwd);
/*跳到什么页面 就写什么名字 不要带上拓展名*/
/* return "jsp/b"*/
return "index";
b 代码分析
b.1 里面的形参名需要与form表单里面的name属性要保证一致的
b.2 因为暂时没有前后端分离,此时方法的返回值可以用String
b.3 方法的返回值直接填需要跳转的jsp页面名称,不需要写扩展名,如果jsp页面在文件夹里头,那就
4.8.2 在UserController里面配置请求映射
a 代码展示
@RequestMapping(value = "/login1",method = RequestMethod.POST,RequestMethod.GET,
params = "yhm","pwd",
)
b 代码分析
b.1 value是配置访问该方法的地址,不必与方法名一致,访问的链接必须与其一致,不然就会访问错误
b.2 method是请求方式,多个请求可以用装,之中用,分隔
b.3 params是要求访问该地址必须要携带的两个参数,也是通过,进行分隔
4.8.3 完整的UserController类
package com.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class UserController
@RequestMapping(value = "/login2",method = RequestMethod.POST,RequestMethod.GET,
params = "yhm","pwd"
)
/*形参就是name属性*/
public String login1(String yhm, String pwd)
System.out.println("用户名:"+yhm);
System.out.println("密码:"+pwd);
/*跳到什么页面 就写什么名字 不要带上拓展名*/
/* return "jsp/b"*/
return "index";
4.8.4 在index.jsp界面给form表单的action属性加上配好的value值
<form action="/login1" method="get">
用户名:<input type="text" name="yhm">
密码:<input type="password" name="pwd">
<button>登录</button>
</form>
4.9 测试
4.9.1 点击登录按钮前
4.9.2 点击登录按钮后
4.9.3 运行截图
学习SpringMVC——说说视图解析器
各位前排的,后排的,都不要走,咱趁热打铁,就这一股劲我们今天来说说spring mvc的视图解析器(不要抢,都有位子~~~)
相信大家在昨天那篇如何获取请求参数篇中都已经领略到了spring mvc注解的魅力和套路了。搭上@RequestMapping的便车,我们可以去到我们想去的地方(方法)去,借助@RequestParam、@PathVariable等我们可以得到请求中想要的参数值,最终还能够通过神奇的“return SUCCESS”到达我们的目的地。今天主要就来说说在达到目的地的路上,我们都经历了些什么!
在此之前
我们顺便说说@RequestHeader、请求参数类型为POJO(也就是Java对象类型)的情况以及ModelAndView
1. @RequestHeader
这个无需多说,还是原来的配方,还是一样的套路,只要举个例子,你就都明白了。
在SpringMVCTest中添加测试方法
@RequestMapping(value="/testRequestHeader") public String testRequestHeader(@RequestHeader(value="Accept-Language") String language){ System.out.println("testRequestHeader Accept-Languge:" + language); return SUCCESS; }
我们知道一个请求如get请求或post都有请求头和响应头,这里我们想获取的是请求头中“Accept-Language”的具体信息,所以就用上了@RequestHeader注解来获取。
index.jsp中
<a href="springmvc/testRequestHeader">testRequestHeader</a><br/><br/>
启动服务器,点击超链接,我们得到了
testRequestHeader Accept-Languge:zh-CN
2. 请求参数为POJO
前面两篇,我们看到的请求类型都是一些字符串也就是某一个字段。那么如果现在有一个form表单,说夸张点,表单中有10个字段需要提交,行吧,还用原来的匹配的方式,你要用10个参数来接收,累不累?累!有没有办法?有!我们可以把这些要提交的字段封装在一个对象中,从而请求类型就是一个POJO。
这里我们新建一个类User
package com.jackie.springmvc.entities; public class User { private Integer id; private String username; private String password; private String email; private int age; private Address address; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public User(String username, String password, String email, int age) { super(); this.username = username; this.password = password; this.email = email; this.age = age; } public User(Integer id, String username, String password, String email, int age) { super(); this.id = id; this.username = username; this.password = password; this.email = email; this.age = age; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + ", age=" + age + "]"; } public User() { } }
还有一个Address类
package com.jackie.springmvc.entities; public class Address { private String province; private String city; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Address [province=" + province + ", city=" + city + "]"; } }
同时我们还需要在SpringMVCTest中写一个testPojo的测试方法
@RequestMapping(value="/testPojo") public String testPojo(User user){ System.out.println("testPojo: " + user); return SUCCESS; }
好了,这样,我们就可以在前台jsp页面上构造这样的表单数据了
<form action="springmvc/testPojo" method="post"> username: <input type="text" name="username"><br> password: <input type="password" name="password"><br> email: <input type="text" name="email"><br> age: <input type="text" name="age"><br> city: <input type="text" name="address.city"><br> province: <input type="text" name="address.province"><br> <input type="submit" value="submit"> </form><br/><br/>
至此,我们启动tomcat服务器,就可以发送一个POJO类型的参数了,并且我们成功了读取了这个请求参数
3. ModelAndView
ModelAndView是什么鬼?其实它是我们经常写在SpringMVCTest里测试方法的返回值类型,在方法体内我们可以通过ModelAndView对象来是像请求域中添加模型数据的,抽象?那就看例子吧~~~
SpringMVCTest中添加方法
@RequestMapping(value="/testModelAndView") public ModelAndView testModelAndView(){ String viewname = SUCCESS; ModelAndView modelAndView = new ModelAndView(viewname); modelAndView.addObject("time", new Date()); return modelAndView; }
index.jsp中还是添加一个超链接
<a href="springmvc/testModelAndView">testModelAndView</a><br/><br/>
注意我们需要在结果页面中拿到这个放入请求域中的键值对,所以在success.jsp页面中添加
time: ${requestScope.time}<br><br>
最终的效果图是这样的
没错,我们将当前时间信息写进了请求域,并通过视图展示出来。
有了前面的小铺垫,现在我们来唠唠这视图解析器的事儿
视图解析器
这里主要通过调试源代码看看spring mvc的handler是如何利用视图解析器找到并返回实际的物理视图的,别眨眼
1. 如何看源码
说到调试源码,我们就要有源码才行,那么如何看源码,相信这个页面大家已经看腻了吧
没错,这是因为你没有导入源码的jar包,程序没办法给你呈现源代码,还好,这个问题难不倒我们,在第一篇中我们有关于springframework所需要的功能jar包,javadoc以及源码包,那么来导入一波
选中前面提示的spring-context的source jar包,我们就可以一睹这个java文件的庐山真面目了
484很开心~~~
2. 代码调试
为此我们写一个测试方法
@RequestMapping("/testViewAndViewResolver") public String testViewAndViewResolver(){ System.out.println("testViewAndViewResolver"); return SUCCESS; }
index.jsp加个链接
<a href="springmvc/testViewAndViewResolver">testViewAndViewResolver</a><br/><br/>
给testViewAndView方法体一个断点,我们进入调试状态,
程序停在断点处,在调试的上下文中,我们找到DispatcherServlet.doDispaatch方法,以此为入口,来看看视图解析器
(1) 进入DispatcherServlet.doDispaatch
定位到
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
可以看到这里有个mv对象,实际上就是ModelAndView,通过调试我们发现这里的mv中包括了model和view,view的指向就是success,而model这里之所以有值是因为在SpringMVCTest中有一个getUser方法,且加上了@ModelAttribute注解,从而初始化了model。
(2)执行processDispatchResult方法
在doDispatch中继续执行,直到
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
进入该方法进行视图渲染
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name \'" + getServletName() + "\': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
这里我们着重看下render方法,然后得到视图的名字,即运行到view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);进入到该方法后,我们可以看到整个方法如下:
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; }
这里用到了视图解析器即this.viewResolvers。而真正的渲染视图在DispatcherServlet的view.render(mv.getModelInternal(), request, response);点击进入这里的render方法,我们选择AbstractView这个抽象类中的该方法
/** * Prepares the view given the specified model, merging it with static * attributes and a RequestContext attribute, if necessary. * Delegates to renderMergedOutputModel for the actual rendering. * @see #renderMergedOutputModel */ @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isTraceEnabled()) { logger.trace("Rendering view with name \'" + this.beanName + "\' with model " + model + " and static attributes " + this.staticAttributes); } Map<String, Object> mergedModel = createMergedOutputModel(model, request, response); prepareResponse(request, response); renderMergedOutputModel(mergedModel, request, response); }
该方法负责针对具体的Model呈现具体的view,这时候再进入到renderMergedOutputMode的具体实现类
点击后,我们发现对此方法多个类都有实现,那么到底是哪个呢,实际上是InternalResourceView这个类,为什么定位到这个类,笔者是根据之前在springmvc.xml中配置的视图解析器的线索找到的,当时我们配的是InternalResourceViewResolver这个解析器,所以相应的,这里应该是InternalResourceView类,同时通过加断点,更加验证了这一想法~~~
此外在调试DispatcherServlet的resolveViewName方法时,发现,这里的viewResolver正是我们配置的视图解析器InternalResourceViewResolver
同时发现这里返回的view就是/WEB-INF/views/success.jsp
至此,我们就完成了ModelAndView的逻辑路径向这里"/WEB-INF/views/success.jsp"的物理路径的转化,大致了了解了视图解析器的工作机制(感觉还是没有说清楚--!)。
好了,本篇我们主要学习了
- @Request的用法
- 请求参数为POJO的用法
- ModelAndView的用法
- 如何看源代码
- spring mvc如何通过视图解析器得到真正的物理视图页面
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。(点赞不迷路,博主带你上高速~~~)
以上是关于SpringMVC学习篇的主要内容,如果未能解决你的问题,请参考以下文章