在早期 Java Web 的开发中,统一把显示层、控制层、数据层的操做所有交给 JSP 或者 JavaBean 来进行处理,咱们称之为 Model1:javascript
正由于上面的种种弊端,因此很快这种方式就被 Servlet + JSP + Java Bean 所替代了,早期的 MVC 模型(Model2)就像下图这样:php
首先用户的请求会到达 Servlet,而后根据请求调用相应的 Java Bean,并把全部的显示结果交给 JSP 去完成,这样的模式咱们就称为 MVC 模式。html
扩展阅读:Web开发模式前端
为解决持久层中一直未处理好的数据库事务的编程,又为了迎合 NoSQL 的强势崛起,Spring MVC 给出了方案:java
传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。在 Service 下能够经过 Spring 的声明式事务操做数据访问层,而在业务层上还容许咱们访问 NoSQL ,这样就可以知足异军突起的 NoSQL 的使用了,它能够大大提升互联网系统的性能。web
让咱们来写一下咱们的第一个 Spring MVC 程序:spring
而且取名为 【HelloSpringMVC】,点击【Finish】:数据库
IDEA 会自动帮咱们下载好必要的 jar 包,而且为咱们建立好一些默认的目录和文件,建立好之后项目结构以下:编程
咱们打开 web.xml ,按照下图完成修改:后端
把<url-pattern>
元素的值改成 / ,表示要拦截全部的请求,并交由Spring MVC的后台控制器来处理,改完以后:
<servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
这个文件名的开头 dispatcher 与上面 web.xml 中的 <servlet-name>
元素配置的 dispatcher 对应,这是 Spring MVC 的映射配置文件(xxx-servlet.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <!-- /hello 路径的请求交给 id 为 helloController 的控制器处理--> <prop key="/hello">helloController</prop> </props> </property> </bean> <bean id="helloController" class="controller.HelloController"></bean> </beans>
在 Package【controller】下建立 【HelloController】类,并实现 org.springframework.web.servlet.mvc.Controller 接口:
package controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class HelloController implements Controller{ @Override public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { return null; } }
Spring MVC 经过 ModelAndView 对象把模型和视图结合在一块儿
ModelAndView mav = new ModelAndView("index.jsp"); mav.addObject("message", "Hello Spring MVC");
这里表示视图的是index.jsp
模型数据的是 message,内容是 “Hello Spring MVC”
package controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class HelloController implements Controller { public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { ModelAndView mav = new ModelAndView("index.jsp"); mav.addObject("message", "Hello Spring MVC"); return mav; } }
将 index.jsp 的内容修改成:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <h1>${message}</h1>
内容很简单,用El表达式显示 message 的内容。
在【Run】菜单项下找到【Edit Configurations】
配置 Tomcat 环境:
选择好本地的 Tomcat 服务器,并改好名字:
在 Deployment 标签页下完成以下操做:
点击 OK 就行了,咱们点击右上角的三角形将 Tomcat 服务器运行起来。
重启服务器,输入地址:localhost/hello
每当用户在 Web 浏览器中点击连接或者提交表单的时候,请求就开始工做了,像是邮递员同样,从离开浏览器开始到获取响应返回,它会经历不少站点,在每个站点都会留下一些信息同时也会带上其余信息,下图为 Spring MVC 的请求流程:
从请求离开浏览器之后,第一站到达的就是 DispatcherServlet,看名字这是一个 Servlet,经过 J2EE 的学习,咱们知道 Servlet 能够拦截并处理 HTTP 请求,DispatcherServlet 会拦截全部的请求,而且将这些请求发送给 Spring MVC 控制器。
<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 会查询一个或多个处理器映射来肯定请求的下一站在哪里,处理器映射会根据请求所携带的 URL 信息来进行决策,例如上面的例子中,咱们经过配置 simpleUrlHandlerMapping 来将 /hello 地址交给 helloController 处理:
<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <!-- /hello 路径的请求交给 id 为 helloController 的控制器处理--> <prop key="/hello">helloController</prop> </props> </property> </bean> <bean id="helloController" class="controller.HelloController"></bean>
一旦选择了合适的控制器, DispatcherServlet 会将请求发送给选中的控制器,到了控制器,请求会卸下其负载(用户提交的请求)等待控制器处理完这些信息:
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { // 处理逻辑 .... }
当控制器在完成逻辑处理后,一般会产生一些信息,这些信息就是须要返回给用户并在浏览器上显示的信息,它们被称为模型(Model)。仅仅返回原始的信息时不够的——这些信息须要以用户友好的方式进行格式化,通常会是 HTML,因此,信息须要发送给一个视图(view),一般会是 JSP。
控制器所作的最后一件事就是将模型数据打包,而且表示出用于渲染输出的视图名(逻辑视图名)。它接下来会将请求连同模型和视图名发送回 DispatcherServlet。
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { // 处理逻辑 .... // 返回给 DispatcherServlet return mav; }
这样以来,控制器就不会和特定的视图相耦合,传递给 DispatcherServlet 的视图名并不直接表示某个特定的 JSP。(实际上,它甚至不能肯定视图就是 JSP)相反,它传递的仅仅是一个逻辑名称,这个名称将会用来查找产生结果的真正视图。
DispatcherServlet 将会使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现,它多是也可能不是 JSP
上面的例子是直接绑定到了 index.jsp 视图
既然 DispatcherServlet 已经知道由哪一个视图渲染结果了,那请求的任务基本上也就完成了。
它的最后一站是视图的实现,在这里它交付模型数据,请求的任务也就完成了。视图使用模型数据渲染出结果,这个输出结果会经过响应对象传递给客户端。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <h1>${message}</h1>
上面咱们已经对 Spring MVC 有了必定的了解,而且经过 XML 配置的方式建立了第一个 Spring MVC 程序,咱们来看看基于注解应该怎么完成上述程序的配置:
package controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @Controller public class HelloController{ @RequestMapping("/hello") public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { ModelAndView mav = new ModelAndView("index.jsp"); mav.addObject("message", "Hello Spring MVC"); return mav; } }
把实现的接口也给去掉。
@Controller
注解:@Component
注解代替,但我本身尝试了一下并不行,由于上述例子没有配置 JSP 视图解析器我还本身配了一个仍没有成功...)@RequestMapping
注解:/hello
会映射到该方法上在 dispatcher-servlet.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" 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.xsd"> <!--<bean id="simpleUrlHandlerMapping"--> <!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">--> <!--<property name="mappings">--> <!--<props>--> <!--<!– /hello 路径的请求交给 id 为 helloController 的控制器处理–>--> <!--<prop key="/hello">helloController</prop>--> <!--</props>--> <!--</property>--> <!--</bean>--> <!--<bean id="helloController" class="controller.HelloController"></bean>--> <!-- 扫描controller下的组件 --> <context:component-scan base-package="controller"/> </beans>
当配置完成,从新启动服务器,输入 localhost/hello
地址仍然能看到效果:
若是 @RequestMapping
做用在类上,那么就至关因而给该类全部配置的映射地址前加上了一个地址,例如:
@Controller @RequestMapping("/wmyskxz") public class HelloController { @RequestMapping("/hello") public ModelAndView handleRequest(....) throws Exception { .... } }
localhost/wmyskxz/hello
还记得咱们 Spring MVC 的请求流程吗,视图解析器负责定位视图,它接受一个由 DispaterServlet 传递过来的逻辑视图名来匹配一个特定的视图。
localhost/test.jsp
就可以直接访问到了,这会形成数据泄露...localhost/index.jsp
试试,根据咱们上面的程序,这会是一个空白的页面,由于并无获取到 ${message}
参数就直接访问了,这会影响用户体验 咱们将咱们的 JSP 文件配置在【WEB-INF】文件夹中的【page】文件夹下,【WEB-INF】是 Java Web 中默认的安全目录,是不容许用户直接访问的(也就是你说你经过 localhost/WEB-INF/
这样的方式是永远访问不到的)
可是咱们须要将这告诉给视图解析器,咱们在 dispatcher-servlet.xml 文件中作以下配置:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/page/" /> <property name="suffix" value=".jsp" /> </bean>
这里配置了一个 Spring MVC 内置的一个视图解析器,该解析器是遵循着一种约定:会在视图名上添加前缀和后缀,进而肯定一个 Web 应用中视图资源的物理路径的。让咱们实际来看看效果:
咱们将代码修改一下:
按照上述的配置,完成:
<?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" 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.xsd"> <!--<bean id="simpleUrlHandlerMapping"--> <!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">--> <!--<property name="mappings">--> <!--<props>--> <!--<!– /hello 路径的请求交给 id 为 helloController 的控制器处理–>--> <!--<prop key="/hello">helloController</prop>--> <!--</props>--> <!--</property>--> <!--</bean>--> <!--<bean id="helloController" class="controller.HelloController"></bean>--> <!-- 扫描controller下的组件 --> <context:component-scan base-package="controller"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/page/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
在【WEB-INF】文件夹下新建一个【page】文件夹,并将【index.jsp】文件剪贴到里面:
访问 localhost/hello
路径,看到正确效果:
咱们传入的逻辑视图名为 index ,再加上 “/WEB-INF/page/
” 前缀和 “.jsp
” 后缀,就能肯定物理视图的路径了,这样咱们之后就能够将全部的视图放入【page】文件夹下了!
使用控制器接收参数每每是 Spring MVC 开发业务逻辑的第一步,为探索 Spring MVC 的传参方式,为此咱们先来建立一个简单的表单用于提交数据:
<!DOCTYPE html> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" isELIgnored="false"%> <html> <head> <meta charset="utf-8"> <title>Spring MVC 传参方式</title> </head> <body> <form action="/param" role="form"> 用户名:<input type="text" name="userName"><br/> 密码:<input type="text" name="password"><br/> <input type="submit" value="提 交"> </form> </body> </html>
丑就丑点儿吧,咱们就是来测试一下:
咱们很容易知道,表单会提交到 /param
这个目录,咱们先来使用 Servlet 原生的 API 来看看能不能获取到数据:
@RequestMapping("/param") public ModelAndView getParam(HttpServletRequest request, HttpServletResponse response) { String userName = request.getParameter("userName"); String password = request.getParameter("password"); System.out.println(userName); System.out.println(password); return null; }
测试成功:
咱们能够把方法定义的形参名字设置成和前台传入参数名同样的方法,来获取到数据(同名匹配规则):
@RequestMapping("/param") public ModelAndView getParam(String userName, String password) { System.out.println(userName); System.out.println(password); return null; }
测试成功:
@RequestParam("前台参数名")
来注入:@RequestParam
注解细节:value
、required
、defaultvalue
value
:指定 name
属性的名称是什么,value
属性均可以默认不写required
:是否必需要有该参数,能够设置为【true】或者【false】defaultvalue
:设置默认值让咱们先来为咱们的表单建立一个 User 模型:
package pojo; public class User { String userName; String password; /* getter and setter */ }
而后测试仍然成功:
咱们能够经过配置 Spring MVC 字符编码过滤器来完成,在 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】:
<!DOCTYPE html> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.*" isELIgnored="false" %> <html> <head> <title>Spring MVC 数据回显</title> </head> <body> <h1>回显数据:${message}</h1> </body> </html>
咱们先来测试一下 Servlet 原生的 API 是否能完成这个任务:
@RequestMapping("/value") public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { request.setAttribute("message","成功!"); return new ModelAndView("test1"); }
在浏览器地址栏中输入:localhost/value
测试
在 Spring MVC 中,咱们一般都是使用这样的方式来绑定数据,
@ModelAttribute
注解:@ModelAttribute public void model(Model model) { model.addAttribute("message", "注解成功"); } @RequestMapping("/value") public String handleRequest() { return "test1"; }
这样写就会在访问控制器方法 handleRequest() 时,会首先调用 model() 方法将 message
添加进页面参数中去,在视图中能够直接调用,可是这样写会致使该控制器全部的方法都会首先调用 model() 方法,但一样的也很方便,由于能够加入各类各样的数据。
前面不论是地址 /hello
跳转到 index.jsp 仍是 /test
跳转到 test.jsp,这些都是服务端的跳转,也就是 request.getRequestDispatcher("地址").forward(request, response);
那咱们如何进行客户端跳转呢?咱们继续在 HelloController 中编写:
@RequestMapping("/hello") public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { ModelAndView mav = new ModelAndView("index"); mav.addObject("message", "Hello Spring MVC"); return mav; } @RequestMapping("/jump") public ModelAndView jump() { ModelAndView mav = new ModelAndView("redirect:/hello"); return mav; }
咱们使用 redirect:/hello
就表示咱们要跳转到 /hello
这个路径,咱们重启服务器,在地址栏中输入:localhost/jump
,会自动跳转到 /hello
路径下:
也能够这样用:
@RequestMapping("/jump") public String jump() { return "redirect: ./hello"; }
咱们先来回顾一下传统的文件上传和下载:这里
咱们再来看一下在 Spring MVC 中如何实现文件的上传和下载
commons-io-1.3.2.jar
和 commons-fileupload-1.2.1.jar
两个包在 dispatcher-servlet.xml 中新增一句:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
开启对上传功能的支持
文件名为 upload.jsp,仍建立在【page】下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>测试文件上传</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="picture"> <input type="submit" value="上 传"> </form> </body> </html>
在 Package【controller】下新建【UploadController】类:
package controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; @Controller public class UploadController { @RequestMapping("/upload") public void upload(@RequestParam("picture") MultipartFile picture) throws Exception { System.out.println(picture.getOriginalFilename()); } @RequestMapping("/test2") public ModelAndView upload() { return new ModelAndView("upload"); } }
在浏览器地址栏中输入:localhost/test2
,选择文件点击上传,测试成功:
欢迎转载,转载请注明出处!转载自@我没有三颗心脏