Spring MVC【入门】就这一篇!
本文内容纲要:
-MVC设计概述
-HelloSpringMVC
-跟踪SpringMVC的请求
-使用注解配置SpringMVC
-配置视图解析器
-控制器接收请求数据
-控制器回显数据
-客户端跳转
-文件上传
MVC设计概述
在早期JavaWeb的开发中,统一把显示层、控制层、数据层的操作全部交给JSP或者JavaBean来进行处理,我们称之为Model1:
- 出现的弊端:
- JSP和JavaBean之间严重耦合,Java代码和HTML代码也耦合在了一起
- 要求开发者不仅要掌握Java,还要有高超的前端水平
- 前端和后端相互依赖,前端需要等待后端完成,后端也依赖前端完成,才能进行有效的测试
- 代码难以复用
正因为上面的种种弊端,所以很快这种方式就被Servlet+JSP+JavaBean所替代了,早期的MVC模型**(Model2)**就像下图这样:
首先用户的请求会到达Servlet,然后根据请求调用相应的JavaBean,并把所有的显示结果交给JSP去完成,这样的模式我们就称为MVC模式。
- M代表模型(Model)
模型是什么呢?模型就是数据,就是dao,bean - V代表视图(View)
视图是什么呢?就是网页,JSP,用来展示模型中的数据 - C代表控制器(controller)
控制器是什么?控制器的作用就是把不同的数据(Model),显示在不同的视图(View)上,Servlet扮演的就是这样的角色。
扩展阅读:Web开发模式
SpringMVC的架构
为解决持久层中一直未处理好的数据库事务的编程,又为了迎合NoSQL的强势崛起,SpringMVC给出了方案:
**传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,DataAccessObject)。**在Service下可以通过Spring的声明式事务操作数据访问层,而在业务层上还允许我们访问NoSQL,这样就能够满足异军突起的NoSQL的使用了,它可以大大提高互联网系统的性能。
- 特点:
结构松散,几乎可以在SpringMVC中使用各类视图
松耦合,各个模块分离
与Spring无缝集成
HelloSpringMVC
让我们来写一下我们的第一个SpringMVC程序:
第一步:在IDEA中新建SpringMVC项目
并且取名为【HelloSpringMVC】,点击【Finish】:
IDEA会自动帮我们下载好必要的jar包,并且为我们创建好一些默认的目录和文件,创建好以后项目结构如下:
第二步:修改web.xml
我们打开web.xml,按照下图完成修改:
把<url-pattern>
元素的值改为/,表示要拦截所有的请求,并交由SpringMVC的后台控制器来处理,改完之后:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
第三步:编辑dispatcher-servlet.xml
这个文件名的开头dispatcher与上面web.xml中的<servlet-name>
元素配置的dispatcher对应,这是SpringMVC的映射配置文件(xxx-servlet.xml),我们编辑如下:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd">
<beanid="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<propertyname="mappings">
<props>
<!--/hello路径的请求交给id为helloController的控制器处理-->
<propkey="/hello">helloController</prop>
</props>
</property>
</bean>
<beanid="helloController"class="controller.HelloController"></bean>
</beans>
第四步:编写HelloController
在Package【controller】下创建【HelloController】类,并实现org.springframework.web.servlet.mvc.Controller接口:
packagecontroller;
importorg.springframework.web.servlet.ModelAndView;
importorg.springframework.web.servlet.mvc.Controller;
publicclassHelloControllerimplementsController{
@Override
publicModelAndViewhandleRequest(javax.servlet.http.HttpServletRequesthttpServletRequest,javax.servlet.http.HttpServletResponsehttpServletResponse)throwsException{
returnnull;
}
}
- 出现了问题:javax.servlet包找不到
- 解决:将本地Tomcat服务器的目录下【lib】文件夹下的servlet-api.jar包拷贝到工程【lib】文件夹下,添加依赖
SpringMVC通过ModelAndView对象把模型和视图结合在一起
ModelAndViewmav=newModelAndView("index.jsp");
mav.addObject("message","HelloSpringMVC");
这里表示视图的是index.jsp
模型数据的是message,内容是“HelloSpringMVC”
packagecontroller;
importorg.springframework.web.servlet.ModelAndView;
importorg.springframework.web.servlet.mvc.Controller;
publicclassHelloControllerimplementsController{
publicModelAndViewhandleRequest(javax.servlet.http.HttpServletRequesthttpServletRequest,javax.servlet.http.HttpServletResponsehttpServletResponse)throwsException{
ModelAndViewmav=newModelAndView("index.jsp");
mav.addObject("message","HelloSpringMVC");
returnmav;
}
}
第五步:准备index.jsp
将index.jsp的内容修改为:
<%@pagelanguage="java"contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"isELIgnored="false"%>
<h1>${message}</h1>
内容很简单,用El表达式显示message的内容。
第六步:部署Tomcat及相关环境
在【Run】菜单项下找到【EditConfigurations】
配置Tomcat环境:
选择好本地的Tomcat服务器,并改好名字:
在Deployment标签页下完成如下操作:
点击OK就好了,我们点击右上角的三角形将Tomcat服务器运行起来。
- 出现的问题:Tomcat服务器无法正常启动
- 原因:Tomcat服务器找不到相关的jar包
- 解决方法:将【lib】文件夹整个剪贴到【WEB-INF】下,并重新建立依赖:
第七步:重启服务器
重启服务器,输入地址:localhost/hello
参考资料:SpringMVC教程(how2j.cn)
跟踪SpringMVC的请求
每当用户在Web浏览器中点击链接或者提交表单的时候,请求就开始工作了,像是邮递员一样,从离开浏览器开始到获取响应返回,它会经历很多站点,在每一个站点都会留下一些信息同时也会带上其他信息,下图为SpringMVC的请求流程:
第一站:DispatcherServlet
从请求离开浏览器以后,第一站到达的就是DispatcherServlet,看名字这是一个Servlet,通过J2EE的学习,我们知道Servlet可以拦截并处理HTTP请求,DispatcherServlet会拦截所有的请求,并且将这些请求发送给SpringMVC控制器。
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!--拦截所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
- DispatcherServlet的任务就是拦截请求发送给SpringMVC控制器。
第二站:处理器映射(HandlerMapping)
- **问题:**典型的应用程序中可能会有多个控制器,这些请求到底应该发给哪一个控制器呢?
所以DispatcherServlet会查询一个或多个处理器映射来确定请求的下一站在哪里,处理器映射会根据请求所携带的URL信息来进行决策,例如上面的例子中,我们通过配置simpleUrlHandlerMapping来将/hello地址交给helloController处理:
<beanid="simpleUrlHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<propertyname="mappings">
<props>
<!--/hello路径的请求交给id为helloController的控制器处理-->
<propkey="/hello">helloController</prop>
</props>
</property>
</bean>
<beanid="helloController"class="controller.HelloController"></bean>
第三站:控制器
一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器,到了控制器,请求会卸下其负载(用户提交的请求)等待控制器处理完这些信息:
publicModelAndViewhandleRequest(javax.servlet.http.HttpServletRequesthttpServletRequest,javax.servlet.http.HttpServletResponsehttpServletResponse)throwsException{
//处理逻辑
....
}
第四站:返回DispatcherServlet
当控制器在完成逻辑处理后,通常会产生一些信息,这些信息就是需要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。仅仅返回原始的信息时不够的——这些信息需要以用户友好的方式进行格式化,一般会是HTML,所以,信息需要发送给一个视图(view),通常会是JSP。
控制器所做的最后一件事就是将模型数据打包,并且表示出用于渲染输出的视图名**(逻辑视图名)。它接下来会将请求连同模型和视图名发送回DispatcherServlet。**
publicModelAndViewhandleRequest(javax.servlet.http.HttpServletRequesthttpServletRequest,javax.servlet.http.HttpServletResponsehttpServletResponse)throwsException{
//处理逻辑
....
//返回给DispatcherServlet
returnmav;
}
第五站:视图解析器
这样以来,控制器就不会和特定的视图相耦合,传递给DispatcherServlet的视图名并不直接表示某个特定的JSP。(实际上,它甚至不能确定视图就是JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图。
DispatcherServlet将会使用视图解析器(viewresolver)来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是JSP
上面的例子是直接绑定到了index.jsp视图
第六站:视图
既然DispatcherServlet已经知道由哪个视图渲染结果了,那请求的任务基本上也就完成了。
它的最后一站是视图的实现,在这里它交付模型数据,请求的任务也就完成了。视图使用模型数据渲染出结果,这个输出结果会通过响应对象传递给客户端。
<%@pagelanguage="java"contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"isELIgnored="false"%>
<h1>${message}</h1>
使用注解配置SpringMVC
上面我们已经对SpringMVC有了一定的了解,并且通过XML配置的方式创建了第一个SpringMVC程序,我们来看看基于注解应该怎么完成上述程序的配置:
第一步:为HelloController添加注解
packagecontroller;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.servlet.ModelAndView;
@Controller
publicclassHelloController{
@RequestMapping("/hello")
publicModelAndViewhandleRequest(javax.servlet.http.HttpServletRequesthttpServletRequest,javax.servlet.http.HttpServletResponsehttpServletResponse)throwsException{
ModelAndViewmav=newModelAndView("index.jsp");
mav.addObject("message","HelloSpringMVC");
returnmav;
}
}
把实现的接口也给去掉。
- 简单解释一下:
@Controller
注解:
很明显,这个注解是用来声明控制器的,但实际上这个注解对SpringMVC本身的影响并不大。(Spring实战说它仅仅是辅助实现组件扫描,可以用@Component
注解代替,但我自己尝试了一下并不行,因为上述例子没有配置JSP视图解析器我还自己配了一个仍没有成功...)@RequestMapping
注解:
很显然,这就表示路径/hello
会映射到该方法上
第二步:取消之前的XML注释
在dispatcher-servlet.xml文件中,注释掉之前的配置,然后增加一句组件扫描:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
<!--<beanid="simpleUrlHandlerMapping"-->
<!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">-->
<!--<propertyname="mappings">-->
<!--<props>-->
<!--<!–/hello路径的请求交给id为helloController的控制器处理–>-->
<!--<propkey="/hello">helloController</prop>-->
<!--</props>-->
<!--</property>-->
<!--</bean>-->
<!--<beanid="helloController"class="controller.HelloController"></bean>-->
<!--扫描controller下的组件-->
<context:component-scanbase-package="controller"/>
</beans>
第三步:重启服务器
当配置完成,重新启动服务器,输入localhost/hello
地址仍然能看到效果:
@RequestMapping注解细节
如果@RequestMapping
作用在类上,那么就相当于是给该类所有配置的映射地址前加上了一个地址,例如:
@Controller
@RequestMapping("/wmyskxz")
publicclassHelloController{
@RequestMapping("/hello")
publicModelAndViewhandleRequest(....)throwsException{
....
}
}
- 则访问地址:
localhost/wmyskxz/hello
配置视图解析器
还记得我们SpringMVC的请求流程吗,视图解析器负责定位视图,它接受一个由DispaterServlet传递过来的逻辑视图名来匹配一个特定的视图。
- 需求:有一些页面我们不希望用户用户直接访问到,例如有重要数据的页面,例如有模型数据支撑的页面。
- 造成的问题:
我们可以在【web】根目录下放置一个【test.jsp】模拟一个重要数据的页面,我们什么都不用做,重新启动服务器,网页中输入localhost/test.jsp
就能够直接访问到了,这会造成数据泄露...
另外我们可以直接输入localhost/index.jsp
试试,根据我们上面的程序,这会是一个空白的页面,因为并没有获取到${message}
参数就直接访问了,这会影响用户体验
解决方案
我们将我们的JSP文件配置在【WEB-INF】文件夹中的【page】文件夹下,【WEB-INF】是JavaWeb中默认的安全目录,是不允许用户直接访问的*(也就是你说你通过localhost/WEB-INF/
这样的方式是永远访问不到的)*
但是我们需要将这告诉给视图解析器,我们在dispatcher-servlet.xml文件中做如下配置:
<beanid="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="prefix"value="/WEB-INF/page/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
这里配置了一个SpringMVC内置的一个视图解析器,该解析器是遵循着一种约定:会**在视图名上添加前缀和后缀,进而确定一个Web应用中视图资源的物理路径的。**让我们实际来看看效果:
第一步:修改HelloController
我们将代码修改一下:
第二步:配置视图解析器:
按照上述的配置,完成:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd">
<!--<beanid="simpleUrlHandlerMapping"-->
<!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">-->
<!--<propertyname="mappings">-->
<!--<props>-->
<!--<!–/hello路径的请求交给id为helloController的控制器处理–>-->
<!--<propkey="/hello">helloController</prop>-->
<!--</props>-->
<!--</property>-->
<!--</bean>-->
<!--<beanid="helloController"class="controller.HelloController"></bean>-->
<!--扫描controller下的组件-->
<context:component-scanbase-package="controller"/>
<beanid="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="prefix"value="/WEB-INF/page/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
</beans>
第三步:剪贴index.jsp文件
在【WEB-INF】文件夹下新建一个【page】文件夹,并将【index.jsp】文件剪贴到里面:
第四步:更新资源重启服务器
访问localhost/hello
路径,看到正确效果:
- 原理:
我们传入的逻辑视图名为index,再加上“/WEB-INF/page/
”前缀和“.jsp
”后缀,就能确定物理视图的路径了,这样我们以后就可以将所有的视图放入【page】文件夹下了!
- **注意:**此时的配置仅是dispatcher-servlet.xml下的
控制器接收请求数据
使用控制器接收参数往往是SpringMVC开发业务逻辑的第一步,为探索SpringMVC的传参方式,为此我们先来创建一个简单的表单用于提交数据:
<!DOCTYPEhtml>
<%@pagelanguage="java"contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"import="java.util.*"isELIgnored="false"%>
<html>
<head>
<metacharset="utf-8">
<title>SpringMVC传参方式</title>
</head>
<body>
<formaction="/param"role="form">
用户名:<inputtype="text"name="userName"><br/>
密码:<inputtype="text"name="password"><br/>
<inputtype="submit"value="提交">
</form>
</body>
</html>
丑就丑点儿吧,我们就是来测试一下:
使用Servlet原生API实现:
我们很容易知道,表单会提交到/param
这个目录,我们先来使用Servlet原生的API来看看能不能获取到数据:
@RequestMapping("/param")
publicModelAndViewgetParam(HttpServletRequestrequest,
HttpServletResponseresponse){
StringuserName=request.getParameter("userName");
Stringpassword=request.getParameter("password");
System.out.println(userName);
System.out.println(password);
returnnull;
}
测试成功:
使用同名匹配规则
我们可以把方法定义的形参名字设置成和前台传入参数名一样的方法,来获取到数据(同名匹配规则):
@RequestMapping("/param")
publicModelAndViewgetParam(StringuserName,
Stringpassword){
System.out.println(userName);
System.out.println(password);
returnnull;
}
测试成功:
- 问题:这样又会和前台产生很强的耦合,这是我们不希望的
- 解决:使用
@RequestParam("前台参数名")
来注入:
@RequestParam
注解细节:
该注解有三个变量:value
、required
、defaultvalue
value
:指定name
属性的名称是什么,value
属性都可以默认不写required
:是否必须要有该参数,可以设置为【true】或者【false】defaultvalue
:设置默认值
使用模型传参
- 要求:前台参数名字必须和模型中的字段名一样
让我们先来为我们的表单创建一个User模型:
packagepojo;
publicclassUser{
StringuserName;
Stringpassword;
/*getterandsetter*/
}
然后测试仍然成功:
中文乱码问题
- 注意:跟Servlet中的一样,该方法只对POST方法有效(因为是直接处理的request)
我们可以通过配置SpringMVC字符编码过滤器来完成,在web.xml中添加:
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
控制器回显数据
通过上面,我们知道了怎么接受请求数据,并能解决POST乱码的问题,那么我们怎么回显数据呢?为此我们在【page】下创建一个【test2.jsp】:
<!DOCTYPEhtml>
<%@pagelanguage="java"contentType="text/html;charset=UTF-8"
pageEncoding="UTF-8"import="java.util.*"isELIgnored="false"%>
<html>
<head>
<title>SpringMVC数据回显</title>
</head>
<body>
<h1>回显数据:${message}</h1>
</body>
</html>
使用Servlet原生API来实现
我们先来测试一下Servlet原生的API是否能完成这个任务:
@RequestMapping("/value")
publicModelAndViewhandleRequest(HttpServletRequestrequest,
HttpServletResponseresponse){
request.setAttribute("message","成功!");
returnnewModelAndView("test1");
}
在浏览器地址栏中输入:localhost/value
测试
使用SpringMVC所提供的ModelAndView对象
使用Model对象
在SpringMVC中,我们通常都是使用这样的方式来绑定数据,
-
使用
@ModelAttribute
注解:@ModelAttribute publicvoidmodel(Modelmodel){ model.addAttribute("message","注解成功"); }
@RequestMapping("/value") publicStringhandleRequest(){ return"test1"; }
这样写就会在访问控制器方法handleRequest()时,会首先调用model()方法将message
添加进页面参数中去,在视图中可以直接调用,但是这样写会导致该控制器所有的方法都会首先调用model()方法,但同样的也很方便,因为可以加入各种各样的数据。
客户端跳转
前面不管是地址/hello
跳转到index.jsp还是/test
跳转到test.jsp,这些都是服务端的跳转,也就是request.getRequestDispatcher("地址").forward(request,response);
那我们如何进行客户端跳转呢?我们继续在HelloController中编写:
@RequestMapping("/hello")
publicModelAndViewhandleRequest(javax.servlet.http.HttpServletRequesthttpServletRequest,javax.servlet.http.HttpServletResponsehttpServletResponse)throwsException{
ModelAndViewmav=newModelAndView("index");
mav.addObject("message","HelloSpringMVC");
returnmav;
}
@RequestMapping("/jump")
publicModelAndViewjump(){
ModelAndViewmav=newModelAndView("redirect:/hello");
returnmav;
}
我们使用redirect:/hello
就表示我们要跳转到/hello
这个路径,我们重启服务器,在地址栏中输入:localhost/jump
,会自动跳转到/hello
路径下:
也可以这样用:
@RequestMapping("/jump")
publicStringjump(){
return"redirect:./hello";
}
文件上传
我们先来回顾一下传统的文件上传和下载:这里
我们再来看一下在SpringMVC中如何实现文件的上传和下载
- 注意:需要先导入
commons-io-1.3.2.jar
和commons-fileupload-1.2.1.jar
两个包
第一步:配置上传解析器
在dispatcher-servlet.xml中新增一句:
<beanid="multipartResolver"class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
开启对上传功能的支持
第二步:编写JSP
文件名为upload.jsp,仍创建在【page】下:
<%@pagecontentType="text/html;charset=UTF-8"language="java"%>
<html>
<head>
<title>测试文件上传</title>
</head>
<body>
<formaction="/upload"method="post"enctype="multipart/form-data">
<inputtype="file"name="picture">
<inputtype="submit"value="上传">
</form>
</body>
</html>
第三步:编写控制器
在Package【controller】下新建【UploadController】类:
packagecontroller;
importorg.springframework.stereotype.Controller;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RequestParam;
importorg.springframework.web.multipart.MultipartFile;
importorg.springframework.web.servlet.ModelAndView;
@Controller
publicclassUploadController{
@RequestMapping("/upload")
publicvoidupload(@RequestParam("picture")MultipartFilepicture)throwsException{
System.out.println(picture.getOriginalFilename());
}
@RequestMapping("/test2")
publicModelAndViewupload(){
returnnewModelAndView("upload");
}
}
第四步:测试
在浏览器地址栏中输入:localhost/test2
,选择文件点击上传,测试成功:
参考资料:
- 《JavaEE互联网轻量级框架整合开发》
- 《Spring实战》
- How2jSpringMVC系列教程
- 全能的百度和万能的大脑
欢迎转载,转载请注明出处!
简书ID:@我没有三颗心脏
github:wmyskxz
欢迎关注公众微信号:wmyskxz_javaweb
分享自己的JavaWeb学习之路以及各种Java学习资料
本文内容总结:MVC设计概述,HelloSpringMVC,跟踪SpringMVC的请求,使用注解配置SpringMVC,配置视图解析器,控制器接收请求数据,控制器回显数据,客户端跳转,文件上传,
原文链接:https://www.cnblogs.com/wmyskxz/p/8848461.html