在此之前,先来学习MVC设计模式。MVC全名是(Model View Controller),是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。其实就是Controller负责接收并处理请求,响应客户端;Model包括模型数据和业务逻辑;View主要是呈现模型,与用户进行交互:
那么什么是SpringMVC呢?SpringMVC是实现了MVC设计模式的框架,同时是Spring框架的一个子模块,二者可以很好地结合使用,不需要整合。
SpringMVC核心组件
接下来通过SpringMVC的工作流程来介绍SpringMVC各核心组件的名称及用法:
1、前端控制器(DispatcherServlet
):接收前端页面的请求,并响应结果到前端页面,同时中间作请求转发,相当于中央处理器;
2、处理器映射器(HandlerMapping
): 根据请求的url来查找对应的处理器(将请求映射到Handle);
3、处理器适配器(HandlerAdapter
): 执行符合特定规则(HandlerAdapter
规则)的处理器;
4、处理器(Handler
):后端控制器,完成具体的业务逻辑,由程序员开发。
5、视图解析器(ViewResolver
): 进行视图解析,根据逻辑视图名解析为真正的视图;
6、视图: View是一个接口,其实现类支持不同的view类型,如jsp,freemaker,pdf等,由程序员开发。
也有人认为有8种核心组件,分别是:1、前端控制器(DispatcherServlet
);2、处理器映射器(HandlerMapping
);3、后端控制器(Handler
);4、处理器拦截器(HandlerInterceptor
);5、处理器执行链(HandlerExecutionChain
);6、处理器适配器(HandlerAdapter
);7、装载模型数据和视图信息(ModelAndView
);8、视图解析器(ViewResolver
);
接下来介绍SpringMVC的实现流程:
1、客户端请求被DispatcherServlet
接收;2、DispatcherServlet
将请求映射到Handler
;3、生成的Handler
以及HandlerInterceptor
;4、返回HandlerExecutionChain
(Handler
+HandlerInterceptor
);5、DispatcherServlet
通过HandlerAdapter
执行Handler
;6、返回一个ModelAndView
;7、DispatcherServlet
通过ViewResolver
进行解析;8、返回填充了模型数据的View,响应给客户端。
简单点就是客户端向服务器发生请求,dispatcherServlet
收到请求后, 依据HandlerMapping
来调用相应的Controller
, Controller
将处理的结果封装成MoodelAndView
对象并返回给dispatcherServlet
, dispatcherServlet
依据viewResolver
解析调用相应的视图对象来生成相应的页面。
尽管上面的过程较为复杂,但是SpringMVC的使用却并不复杂,因为它大部分组件由框架提供,用户只需通过配置进行关联;其实前面也说过用户只需动手编写Handler
和View
即可。
SpringMVC提供了两种方式进行使用:XML配置方式和注解方式,下面将逐一进行介绍。
基于XML方式的使用
基于XML方式使用SpringMVC,只需要三个步骤:1、SpringMVC基础配置;2、XML配置Controller
,HandlerMappering
组件映射;3、XML配置ViewResolver
组件映射.
使用Maven新建一个webapp项目,名称为springmvc_base
。第一步,配置pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!--springmvc相关--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.9.RELEASE</version> </dependency> <!--servlet相关--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!--junit测试相关--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency>
|
第二步,修改web.xml文件内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--配合SpringMVC.xml文件的位置--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
|
第三步,新建com.envy.handler
包,然后在该包内新建MyHandler.java
文件,里面的代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.envy.handler;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller;
public class MyHandler implements Controller { @Override public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { //装载模型数据和逻辑视图 ModelAndView modelAndView = new ModelAndView(); //添加模型数据 modelAndView.addObject("name","envy"); //添加逻辑视图 modelAndView.setViewName("show"); return modelAndView; } }
|
这个MyHandler需要实现Controller接口,注意是org.springframework.web.servlet.mvc.Controller
包内的,这个ModelAndView
中需要装载模型数据和逻辑视图。
第四步,在resources包内新建springmvc.xml文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?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">
<!--配置HandleMapping,将URL请求映射到Handler--> <bean id="handleMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <!--配置mapping--> <property name="mappings"> <props> <!--配置test请求对应的handler为testHandler--> <prop key="/test">testHandler</prop> </props> </property> </bean>
<!--配置handler--> <bean id="testHandler" class="com.envy.handler.MyHandler"></bean>
<!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--配置前缀--> <property name="prefix" value="/"></property> <!--配置后缀--> <property name="suffix" value=".jsp"></property> </bean> </beans>
|
第五步,新建show.jsp页面(注意由于数据时存放在request作用域中,因此在View也就是jsp中可以通过EL表达式获取数据,但前提是设置了<%@ page isELIgnored="false"%>
):
1 2 3 4 5 6 7 8 9 10
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page isELIgnored="false"%> <html> <head> <title>XML方式</title> </head> <body> ${name} </body> </html>
|
运行该项目,在浏览器地址栏中输入http://localhost:8080/show.jsp
,你会发现什么也没有,其实这是正常现象,因为你是直接访问了show.jsp
页面,而不是通过SpringMVC把请求通过Handler处理了,因此正确的访问路径应该是http://localhost:8080/test
,然后页面就会显示envy。
基于注解方式的使用
其实基于注解方式的使用比XML方式更便捷一些,但是针对不同的场合还是选择合适的为好。基于注解方式使用SpringMVC,也只需三个步骤:1、SpringMVC基础配置;2、Controller
,HandlerMappering
组件通过注解方式映射;3、XML配置ViewResolver
组件映射。其实就只有第二步使用了注解,第三步依旧还是使用XML配置。
新建一个MyNewHandler.java
文件(注意此时不再需要继承Controller接口,但是需要在类上使用Controller注解,在方法上面也要使用RequestMapping注解,用于告知访问哪个URL时使用MyNewHandler这个处理器):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.envy.handler;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView;
@Controller public class MyNewHandler{ //自己定义方法用于返回装载模型数据和逻辑视图的ModelAndView //RequestMapping中的参数一般都与方法名称一致,但是可以不一致 @RequestMapping("/getModelAndView") public ModelAndView getModelAndView(){ //装载模型数据和逻辑视图 ModelAndView modelAndView = new ModelAndView(); //添加模型数据 modelAndView.addObject("sex","male"); //设置逻辑视图 modelAndView.setViewName("good"); return modelAndView; }
}
|
接着修改springmvc.xml文件为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?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">
<!--基于注解方式--> <!--将MyNewHandler自动扫描到IOC容器--> <context:component-scan base-package="com.envy.handler"></context:component-scan>
<!--配置视图解析器,两种方式都需要--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--配置前缀--> <property name="prefix" value="/"></property> <!--配置后缀--> <property name="suffix" value=".jsp"></property> </bean> </beans>
|
注意基于注解方式时需要开启包扫描,也就是你所配置的Hander所在的包,让它自动被扫描到IOC容器。而后面的视图解析器是两者都需要配置的。
最后新建good.jsp页面:
1 2 3 4 5 6 7 8 9 10
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page isELIgnored="false" %> <html> <head> <title>注解方式</title> </head> <body> ${sex} </body> </html>
|
然后运行项目,在浏览器地址栏中输入http://localhost:8080/getModelAndView
,你会发现页面显示male。**(注意两种方式同时只能使用一种,另一种代码需要注释,否则会出错误。)**
其实在MyNewHandler
类中定义的getModelAndView
方法就是返回一个ModelAndView
对象,我们知道ModelAndView
对象里面包含了模型数据和逻辑视图,那么可以构造一个方法,只要也能返回模型数据和逻辑视图就能实现相同的功能:
1 2 3 4 5 6 7 8
| //业务方法:model传值、String用于视图解析 @RequestMapping("/ModelStringTest") public String ModelStringTest(Model model){ //添加模型数据 model.addAttribute("sex","male"); //设置逻辑视图 return "good"; }
|
然后运行项目,在浏览器地址栏中输入http://localhost:8080/ModelStringTest
,你会发现页面居然也显示male,那就说明这种方法是可行的。
其实还可以使用map来实现上述功能,但是和前面其实没有什么区别:
1 2 3 4 5 6 7 8
| //业务方法:map传值、String用于视图解析 @RequestMapping("/MapTest") public String MapTest(Map<String,String> map){ //添加模型数据 map.put("sex","male"); //设置逻辑视图 return "good"; }
|
然后运行项目,在浏览器地址栏中输入http://localhost:8080/MapTest
,你会发现页面也显示male,那就说明这种方法同样是可行的。
入门小Demo
接下来通过一个小demo来巩固SpringMVC的入门学习和使用,demo的需求是这样的:前端通过form表单添加商品信息(商品信息包括名称和价格),然后在后端对其进行封装,最后在前端进行展示。
新建一个add.jsp页面,内容为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>add</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <style type="text/css"> body{ overflow-x:hidden; } </style> </head> <body> <form class="form-horizontal" role="form" action="addGoods" method="post"> <div class="form-group"> <label class="col-sm-1 control-label">名称</label> <div class="col-sm-3"> <input type="text" class="form-control" name="name" placeholder="请输入商品名称"> </div> </div> <div class="form-group"> <label class="col-sm-1 control-label">价格</label> <div class="col-sm-3"> <input type="text" class="form-control" name="price" placeholder="请输入商品价格"> </div> </div> <div class="form-group"> <div class="col-sm-offset-1 col-sm-3"> <button type="submit" class="btn btn-default">提交</button> </div> </div> </form> </body> </html>
|
当用户在add.jsp页面form表单中填写信息并提交后,我们通过什么方法来获取到他提交的信息呢?在spring里面常用的就是使用request.getParameter("name属性值")
方式来获取信息,而在springMVC中是通过实体类属性与控件名称来绑定的,即代码里面的name="name",name="price"
,也就是说Goods必须有name和price这两个属性。
新建com.envy.entity
包,接着在里面新建一个Goods.java
文件,里面的代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package com.envy.entity;
public class Goods { private String name; private double price;
public String getName(){ return this.name; } public void setName(String name){ this.name =name; }
public double getPrice(){ return this.price; }
public void setPrice(double price){ this.price=price; }
public Goods(){};
public Goods(String name,double price){ this.name=name; this.price=price; } }
|
接着在MyNewHandler类中定义一个方法,用于处理form表单中提交过来的信息,可以发现action="addGoods"
,即该方法为@RequestMapping("/addGoods")
,定义一个方法addGoods
,代码如下:
1 2 3 4 5 6 7 8 9
| //添加商品并展示 @RequestMapping("/addGoods") public ModelAndView addGoods(Goods goods){ System.out.println(goods.getName()+"---"+goods.getPrice()); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("goods",goods); modelAndView.setViewName("goods"); return modelAndView; }
|
当然需要新建一个goods.jsp页面负责将数据展示出来:
1 2 3 4 5 6 7 8 9 10 11
| <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page isELIgnored="false" %> <html> <head> <title>入门小demo</title> </head> <body> <p>商品名称:${requestScope.goods.name}</p> <p>商品价格:${requestScope.goods.price}</p> </body> </html>
|
然后运行项目,在浏览器地址栏中输入http://localhost:8080/add.jsp
,输入商品信息和名称,点击提交后页面显示刚才提交的信息。
不知道你注意没有,我们在jsp页面中使用了requestScope.goods.name
这种方式来获取name,其实这表明最后提交的信息都是在request作用域中,因此可以通过该方式获取。