Spring MVC为编程人员默认提供了很多的ViewResolver,ViewResolver的主要作用就是讲Controller层返回的View进行处理渲染并最终写入到Response中对请求进行返回。当默认的ViewResolver无法满足要求时我们可以通过自定义ViewResolver的形式来增加独特的功能。
先定义一个对象ViewName,由于这个对象是我们自定义的,所以当返回这个对象的时候没有相应的ViewResolver可以对它进行处理,这个时候就需要使用自定义的ViewResolver来处理这种情况。
package hello.test;
public class ViewName {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接着是定义一个Controller,这个Controller的第一个方法返回刚刚自定义的ViewName对象
package hello.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.concurrent.Callable;
/**
* @Auther: wangzhen25
* @Date: 2018/11/22 14:07
* @Description:
*/
@Controller
public class TestController {
@GetMapping("/test")
public ViewName test4(ModelMap modelMap) {
modelMap.put("key", "value");
ViewName viewName = new ViewName();
viewName.setName("test");
return viewName;
}
}
接着定义一个自定义的View,自定义的View需要继承View接口的同时要重写render方法,render方法的作用就是使用传入的Model对象对视图进行渲染并最终写入到Response中。
package hello.test;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
public class MyView implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
for(Map.Entry<String, ?> entry : model.entrySet()) {
response.getWriter().write(entry.getKey() + " " + entry.getValue());
}
}
}
然后定义一个自定义的ViewResolver,这个ViewResovler用于对Controller层返回的ViewName进行判断,如果发现返回的viewName是test的话就返回刚刚自定义的View,否则返回null
package hello.test;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import java.util.Locale;
public class MyViewResolver implements ViewResolver {
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
if(viewName.equals("test")) {
return new MyView();
}
return null;
}
}
最后再定义一个自定义的HandlerMethodReturnValueHandler用来对一开始返回的ViewName对象进行解析判断,首先是在supportReturnType中判断是否能够处理这个类型,在这里将它的类型和ViewName这个类进行判断,如果发现是ViewName类就返回true,否则返回false,只有返回true才能最终进入handleReturnValue这个方法,在这个方法中主要的作用就是从ViewName这个对象中解析出了一个字符串View同时向ModelAndView容器中添加,在这一步也可以进行其他的复杂操作,但是最终都需要对mavContainer进行view属性的设置,这样才能进入MyViewResolver类中对请求想要获取的具体View进行判断。
package hello.test;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
public class MyHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
boolean result = returnType.getParameterType().isAssignableFrom(ViewName.class);
return result;
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
ViewName viewName = (ViewName) returnValue;
mavContainer.setViewName(viewName.getName());
}
}
最终将以上的视图处理器和视图解析器都配置到Spring中即可使用。
@Configuration
public class TestConfiguration implements WebMvcConfigurer {
@Bean("/controller")
public MyController myController() {
return new MyController();
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(new MyViewResolver());
registry.viewResolver(new MySecondViewResolver());
}
@Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
handlers.add(new MyHandlerMethodReturnValueHandler());
}
}
整个流程可总结如下:首先是Controller层中返回了一个对象,接着框架会进行遍历判断找出能够处理这个类型的HandlerMethodReturnValueHandler,找到相应的Handler后就会对使用返回的对象进行操作,同时会生成一个带有ViewName的ModelAndView对象,接着框架会对这个ModelAndView对象进行处理,这时候框架就会使用所有ViewResolver对这个ViewName进行遍历判断找到能够处理这个ViewName的ViewResolver,最终在ViewResolver中生成视图并返回。