SpringMVC
一、SpringMVC介绍
1.为什么要使用springMVC?
很多应用程序的问题在于处理业务数据和显示业务数据的视图的对象之间存在紧密耦合。通常,更新业务对象的命令都是从视图本身发起的,使视图对任何业务对象更改都有高度敏感性。而且,当多个视图依赖于同一个业务对象时是没有灵活性的。
Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。
2.MVC设计模型
MVC 是一种著名的设计模式,特别是在 Web 应用程序领域。模式全都是关于将包含业务数据的模块与显示模块的视图解耦的。这是怎样发生的?视图(例如,JSP 页面)怎样能够与其模型(例如,包含数据的 JavaBean)解耦?记得这句格言么?一个层次的重定向几乎可以解决计算机业中的所有问题。确实,在模型和视图之间引入重定向层可以解决问题。此重定向层是控制器。控制器将接收请求,执行更新模型的操作,然后通知视图关于模型更改的消息。依赖于模型的状态并且依赖于请求的控制器可以决定要显示哪个视图。图 1 演示了这种模式。
3.springMVC的强大之处
1.Spring MVC 实现了即用的 MVC 的核心概念。它为控制器和处理程序提供了大量与此模式相关的功能。并且当向 MVC 添加反转控制(Inversion of Control,IoC)时,它使应用程序高度解耦,提供了通过简单的配置更改即可动态更改组件的灵活性。Spring MVC 为您提供了完全控制应用程序的各个方面的力量。
2.Spring 的 Web MVC 模块是围绕 DispatcherServlet 而设计的。DispatcherServlet 给处理程序分派请求,执行视图解析,并且处理语言环境和主题解析,此外还为上传文件提供支持。
3.DispatcherServlet 通过使用处理程序映射来决定哪一个处理程序应当处理传入的请求(处理器映射器决定调用什么控制器)。处理程序映射只是用于标识使用哪一个处理程序来处理特定 URL 模式的映射。处理程序是只有一种方法 ModelAndViewhandleRequest(request,response) 的控制器接口的实现。Spring 还有一些可用的高级处理程序实现;其中一个重要的高级处理程序实现是 SimpleFormController,它提供了将命令对象绑定到表单、对其执行验证等功能。
4.springMVC优势
1、清晰的角色划分:核心控制器(DispatcherServlet 不用写)、请求到处理器映射器(HandlerMapping 不用写)、处理器适配器(HandlerAdapter 不用写)、视图解析器(ViewResolver不用写)、处理器或页面控制器(Controller 程序员自己写 相当于web阶段的servlet)、验证器( Validator)、命令对象(Command 请求参数绑定到的对象就叫命令对象 AbstractCommadController)、表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)。
2、分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要;
3、封装数据简单
4、和Spring 其他框架无缝集成,是其它Web框架所不具备的;
5、可适配,通过HandlerAdapter可以支持任意的类作为处理器(控制器);
6、可定制性,HandlerMapping、ViewResolver等能够非常简单的定制;
7、功能强大的数据验证、格式化、绑定机制;
8、利用Spring提供的Mock对象能够非常简单的进行Web层单元测试;
9、本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换。
10、强大的JSP标签库,使JSP编写更容易。
………………还有比如
风格的支持、简单的文件上传、约定大于配置的契约式编程支持、基于注解的零配置支持等等。
5.springMVC的运行原理
1.架构图
spring架构图
2.传统的MVC架构
首先让我们了解下 MVC(Model-View-Controller)三元组的概念:
Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或 JavaBean 组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据) 和 服务层(行为)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
领域模型
javaBean组件等价于 域模型层 + 业务逻辑层 + 持久层
View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,
由视图负责展示。
也就是说控制器做了个调度员的工作。
菜:视图层
服务员:控制器
厨师,材料:model
面试:springMVC架构图
springMVC执行流程
核心架构的具体流程步骤如下:
1、 首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;
2、DispatcherServlet——>HandlerMapping(处理器映射器),HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
把请求映射给某一个处理器(控制器)
3、 DispatcherServlet——>HandlerAdapter(处理器适配器),HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
4、 HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
处理器适配器 执行 处理器(控制器) 返回 ModelAndView
5、 ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
视图解析器 解析 modelAndView 为一个视图
6、 View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
二、springMVC的第一个程序
1. maven创建一个javaweb工程
2.导入spring的jar包
spring4+1
spring的四个核心包
依赖包common.logging
springweb+springmvc
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.5.RELEASE</version> </dependency> |
3.配置DispatcherServlet核心分发器(web.xml)
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--我们经常使用的配置文件加载方式-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<!-- 加载默认mvc配置文件:springname-servlet.xml ,必须放在WEB-INF下面-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>//或者使用默认拦截/
</servlet-mapping>
说明:/和/*,*.do的区别?
/可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。
弊端:会导致静态文件(js,css)被拦截后不能正常显示。想实现REST风格,就是麻烦一些。后面有解决办法。
拦截/*,这是一个错误的方式,请求可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。(springMVC不能用/*)
*.do:拦截的后缀为do的请求
4.在springmvc的配置文件中配置handlerMapping映射器
<!-- 处理器映射器 -->
<!-- 根据bean的name进行查找Handler 将action的url配置在bean的name中 -->
<!-- 这是一个默认的映射处理器,即使不配置,那么也是默认就是这个 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
5.配置handlerAdapter适配器
//注意:这个适配器不是必须配置的,这是默认的、他在servlet容器已启动就被加载。
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
6.编写一个Controller类
public class MyController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
/**
* 1.收集参数、验证参数
* 2.绑定参数到命令对象
* 3.将命令对象传入业务对象进行处理
* 4.选择视图
*/
ModelAndView mv = new ModelAndView();
//添加模型数据,那么这个数据可以是任意的POJO对象。
mv.addObject("hello","hello world!!");
//设置逻辑视图名,视图解析器会根据该名字解析到具体的视图界面
mv.setViewName("/WEB-INF/jsps/hello.jsp");
return mv;
}
}
7.配置自定义控制器
<!-- 配置自定义controller ,使用beanName:name=”/hello.do”进行进行请求映射匹配-->
<bean name="/hello.do" class="com.qf.controller.MyController"></bean>
8.定义一个响应页面
9.配置视图解析器
使用视图解析器解析逻辑视图,这样更方便,易于扩展。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--逻辑视图前缀-->
<property name="prefix" value="/WEB-INF/jsps/"></property>
<!--逻辑视图后缀,匹配模式:前缀+逻辑视图+后缀,形成完整路径名-->
<property name="suffix" value=".jsp"></property>
10.分析程序执行流程
1、 首先用户发送请求http://localhost:8080/springmvc-01/hello.do——>web容器,web容器根据“/hello.do”路径映射到DispatcherServlet(url-pattern为*.do)进行处理;
2、 DispatcherServlet——>BeanNameUrlHandlerMapping进行请求到处理的映射,BeanNameUrlHandlerMapping将“/hello.do”路径直接映射到name属性为“/hello.do”的Bean进行处理,即MyController,BeanNameUrlHandlerMapping将其包装为HandlerExecutionChain(只包括MyController处理器,没有拦截器)
3、DispatcherServlet——>,SimpleControllerHandlerAdapter (执行器)SimpleControllerHandlerAdapter将HandlerExecutionChain中的处理器(MyController)适配为SimpleControllerHandlerAdapter;
4、 SimpleControllerHandlerAdapter——>MyController处理器功能处理方法的调用,SimpleControllerHandlerAdapter将会调用处理器的handleRequest方法进行功能处理,该处理方法返回一个ModelAndView给DispatcherServlet;
5、 show(ModelAndView的逻辑视图名)——>InternalResourceViewResolver, InternalResourceViewResolver使用JstlView,具体视图页面在/WEB-INF/jsp/show.jsp;
6、 JstlView(/WEB-INF/jsp/hello.jsp)——>渲染,将在处理器传入的模型数据(message=HelloWorld!)在视图中展示出来;
7、 返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
三、HandlerMapping(处理器映射器)
1.BeanNameUrlHandlerMapping
根据bean的name找处理器
HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器(页面控制器)对象、多个 HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;
配置案例:
//默认映射器,即使不配置,默认就使用这个来映射请求。
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
//映射器把请求映射到controller
<bean id="testController" name="/hello.do" class="com.qf.controller.MyController"></bean>
2.SimpleUrlHandlerMapping
可以配置多个映射路径,根据bean的id找处理器
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings"><!—可以配置多个路径-->
<props>
<prop key="/hello1.do">testController</prop><!—一个处理器的bean的id属性 -->
<prop key="/a.do">testController</prop>
</props>
</property>
</bean>
//那么上面的这个映射配置:表示多个*.do文件可以访问多个Controller或者一个Controller。
//前提是:都必须依赖自定义的控制器bean
<bean id="testController" name="/hello.do" class="com.qf.controller.TestController"></bean>
3.ControllerClassNameHandlerMapping
根据控制器的class类名找。
//这个Mapping一配置:我们就可以使用Contrller的 [类名.do]来访问这个Controller.
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean>
总结:多个映射器可以共存。相互不影响。
测试策略:三个映射器全部存在于配置文件中,使用映射器的不同访问方式,全部可以访问成功。
四、HandlerAdapter(处理器适配器)
1.SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter:
表示所有实现了org.springframework.web.servlet.mvc.Controller 接口的Bean 可以作为Spring Web MVC 中的处理器。如果需要其他类型的处理器可以通过实现 HandlerAdapter 来解决。
SimpleControllerHandlerAdapter--->执行处理器的(执行实现了Controller接口的处理器)
2.HttpRequestHandlerAdapter
HttpRequestHandlerAdapter-à执行处理器 (实现了HttpRequestHandler接口的处理器)
HTTP请求处理器适配器
HTTP请求处理器适配器将http请求封装成HttpServletResquest 和HttpServletResponse对象,和servlet接口类似
第一步:配置HttpRequestHandlerAdapter适配器
<!-- 配置HttpRequestHandlerAdapter适配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>
第二步:编写Controller
blic class HttpRequestController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("message", "我是中国人");
request.getRequestDispatcher("/WEB-INF/jsps/mess.jsp").forward(request, response);
}
}
调试运行
总结:适配器可以共存,只是不同的适配器处理实现不同的接口的处理器。
注意:SimpleControllerHandlerAdapter是默认的适配器。如果使用后面这个适配器必须手动配置。否则将会访问失败。
实现了HttpRequestHandler接口:配置文件里面没有配置这个适配器报如下错误:
No adapter for handler [springMVC_01.HttpRequestController@12d527f]: Does your handler implement a supported interface like Controller?
通过这个错误,我们发现: SimpleControllerHandlerAdapter是默认的适配器,只支持实现Controller接口的控制器。
还必须注意:
实现HttpRequestHandler接口的控制器不支持ControllerClassNameHandlerMapping这个处理器映射器。必须使用HttpRequestHandlerAdapter执行该控制器。
总结:
- 实现了Controller接口的处理器 (Controller)
三种映射器都支持
SimpleUrlHandlerMapping
BeanNameHandlerMapping
ControllerClassHandlerMapping
处理器适配器- SimpleControllerHandlerAdapter
不支持HttpRequestHandlerApdater
- 实现了HttpRequestHandler接口的处理器
只支持两种映射器:
SimplerUrlHandlerMapping 根据bean的id 查找 且可以配置多个路径
BeanNameUrlHandlerMapper 根据bean的name属性找
不支持:ControllerClassNameHandlerMapping
处理器适配器-àHttpRequestHandlerAdapter
不支持SimpleControllerHandlerAdapter
五.控制器
1.控制器架构图
2.Controller 简介
Struts2框架的action 多例
web阶段的Servlet 单例
springmvc的controller 单例
1、 收集、验证请求参数并绑定到命令(参数)对象;
id username password
Users u
2、将命令对象交给业务对象,由业务对象处理并返回模型数据;
3、返回ModelAndView(Model部分是业务对象返回的模型数据,视图部分为逻辑视图名)。
六.注解(*****)
1.注解入门
新建一个web工程并导入jar
配置springMVC配置文件
<!-- 添加注解扫描 -->
<context:component-scan base-package="com.qf"></context:component-scan>
<!-- 添加注解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!-- 注解适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsps/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
注解映射器和注解适配器可以使用<mvc:annotation-driven />代替。
<mvc:annotation-driven />默认注册了注解映射器和注解适配器。 |
配置web.xml文件
编写Controller
@Controller
@RequestMapping("/user")
public class MyOneController {
@RequestMapping("/fun")
public String fun(){
return "hello";
}
}
编写hello的JSP页面
2.Controller
@Controller:用于标识是处理器类.表示把我的控制器对象交给spring来创建。l Controller起作用:只需要扫描即可。
<!-- 添加注解扫描!!! -->
<context:component-scan base-package="com.qf.controller"></context:component-scan>
3.RequestMapping
@RequestMapping:请求到处理器功能方法的映射规则;
URL路径映射:@RequestMapping(value="/user")或@RequestMapping("/user")
RequestMapping请求方法限定:get、post
URL模板映射
¨ 需求:修改请求方式改变 restFul风格
@RequestMapping(value="/useredit/{userId}"):{×××}占位符,请求的URL可以是“/useredit/001”或“/useredit/abc”,通过在方法中使用@PathVariable获取{×××}
中的×××变量
useredit?id=001 ----à useredit/001
@RequestMapping("/useredit/{userid}")
public String useredit(@PathVariable String userid) throws Exception{
//方法中使用@PathVariable获取useried的值, model.addAttribute("userid", userid);
return"/user/useredit";
} useredit?id=1;
实现restFul,所有的url都是一个资源的链接,有利于搜索引擎对网址收录。
多个占位符:
@RequestMapping("/useredit/{groupid}/{userid}")
public String useredit(@PathVariable String groupid,@PathVariable String userid) throws Exception{
//方法中使用@PathVariable获取useried的值 model.addAttribute("groupid", groupid);
return"/user/useredit";
}
根路径+子路径
根路径:
@RequestMapping放在类名上边,如下:
@Controller
@RequestMapping("/user")
子路径
@RequestMapping放在方法名上边,如下:
@RequestMapping("/useradd")
public String useradd(…)
请求方法限定
¨ @RequestMapping(method = RequestMethod.GET)
如果通过Post访问则报错:
HTTP Status 405 - Request method 'POST' not supported
例如:
@RequestMapping(value="/useredit/{userid}",method=RequestMethod.GET)
¨ 限定POST方法
@RequestMapping(method = RequestMethod.POST)
如果通过Post访问则报错:
HTTP Status 405 - Request method 'GET' not supported
¨ GET和POST都可以
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
参数传递【请求参数绑定】
¨ 默认支持的参数类型
HttpServletRequest、HttpServletResponse,HttpSession、
基本类型参数(Integer、Long、boolean)
布尔型:
<tr>
<td>用户状态:</td>
<td>
<input type="radio" name="userstate" value="true"/>
<input type="radio" name="userstate" value="false"/></td>
</tr>
<tr>
@RequestMapping("/fun")
public String fun(String userName,Integer age){
return "hello";
}
¨ Pojo对象(Object)
@RequestMapping("/fun")
public String fun(Model model,User user){
return "hello";
}
业务需求:
假如:现在有多个pojo、并且里面具有相同的属性,如果现在还使用基本的对象封装方式,那么将会出现错误。
解析:我们使用包装类来包装pojo、经过包装的pojo相当于加了一层包结构。所以后面即使具有相同的属性也无所谓。
¨ 数组(Array)
页面定义如下:
页面选中多个checkbox向controller方法传递
<input type="checkbox" name="ids" value="001"/>
<input type="checkbox" name=" ids " value="002"/>
<input type="checkbox" name=" ids " value="003"/>
//修改时可以采用循环模式
@RequestMapping("/fun")
public String fun(Model model,Integer[] ids){
return "hello";
}
4.RequestParam
value:参数名字,即入参的请求参数名字,如value=“studentid”表示请求的参数区中的名字为studentid的参数的值将传入;
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报400错误码;
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
定义如下:
public String userlist( @RequestParam(defaultValue="2",value="group",required=true) String groupid) {
}
形参名称为groupid,但是这里使用value="group"限定参数名为group,所以页面传递参数的名必须为group。这里通过required=true限定groupid参数为必需传递,如果不传递则报400错误,由于使用了defaultvalue=”2”默认值即使不传group参数它的值为”2”,所以页面不传递group也不会报错,如果去掉defaultvalue=”2”且定义required=true则如果页面不传递group则会报错。
转发和重定向
5.Redirect
Controller方法返回结果重定向到一个url地址,如果方式:
return "redirect:/user/userlist.do";
注意:
¨ redirect:add.do 与 redirect:/user/add.do" 同一个类
在同一个类里面进行跳转。上面2个都可以实现跳转。但是有区别:
第一个是同一个根路径下面跳转。第二个是在项目路径下进行跳转。
¨ 不同的类进行跳转
不同的类进行跳转只能使用:redirect:/user/add.do进行跳转。即是从项目路径下来查询。
redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/user/userlist.do后边加参数,如下:
/user/userlist.action?groupid=2&…..
6.Forward
controller方法执行后继续执行另一个controller方法。
return "forward:/user/userlist.action";
forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
如下例子:
@RequestMapping("/c")
public String c(String groupid,UserVo userVo)throws Exception{
System.out.println("...c...."+groupid+"...user..."+userVo.getUser());
return "forward:/to/d.action";
}
@RequestMapping("/d")
public String d(String groupid,UserVo userVo)throws Exception{
System.out.println("...d...."+groupid+"...user..."+userVo.getUser());
return "success";
@Controller @Service @Repository @Component
注入
@Resources 按照名称
@Autowired 按照类型
@Qualifier 可以指定名称,结合Autowired 使用
作用域:@Scope
得到properties文件内容的注解:
@Value
@RequestMapping 请求映射
value:指定映射路径
method:指定映射类型 get post
@PathVariable
得到URL中占位符({userid})代表的变量的值
@PathVariable Integer userid
@RequestParam 请求参数设置
require :默认true,限制非空
defualtValue:默认值
value/name :限制变量名
场景:分页时的nowPage
Jackson:
@ResponseBody (把方法的返回值-àjson)
@RequestBody