1.SpringMVC对控制器Controller的支持
SpringMVC控制器的核心是DispatcherServlet。
DispatcherServlet的作用:
- Model处理逻辑的查找(HandlerMapping)
- Model处理逻辑的执行(Handler)
- View的渲染
1.1 处理器controller的查找
1.1.1 直接URL映射Handler
该映射方式通过向spring注册SimpleUrlHandlerMapping类型的Bean来实现。
@Configuration
public class HandlerMappingConfig {
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping(){
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
// 设置url与handler的映射管理,构建一个map
mapping.setUrlMap(Collections.singletonMap("/url1",Handler));
return mapping;
}
}
1.1.2 通过beanName(Handler)与URL映射
该映射方式如果Bean的name或者别名是以 / 开头的,那么这个bean会自动作为Handler的映射路径,URL为Bean的name或者别名,Handler为该Bean本身。
这个自动注册的逻辑发生在BeanNameUrlHanlerMapping中:
// 源码
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
public BeanNameUrlHandlerMapping() {
}
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList();
// 判断beanName是否以/开头
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = this.getApplicationContext().getAliases(beanName);
String[] var4 = aliases;
int var5 = aliases.length;
for(int var6 = 0; var6 < var5; ++var6) {
String alias = var4[var6];
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
注册方式有下面两种:
(1)在configuration注解类里面加入bean:
@Bean(name={"/url1","url2"})
public HttpRequestHandler beanNameHandler(){
// 自己定义的handler
return new BeanNameUrlRequestHandler();
}
(2)使用Component注解来获取bean
@Component()
public class BeanNameUrlRequestHandler{
...
}
1.1.3 RequestMapping映射
这种是最常用的映射使用方式,其Handler查找策略是通过RequestMappingHandlerMapping来实现的:
1.2 处理器执行
Handler的查找和执行是分开的,下面介绍几种常用的Handler:
1.2.1 HttpRequestHandler
这是一种最简单的Handler,其只有一个方法HandleRequest,传入HttpServletRequest和HttpServletResponse两个参数,而且没有返回值,要通过response对象将内容返回:
接口:
public interface HttpRequestHandler {
void handleRequest(HttpServletRequest request, HttpServletResponse response);
}
使用:
(1) 首先建立一个HttpRequestHandler 的实现类
public class MyRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response){
response.getWriter().write("hello httpRequestHandler");
}
}
(2)使用相关mapping来使该handler能够被查找到:
@Configuration
public class HandlerMappingConfig {
@Bean
public SimpleUrlHandlerMapping simpleUrlHandlerMapping(){
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
// 设置url与handler的映射管理,构建一个map
mapping.setUrlMap(Collections.singletonMap("/url1",new MyRequestHandler()));
return mapping;
}
}
虽然该Handler并没有视图查找解析等流程,但是对于一些不需要解析的静态资源,就可以使用该handler来实现。
1.2.2 Controller接口的Handler
该Handler更接近于MVC定义的Controller,虽然同样只有handleRequest方法以及相同的参数,但是其返回值是ModelAndView对象,其中封装了Model和View
@Component("/myController")
public class MyController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
Map<String,Object> model = new HashMap<>();
model.put("name","123");
return new ModelAndView("defaultView",model);
}
}
1.2.3 RequestMapping定义的HandlerMethod
该类型的handler,首先通过RequestMappingHandlerMapping查找满足条件的@RequestMapping,然后将其标注的方法封装成HandlerMethod对象
HandlerMethod对象虽然也可以通过SimpleUrlHandlerMapping来结合使用,但是一般都是与RequestMappingHandlerMapping结合使用
1.3 拦截器Interceptor
在查找Handler的时候,HandlerMapping并不是返回Handler本身,而是返回Handler的执行链HandlerExecutionChain。该执行链中国封装的有需要应用到该Handler上的所有拦截器。
执行流程:
- 在执行处理器handler之前,会先执行HandlerInterceptor的preHandle方法。如果该方法返回false,会中断处理流程。
- 执行处理器Handler,执行完Handler后,有两种结果:正常执行完整和抛出异常。
- 如果是正常执行完Handler,会执行拦截器的postHandle的方法;如果是抛出异常,则会跳过该方法
- 不论是否产生异常,最终都会执行afterCompletion方法。
2.SpringMVC对模型(Model)的支持
2.1 Model模型的相关类型
- Map接口
- Model接口:提供了添加属性、合并属性等功能,虽然该接口没有实现Map,但是可以使用asMap方法来将其转换成map
- ModelMap接口:继承与LinkedHashMap类,该接口的所有方法都是通过Map的put方法来实现添加属性操作
- RedirectAttributes接口:继承自Model接口,该接口为重定向参数提供了特殊方式,通过addFlashAttribute添加重定向可以使用的Model参数。
2.2 模型的使用
1.声明Map的类型参数
@RequestMapping("/testUrl")
public String mapParams(Map<String,Object> map){
map.put("name",123);
return "defaultView";
}
2.声明Model的类型参数
@RequestMapping("/testUrl")
public String modelParams(Model model){
model.addAttribute("name",123);
return "defaultView";
}
3.声明ModelMap类型参数
ModelMap自身是Map类型,但是其也可以使用Model开放的接口方法来操作数据
@RequestMapping("/testUrl")
public String modelMapParams(ModelMap modelMap){
modelMap.put("name",123);
modelMap.addAttribute("name",123);
return "defaultView";
}
4.自己创建Model并返回
@RequestMapping("/defaultView")
public Map createModel(){
Map<String,Object> model = new HashMap<>();
model.put("name",123);
return model;
}
这里由于返回的是model对象而不是view,所以视图名称来源于路径url,这里的url是/defaultView,所以视图名称也就是defaultView。
5.@ModelAttribute方式
该方式同样会取返回值放入model中,属性名称是@ModelAttribute注解声明的名称,值是方法的返回值。
6.直接返回ModelAndView
@RequestMapping("/testUrl")
public ModelAndView createMV(){
ModelAndView mv = new ModelAndView();
mv.setViewName("/defaultView");
mv.addObject("name",123);
return mv;
}
3.SpringMVC对视图的支持
3.1 视图类型
(1)内部资源视图InternalResourceView:该视图代表内部的资源。创建该视图的时候,需要传入URL来表示内部资源的路径。常见资源类型有:静态资源、JSP视图和转发视图
这三种视图跳转都是通过获取对应路径RequestDispatcher来实现的。
转发视图为例:
@RequestMapping("/testUrl")
public String forwardView(Model model){
// 在转发前,springmvc通过request.setAttribute将model中的属性放在了request中
model.addAttribute("name",123);
return "forward:forwardTargetView";
}
@RequestMapping("/forwardTargetView")
public String targetView(HttpServletRequest request,Model model){
// 转发共享request,但是model是独立的
String name = (String)request.getAttribute("name");
model.addAttribute("name",name + 123);
return "defaultView"
}
(2)重定向视图
@RequestMapping("/testRedirect")
public String redirectView(RedirectAttributes model){
model.addAttribute("name",123);
return "redirect:redirectTargetView";
}
3.2 视图查找解析
SpringMVC提供了ViewResolver接口来通过视图名称查找并解析视图,该接口有resolveViewName方法来返回View视图。
public interface ViewResolver {
/**
* params:
* viewName: 视图名称
* locale:地区参数,国际化
*/
View resolveViewName(String viewName, Locale locale throws Exception;
}
3.2.1 通过BeanName来查找视图
这种方式来查找view视图需要声明为spring容器的bean,该解析方式是通过BeanNameViewResolver来执行的,该方式要求声明的bean实现view接口。
// 会匹配/testUrl的ViewName
@Component("testUrl")
public class CustomerView implements View {
@Override
public String getContentType() {
return MediaType.TEXT_HTML_VALUE;
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Object name = model.get("name");
response.getWriter().write("name is " + name);
}
}
3.2.2 模板引擎解析视图(以Thymeleaf为例)
每种模板引擎都会有自己的视图解析器,在引入spring-boot-starter-freemarler后,自动生成ThymeleafViewResolver到SpringMVC中。默认情况下该解析器会以classpath:/templates/为前缀,以.html为后缀,这两个属性可以通过下面方式进行修改
spring.thymeleaf.prefix = 视图前缀
spring.thymeleaf.suffix = 视图后缀
3.2.3 内部资源视图解析
3.2.4 直接指定视图
@RequestMapping("/testUrl")
public View returnView(Model model){
model.addAttribute("name",123);
return new CustomerView();--->实现了View接口的视图对象
}