Spring MVC是Spring给我们提供的一个用于简化Web开发的框架
简介
MVC体系
常用的开发框架一般基于两种形式,一种C/S架构,一种B/S架构。在JavaEE开发中,几乎都是基于B/S架构的。在B/S架构中,系统标准的三层包括:表现层、业务层、持久层。
-
表现层
也就是我们常说的Web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,完成http响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求一般回调用业务层进行业务处理,并将处理结果响应给客户端。表现层的设计一般都使用MVC模型。 -
业务层
也就是我们常说的Service层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但业务层不依赖web层。 -
持久层
也就是我们常说的dao层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。
MVC设计
MVC全名Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建Web应用程序表现层的模式。
-
Model(模型)
模型包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务 -
View(视图)
通常指我们的JSP或者HTML页面,作用一般就是展示数据。视图是依据模型数据创建 -
Controller(控制器)
应用程序中处理用户交互的部分。作用一般是处理程序逻辑的。
TIPS:MVC提倡每一层只编写自己的东西,不编写任何其他的代码;分层是为了解耦,为了维护方便和分工协作
Spring MVC
SpringMVC全名Spring Web MVC,是一种基于Java实现的MVC设计模型的请求驱动类型的轻量级Web框架,属于spring-framework后续产品。SpringMVC本质可以认为是对Servlet的封装,简化了我们Servlet的开发
工作流程
1、配置DispatchServlet前端控制器
2、开发处理具体业务逻辑的Handler(@Controller、@RequestMapping)
3、xml配置文件配置Controller扫描,配置springmvc三个件
4、将xml文件路径告诉springmvc(DispatchServlet)
请求处理流程
-
step1:用户发送请求至前端控制器DispatchServlet
step2:DispatchServlet收到请求调用HandlerMapping处理器映射器
step3:处理器映射器根据请求URL找到具体的Handler,生产处理器对象及处理器拦截器,返回DispatchServlet
step4:DispatchServlet调用HandlerAdapter处理器适配器去调用Handler
step5:处理器适配器执行handler
step6:Handler执行完成给处理器适配器返回ModelAndView
step7:处理器适配器向前端控制返回ModelAndView,ModelAndView是SpringMVC框架的一个底层对象,包括model和view
step8:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名来解析真正的视图
step9:视图解析器向前端控制器返回view
step10:前端控制器进行视图渲染,就是将模型数据填充到request域
step11:前端控制器向用户响应结果
九大组件
-
HandlerMapping(处理器映射器)
HandlerMapping是用来查找Handler的,也就是处理器,具体的表现形式可以是类,也可以是方法。比如标注了@RequestMapping的每个方法都可以看成一个Handler。Handler负责具体实际的请求处理,在请求到达后,HandlerMapping的作用就是找到请求相应的处理器Handler和Interceptor -
HandlerAdapter(处理器适配器)
HandlerAdapter是一个适配器。因为SpringMVC中Handler可以是任意形式的,只要能处理请求即可。但是把请求交给Servlet的时候,由于Servlet的方法结构都是doService(HttpServletRequest,HttpServletResponse)形式的,要让固定的Servlet处理方法调用Handler来进行处理,便是HandlerAdapter的职责 -
HandlerExceptionResolver
HandlerExceptionResolver用于处理Handler产生的异常情况。它的作用是根据异常设置ModelAndView,之后交给渲染方法进行渲染,渲染方法会将ModelAndView渲染成页面 -
ViewResolver
ViewResolver即视图解析器,用于将String类型的视图名和Locale解析为View类型的视图,只有一个resolveViewName方法。Controller层返回的String类型视图名viewName最终会在这里被解析成View。View是用来渲染页面的,也就是说它会将程序返回的参数和数据填入模板中,生产HTML文件。ViewResolver主要完成两件事:1)找到渲染所用的模板,2)找到所用的技术(找到视图的类型,如JSP)并填入参数。默认情况下,SpringMVC会自动配置一个InternalResourceViewResolver是针对JSP类型视图的。 -
RequestToViewNameTranslator
RequestToViewNameTranslator组件的作用是从请求中获取ViewName。因为ViewResolver根据ViewName查找View,但有的Handler处理完成之后没有设置View也没有设置ViewName,便要通过这个组件从请求中查找ViewName -
LocaleResolver
ViewResolver组件的resolveViewName方法需要两个参数,一个是视图名,一个是Locale。LocaleResolver用于从请求中解析出Locale,比如中国Locale是zh-CN,用来表示一个区域。这个组件也是i18n的基础 -
ThemeResolver
ThemeResolver组件是用来解析主题的。主题是样式、图片及它们所形成的的显示效果的集合。SpringMVC中一套主题对应一个properties文件。里面存放着与当前主题相关的所有资源,如图片、CSS样式等。创建主题只需要准备好资源,然后新建一个“主题名.properties”并将资源设置进去,放在classpath下,之后便可以在页面中使用了。SpringMVC中与主题相关的类有ThemeResolver、ThemeSource和Theme。ThemeResolver负责从请求中解析出主题名,ThemeSource根据主题名找到具体的主题,其抽象就是Theme,可以通过Theme来获取主题和具体的资源。 -
MultipartResolver
MultipartResolver用于上传请求,通过将普通的请求包装成MultipartHttpServletRequest来实现。MultipartHttpServletRequest可以通过getFile方法直接获得文件。如果上传多个文件,还可以调用getFileMap方法得到Map<FileName, File>这样的结构,MultipartResolver的作用就是封装普通的请求,使其拥有文件上传的功能。 -
FlashMapManager
FlashMap用于重定向时的参数传递。因为重定向时没有传递参数这一功能,如果不想把参数写进URL(不推荐),那么就可以通过FlashMap来传递。只需要在重定向之前将要传递的数据写入请求的属性OUTPUT_FLASH_MAP_ATTRIBUTE中,这样在重定向之后的handler中Spring就会自动将其设置到Model中。FlashMapManager就是用来管理FlashMap的。
参数绑定
-
默认支持Servlet API作为方法参数
当需要使用HttpServletRequest、HttpServletResponse、HttpSession等原生Servlet对象时,直接在handler方法中形参声明使用即可
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, HttpSession session) {}
-
绑定简单类型参数
绑定简单数据类型参数,只需要直接声明形参即可,形参参数名和传递的参数名要保持一致。当形参参数名和传递参数名不一致时可以使用@RequestParam注解进行手动映射。建议使用包装类型,因为基础数据类型不可以为null
public ModelAndView handle(@RequestParam("ids") Integer id, Boolean flag) {}
-
绑定POJO类型参数
接受POJO类型参数,直接形参声明即可,类型就是POJO类型,形参名无所谓,但是要求传递的参数名必须和POJO的属性名保持一致
public ModelAndView handle(User user) {}
-
绑定POJO包装对象参数
不管包装POJO与否,首先它是一个POJO,那么就可以按照POJO的要求来。绑定是直接形参声明即可;传参参数名和POJO属性保持一致,如果不能定位数据项,那么通过属性名+“.”的方式进一步锁定数据
public class QueryVO {
private User user;
}
// url: /demo/handle?user.id=1&user.username=zhangsan
public ModelAndView handle(QueryVO query) {}
- 自定义类型转换器
// 定义类型转换器
public class DateConverter implements Converter<String, Date> {
public Date convert(String source) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {}
}
return null;
}
// 注册自定义类型转换器
<!-- 自动注册最合适的处理器映射器,处理器适配器 -->
<mvc:annotation-driven conversion-service="conversionServiceBean"/>
<!--注册自定义转化器-->
<bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.xxx.converter.DateConverter"/>
</set>
</property>
</bean>
@RequestMapping("/handle")
public ModelAndView handle(Date birthday) {
}
RESTful风格支持
RESTful风格URL:互联网所有的事物都是资源,要求URL中只有表示资源的名词,没有动词
RESTful风格资源操作:使用HTTP请求中的method方法put、delete、post、get来操作资源,分别对应添加、删除、修改、查询。不过一般使用时还是POST和GET。PUT和DELETE几乎不用
RESTful风格资源表述:可以根据需求对URL定位的资源返回不同的表述(也就是返回数据类型,比如XML、JSON等数据格式)
SpringMVC支持RESTful风格请求,具体讲就是使用@PathVariable注解获取RESTful风格的请求URL中的路径变量
<form method="post" action="/demo/handle">
<input type="text" name="username"/>
<input type="submit" value="修改"/>
</form>
<form method="post" action="/demo/handle/15/lisi">
<input type="hidden" name="_method" value="put"/>
<input type="submit" value="新增"/>
</form>
<form method="post" action="/demo/handle">
<input type="hidden" name="_method" value="delete"/>
<input type="submit" value="删除"/>
</form>
@RequestMapping(value = "/demo/handle/{id}", method={RequestMethod.GET}
public ModelAndView get(@PathVariable("id") Integer id) {
}
@RequestMapping(value = "/demo/handle/{id}/{name}", method={RequestMethod.PUT}
public ModelAndView add(@PathVariable("id") Integer id, @PathVariable("username") String username) {
}
@RequestMapping(value = "/demo/handle/{id}", method={RequestMethod.POST}
public ModelAndView update(@PathVariable("id") Integer id, String username) {
}
@RequestMapping(value = "/demo/handle/{id}", method={RequestMethod.DELETE}
public ModelAndView add(@PathVariable("id") Integer id) String username) {
}
<!-- 配置springmvc请求方式转换过滤器,会检查请求参数中是否有_method参数,如果有就按照指定的请求方式进行转换-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*<url-pattern>
</filter-mapping>
Ajax Json交互
@ResponseBody注解
@ResponseBody注解的作用是将Controller的方法返回的对象通过适当的转化器转化为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者XML数据。
TIPS:在使用此注解之后不会再走视图处理器,而是直接将数据写入输入流中,它的效果等同于通过response对象输入指定格式的数据
@RequestMapping("/handle")
public @ResponseBody User handle(@RequestBody User user) {
}
SpringMVC高级技术
拦截器(Interceptor)
监听器、过滤器和拦截器对比
- Servlet:处理Request请求和Response相应
- 过滤器(Filter):堆Request请求起到过滤的作用,作用在Servlet之前,如果配置为/*可以对所有的资源访问进行过滤处理
-
监听器(Listener):实现了javax.servlet.ServletContextListener接口的服务端组件,它随Web应用的启动而启动,只初始化一次,然后会一直运行监视,随web应用的停止而销毁
作用一:做一些初始化工作,web应用中spring容器启动ContextLoaderListener
作用二:监听web中的特定事件,比如HttpSession,ServletRequest的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控,比如统计在线人数,利用HttpSessionListener -
拦截器(Interceptor):是SpringMVC、Struts等表现层框架自己的,不会拦截jsp/html/css/images的访问等,只会拦截访问的控制器方法(Handler)
从配置的角度也能够发现:servlet、filter、listener是配置在web.xml中的,而Interceptor是配置在表现层框架自己的配置文件中的。
拦截器执行流程
1)程序先执行preHandle方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行
2)在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle方法,然后会通过DispatchServlet向客户端返回响应
3)在DispatchServlet处理完请求后,才会执行afterCompletion方法
多拦截器的执行流程
当有多个拦截器同时工作时,它们的preHandle方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle和afterCompletion则会按照配置顺序的反序执行
控制器中处理异常
@ControllerAdvice
public class GlobalExceptionResolver {
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleException(ArithmeticException exception, HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", exception.getMessage());
modelAndView.setViewName("error");
}
}
基于Flash属性的跨重定向请求数据传递
重定向时请求参数会丢失,我们往往需要重新携带请求参数,我们可以进行手动参数拼接。
return "redirect:handle?name=" + name;
但上述拼接参数的方法属于get请求,携带参数长度有限,参数安全性也不高,此时,我们可以使用SpringMVC提供的flash属性机制,向上下文中添加flash属性,框架会在session中记录该属性,当跳转到页面之后框架会自动删除flash属性,不需要我们手动删除,通过这种方式进行重定向参数传递,参数长度和安全性都得到了保障。
@RequestMapping("/handle")
public String handle(String name, RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("name", name);
return "redirect:handle01";
}
网友评论