@Configuration
@ComponentScan // 没有设置扫描包的话默认扫描当前配置的包及其子包
@PropertySource("classpath:application.properties") // 指定资源文件读取的位置
@EnableConfigurationProperties({WebMvcProperties.class, ServerProperties.class}) //使 @ConfigurationProperties 注解生效,并且将组件加入 IOC 容器中
public class WebConfig {
// 内嵌web容器工厂
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory(ServerProperties serverProperties) {
TomcatServletWebServerFactory serverFactory = new TomcatServletWebServerFactory();
serverFactory.setPort(serverProperties.getPort());
return serverFactory;
}
// 创建DispatcherServlet
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
// 注册DispatcherServlet到tomcat
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
// 1=tomcat启动时DispatcherServlet初始化
// -1=第一次请求到时候初始化
registrationBean.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
return registrationBean;
}
}
public class A10Application {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
context.registerBean(WebConfig.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
}
}
server.port=8080
spring.mvc.servlet.load-on-startup=1
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context); // 初始化文件上传解析器
this.initLocaleResolver(context); // 初始化本地语言解析器 (中文、英文...)
this.initThemeResolver(context);
this.initHandlerMappings(context); // 初始化映射处理器
this.initHandlerAdapters(context); // 初始化适配处理器
this.initHandlerExceptionResolvers(context); // 初始化异常解析处理器
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
RequestMappingHandlerMapping用途
/*
如果使用DispatcherServlet的默认RequestMappingHandlerMapping,从容器中是获取不到这个bean的,因为DispatcherServlet将它放入到了自已的成员变量中
*/
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
public class A10Application {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取映射结果
handlerMapping.getHandlerMethods().forEach((k, v) -> {
System.out.println(k + "=" + v);
});
// 请求来了,获取控制器方法,返回控制链结果
HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/test1"));
System.out.println(chain);
}
}
RequestMappingHandlerAdapter用途
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
return new RequestMappingHandlerAdapter();
}
public class A10Application {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取映射结果
handlerMapping.getHandlerMethods().forEach((k, v) -> {
System.out.println(k + "=" + v);
});
// RequestMappingHandlerMapping作用:请求来了,获取控制器方法,返回控制链结果
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/test2");
request.setParameter("name", "张三");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// RequestMappingHandlerAdapter作用:调用控制器
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
adapter.handle(request, response, chain.getHandler());
System.out.println(">>>>>>>>>>>>>>>>> 参数解析器");
for (HandlerMethodArgumentResolver resolver : adapter.getArgumentResolvers()) {
System.out.println(resolver);
}
System.out.println(">>>>>>>>>>>>>>>>> 返回值解析器");
for (HandlerMethodReturnValueHandler returnValueHandler : adapter.getReturnValueHandlers()) {
System.out.println(returnValueHandler);
}
}
}
自定义参数解析器
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
// 自定义参数解析器
TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
adapter.setArgumentResolvers(Arrays.asList(tokenArgumentResolver));
return adapter;
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Token {
}
public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
// 是否支持某个参数
@Override
public boolean supportsParameter(MethodParameter parameter) {
Token annotation = parameter.getParameterAnnotation(Token.class);
return annotation != null;
}
// 解析参数
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
return webRequest.getHeader("token");
}
}
public class A10Application {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取映射结果
handlerMapping.getHandlerMethods().forEach((k, v) -> {
System.out.println(k + "=" + v);
});
// RequestMappingHandlerMapping作用:请求来了,获取控制器方法,返回控制链结果
MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/test3");
request.addHeader("token","令牌");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// RequestMappingHandlerAdapter作用:调用控制器
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
adapter.handle(request, response, chain.getHandler());
}
}
@PutMapping("/test3")
public ModelAndView test3(@Token String token){
System.out.println(token);
return null;
}
自定义结果处理器
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Yml {
}
public class YmlReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Yml yml = returnType.getMethodAnnotation(Yml.class);
return yml != null;
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 转化结果为yaml字符串
String value = new Yaml().dump(returnValue);
// 将yaml结果写入响应
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
response.setContentType("text/plain;charset=utf-8");
response.getWriter().println(value);
// 设置请求已经处理完毕
mavContainer.setRequestHandled(true);
}
}
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
// 自定义结果处理器
YmlReturnValueHandler ymlReturnValueHandler = new YmlReturnValueHandler();
adapter.setReturnValueHandlers(Arrays.asList(ymlReturnValueHandler));
// 自定义参数解析器
TokenArgumentResolver tokenArgumentResolver = new TokenArgumentResolver();
adapter.setArgumentResolvers(Arrays.asList(tokenArgumentResolver));
return adapter;
}
public class A10Application {
public static void main(String[] args) throws Exception {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
// 解析RequestMapping以及派生注解,生成路径与控制器方法的映射关系,在初始化时完成
RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 获取映射结果
handlerMapping.getHandlerMethods().forEach((k, v) -> {
System.out.println(k + "=" + v);
});
// RequestMappingHandlerMapping作用:请求来了,获取控制器方法,返回控制链结果
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test4");
request.addHeader("token","令牌");
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerExecutionChain chain = handlerMapping.getHandler(request);
System.out.println(chain);
// RequestMappingHandlerAdapter作用:调用控制器
RequestMappingHandlerAdapter adapter = context.getBean(RequestMappingHandlerAdapter.class);
adapter.handle(request, response, chain.getHandler());
// 查看响应
String result = response.getContentAsString(StandardCharsets.UTF_8);
System.out.println(result);
}
}
package com.itheima.a21;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockPart;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/*
目标: 解析控制器方法的参数值
常见的参数处理器如下:
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@abbc908
org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver@44afefd5
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver@9a7a808
org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver@72209d93
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver@2687f956
org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver@1ded7b14
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@29be7749
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@5f84abe8
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver@4650a407
org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver@30135202
org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver@6a4d7f76
org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver@10ec523c
org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver@53dfacba
org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver@79767781
org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver@78411116
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver@aced190
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver@245a060f
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@6edaa77a
org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver@1e63d216
org.springframework.web.method.annotation.ModelMethodProcessor@62ddd21b
org.springframework.web.method.annotation.MapMethodProcessor@16c3ca31
org.springframework.web.method.annotation.ErrorsMethodArgumentResolver@2d195ee4
org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver@2d6aca33
org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver@21ab988f
org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver@29314cc9
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver@4e38d975
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@35f8a9d3
*/
public class A21 {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 准备测试 Request
HttpServletRequest request = mockRequest();
// 要点1. 控制器方法被封装为 HandlerMethod
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));
// 要点2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
// 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
ModelAndViewContainer container = new ModelAndViewContainer();
// 要点4. 解析每个参数值
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
// 多个解析器组合
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(
// false 表示必须有 @RequestParam
new RequestParamMethodArgumentResolver(beanFactory, false),
new PathVariableMethodArgumentResolver(),
new RequestHeaderMethodArgumentResolver(beanFactory),
new ServletCookieValueMethodArgumentResolver(beanFactory),
new ExpressionValueMethodArgumentResolver(beanFactory),
new ServletRequestMethodArgumentResolver(),
new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())),
new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam
);
String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
String str = annotations.length() > 0 ? " @" + annotations + " " : " ";
parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
if (composite.supportsParameter(parameter)) {
// 支持此参数
Object v = composite.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
// System.out.println(v.getClass());
System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v);
System.out.println("模型数据为:" + container.getModel());
} else {
System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
}
}
/*
学到了什么
a. 每个参数处理器能干啥
1) 看是否支持某种参数
2) 获取参数的值
b. 组合模式在 Spring 中的体现
c. @RequestParam, @CookieValue 等注解中的参数名、默认值, 都可以写成活的, 即从 ${ } #{ }中获取
*/
}
private static HttpServletRequest mockRequest() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name1", "zhangsan");
request.setParameter("name2", "lisi");
request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
Map<String, String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
System.out.println(map);
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
request.setContentType("application/json");
request.setCookies(new Cookie("token", "123456"));
request.setParameter("name", "张三");
request.setParameter("age", "18");
request.setContent("""
{
"name":"李四",
"age":20
}
""".getBytes(StandardCharsets.UTF_8));
return new StandardServletMultipartResolver().resolveMultipart(request);
}
static class Controller {
public void test(
@RequestParam("name1") String name1, // name1=张三
String name2, // name2=李四
@RequestParam("age") int age, // age=18
@RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1, // spring 获取数据
@RequestParam("file") MultipartFile file, // 上传文件
@PathVariable("id") int id, // /test/124 /test/{id}
@RequestHeader("Content-Type") String header,
@CookieValue("token") String token,
@Value("${JAVA_HOME}") String home2, // spring 获取数据 ${} #{}
HttpServletRequest request, // request, response, session ...
@ModelAttribute("abc") User user1, // name=zhang&age=18
User user2, // name=zhang&age=18
@RequestBody User user3 // json
) {
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class User {
private String name;
private int age;
}
}
第一套底层转换接口与实现
第二套底层转换接口与实现
高级转换接口与实现
public class TestDataBinder {
public static void main(String[] args) {
MyBean myBean = new MyBean();
/* BeanWrapperImpl
BeanWrapperImpl wrapper = new BeanWrapperImpl(myBean);
wrapper.setPropertyValue("a",10);
wrapper.setPropertyValue("b","b");
wrapper.setPropertyValue("c","2023/01/01");
System.out.println(myBean);
*/
/* DirectFieldAccessor
DirectFieldAccessor accessor = new DirectFieldAccessor(myBean);
accessor.setPropertyValue("a",10);
accessor.setPropertyValue("b","b");
accessor.setPropertyValue("c","2023/01/01");
System.out.println(myBean);
*/
/* ServletRequestDataBinder
ServletRequestDataBinder binder = new ServletRequestDataBinder(myBean);
MutablePropertyValues values = new MutablePropertyValues();
values.add("a",10);
values.add("b","b");
values.add("c","2023/01/01");
binder.bind(values);
System.out.println(myBean);*/
}
static class MyBean {
private int a;
private String b;
private Date c;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public Date getC() {
return c;
}
public void setC(Date c) {
this.c = c;
}
@Override
public String toString() {
return "User{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}
public class TestServletDataBinder {
public static void main(String[] args) {
MyBean myBean = new MyBean();
ServletRequestDataBinder binder = new ServletRequestDataBinder(myBean);
// initDirectFieldAccess直接与Filed进行属性绑定 不需要set方法
binder.initDirectFieldAccess();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("a","19");
request.setParameter("b","10");
request.setParameter("c","2023/01/01");
binder.bind(new ServletRequestParameterPropertyValues(request));
System.out.println(myBean);
}
static class MyBean {
private int a;
private String b;
private Date c;
@Override
public String toString() {
return "User{" +
"a=" + a +
", b='" + b + '\'' +
", c=" + c +
'}';
}
}
}
Date类型格式为yyyy|MM|dd 如果使用以下方式与对象绑定,birthday=null,因为默认的转换器无法识别此格式的类型,所以需要添加自定义转换器来绑定
ServletRequestDataBinder binder = new ServletRequestDataBinder(user);
binder.bind(new ServletRequestParameterPropertyValues(request));
public class TestDataBinderFactory {
public static void main(String[] args) throws Exception {
User user = new User();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("birthday","2023|01|01");
request.setParameter("address.name","湘潭");
/*
1.用工厂,无转换功能
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,null);
WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
*/
/*
2.用InitBinder转换 底层走是的jdk的PropertyEditorRegistry
InvocableHandlerMethod invocableHandlerMethod = new InvocableHandlerMethod(new MyController(),MyController.class.getMethod("aaa", WebDataBinder.class));
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Arrays.asList(invocableHandlerMethod),null);
WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
*/
/*
3.用ConversionService转换
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
FormattingConversionService conversionService = new FormattingConversionService();
conversionService.addFormatter(new MyDateFormatter("用ConversionService方式扩展的"));
initializer.setConversionService(conversionService);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,initializer);
WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
*/
/*
4.使用默认用ConversionService转换 配合@DateTimeFormat使用
*/
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
ApplicationConversionService conversionService = new ApplicationConversionService();
initializer.setConversionService(conversionService);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null,initializer);
WebDataBinder binder = factory.createBinder(new ServletWebRequest(request), user, "user");
binder.bind(new ServletRequestParameterPropertyValues(request));
System.out.println(user);
}
static class MyController{
@InitBinder
public void aaa (WebDataBinder binder){
binder.addCustomFormatter(new MyDateFormatter("用InitBinder方式扩展的"));
}
}
static class User{
@DateTimeFormat(pattern = "yyyy|MM|dd")
private Date birthday;
private Address address;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "User{" +
"birthday=" + birthday +
", address=" + address +
'}';
}
}
static class Address{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Address{" +
"name='" + name + '\'' +
'}';
}
}
}
public class MyDateFormatter implements Formatter<Date> {
private static final Logger log = LoggerFactory.getLogger(MyDateFormatter.class);
private final String desc;
public MyDateFormatter(String desc) {
this.desc = desc;
}
@Override
public String print(Date date, Locale locale) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.format(date);
}
@Override
public Date parse(String text, Locale locale) throws ParseException {
log.debug(">>>>>> 进入了: {}", desc);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
return sdf.parse(text);
}
}
@Configuration
public class WebConfig {
@Controller
static class Controller1 {
@ResponseStatus(HttpStatus.OK) // 先不使用ReturnValueHandlerComposite 方便测试
public ModelAndView foo(User user) { // user加入到ModelAndViewContainer中 结果= user:User{name='张三'}
System.out.println("user"+":"+user);
return null;
}
}
static class User{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
public class A11Application {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(WebConfig.class);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("name","张三");
/*
通过ServletInvocableHandlerMethod把这些整合在一起,并完成控制器方法的调用
*/
ServletInvocableHandlerMethod method = new ServletInvocableHandlerMethod(new WebConfig.Controller1(), WebConfig.Controller1.class.getMethod("foo", WebConfig.User.class));
method.setDataBinderFactory(new ServletRequestDataBinderFactory(null,null));
method.setParameterNameDiscoverer(new DefaultParameterNameDiscoverer());
method.setHandlerMethodArgumentResolvers(getArgumentResolvers(context));
ModelAndViewContainer container = new ModelAndViewContainer();
method.invokeAndHandle(new ServletWebRequest(request),container);
context.close();
}
public static HandlerMethodArgumentResolverComposite getArgumentResolvers(AnnotationConfigApplicationContext context) {
HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
composite.addResolvers(
new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), false),
new PathVariableMethodArgumentResolver(),
new RequestHeaderMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ServletCookieValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ExpressionValueMethodArgumentResolver(context.getDefaultListableBeanFactory()),
new ServletRequestMethodArgumentResolver(),
new ServletModelAttributeMethodProcessor(false),
new RequestResponseBodyMethodProcessor(Arrays.asList(new MappingJackson2HttpMessageConverter())),
new ServletModelAttributeMethodProcessor(true),
new RequestParamMethodArgumentResolver(context.getDefaultListableBeanFactory(), true)
);
return composite;
}
}
@Configuration
public class WebConfig {
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setDefaultEncoding("utf-8");
configurer.setTemplateLoaderPath("classpath:templates");
return configurer;
}
@Bean // FreeMarkerView 在借助 Spring 初始化时,会要求 web 环境才会走 setConfiguration, 这里想办法去掉了 web 环境的约束
public FreeMarkerViewResolver viewResolver(FreeMarkerConfigurer configurer) {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver() {
@Override
protected AbstractUrlBasedView instantiateView() {
FreeMarkerView view = new FreeMarkerView() {
@Override
protected boolean isContextRequired() {
return false;
}
};
view.setConfiguration(configurer.getConfiguration());
return view;
}
};
resolver.setContentType("text/html;charset=utf-8");
resolver.setPrefix("/");
resolver.setSuffix(".ftl");
resolver.setExposeSpringMacroHelpers(false);
return resolver;
}
}
/*
目标: 解析控制器方法的返回值
常见的返回值处理器
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler@4c9e38
org.springframework.web.method.annotation.ModelMethodProcessor@5d1e09bc
org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler@4bdc8b5d
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler@3bcd426c
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler@5f14a673
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor@726a17c4
org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler@5dc3fcb7
org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler@c4c0b41
org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler@76911385
org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler@5467eea4
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@160396db
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor@7a799159
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler@40ab8a8
org.springframework.web.method.annotation.MapMethodProcessor@6ff37443
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor@65cc8228
*/
public class A27 {
private static final Logger log = LoggerFactory.getLogger(A27.class);
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(WebConfig.class);
// 1. 测试返回值类型为 ModelAndView
// 2. 测试返回值类型为 String 时, 把它当做视图名
// 3. 测试返回值添加了 @ModelAttribute 注解时, 此时需找到默认视图名
// 4. 测试返回值不加 @ModelAttribute 注解且返回非简单类型时, 此时需找到默认视图名
// 5. 测试返回值类型为 ResponseEntity 时, 此时不走视图流程
// 6. 测试返回值类型为 HttpHeaders 时, 此时不走视图流程
// 7. 测试返回值添加了 @ResponseBody 注解时, 此时不走视图流程
test7(context);
/*
学到了什么
a. 每个返回值处理器能干啥
1) 看是否支持某种返回值
2) 返回值或作为模型、或作为视图名、或作为响应体 ...
b. 组合模式在 Spring 中的体现 + 1
*/
}
private static void test7(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test7");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
if (!container.isRequestHandled()) {
renderView(context, container, webRequest); // 渲染视图
} else {
for (String name : response.getHeaderNames()) {
System.out.println(name + "=" + response.getHeader(name));
}
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
}
}
private static void test6(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test6");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
if (!container.isRequestHandled()) {
renderView(context, container, webRequest); // 渲染视图
} else {
for (String name : response.getHeaderNames()) {
System.out.println(name + "=" + response.getHeader(name));
}
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
}
}
private static void test5(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test5");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
if (!container.isRequestHandled()) {
renderView(context, container, webRequest); // 渲染视图
} else {
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
}
}
private static void test4(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test4");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/test4");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
ServletWebRequest webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}
private static void test3(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test3");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/test3");
UrlPathHelper.defaultInstance.resolveAndCacheLookupPath(request);
ServletWebRequest webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}
private static void test2(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test2");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
ServletWebRequest webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}
private static void test1(AnnotationConfigApplicationContext context) throws Exception {
Method method = Controller.class.getMethod("test1");
Controller controller = new Controller();
Object returnValue = method.invoke(controller); // 获取返回值
HandlerMethod methodHandle = new HandlerMethod(controller, method);
ModelAndViewContainer container = new ModelAndViewContainer();
HandlerMethodReturnValueHandlerComposite composite = getReturnValueHandler();
ServletWebRequest webRequest = new ServletWebRequest(new MockHttpServletRequest(), new MockHttpServletResponse());
if (composite.supportsReturnType(methodHandle.getReturnType())) { // 检查是否支持此类型的返回值
composite.handleReturnValue(returnValue, methodHandle.getReturnType(), container, webRequest);
System.out.println(container.getModel());
System.out.println(container.getViewName());
renderView(context, container, webRequest); // 渲染视图
}
}
public static HandlerMethodReturnValueHandlerComposite getReturnValueHandler() {
HandlerMethodReturnValueHandlerComposite composite = new HandlerMethodReturnValueHandlerComposite();
composite.addHandler(new ModelAndViewMethodReturnValueHandler());
composite.addHandler(new ViewNameMethodReturnValueHandler());
composite.addHandler(new ServletModelAttributeMethodProcessor(false));
composite.addHandler(new HttpEntityMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())));
composite.addHandler(new HttpHeadersReturnValueHandler());
composite.addHandler(new RequestResponseBodyMethodProcessor(List.of(new MappingJackson2HttpMessageConverter())));
composite.addHandler(new ServletModelAttributeMethodProcessor(true));
return composite;
}
@SuppressWarnings("all")
private static void renderView(AnnotationConfigApplicationContext context, ModelAndViewContainer container,
ServletWebRequest webRequest) throws Exception {
log.debug(">>>>>> 渲染视图");
FreeMarkerViewResolver resolver = context.getBean(FreeMarkerViewResolver.class);
String viewName = container.getViewName() != null ? container.getViewName() : new DefaultRequestToViewNameTranslator().getViewName(webRequest.getRequest());
log.debug("没有获取到视图名, 采用默认视图名: {}", viewName);
// 每次渲染时, 会产生新的视图对象, 它并非被 Spring 所管理, 但确实借助了 Spring 容器来执行初始化
View view = resolver.resolveViewName(viewName, Locale.getDefault());
view.render(container.getModel(), webRequest.getRequest(), webRequest.getResponse());
System.out.println(new String(((MockHttpServletResponse) webRequest.getResponse()).getContentAsByteArray(), StandardCharsets.UTF_8));
}
static class Controller {
private static final Logger log = LoggerFactory.getLogger(Controller.class);
public ModelAndView test1() {
log.debug("test1()");
ModelAndView mav = new ModelAndView("view1");
mav.addObject("name", "张三");
return mav;
}
public String test2() {
log.debug("test2()");
return "view2";
}
@ModelAttribute
// @RequestMapping("/test3")
public User test3() {
log.debug("test3()");
return new User("李四", 20);
}
public User test4() {
log.debug("test4()");
return new User("王五", 30);
}
public HttpEntity<User> test5() {
log.debug("test5()");
return new HttpEntity<>(new User("赵六", 40));
}
public HttpHeaders test6() {
log.debug("test6()");
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/html");
return headers;
}
@ResponseBody
public User test7() {
log.debug("test7()");
return new User("钱七", 50);
}
}
// 必须用 public 修饰, 否则 freemarker 渲染其 name, age 属性时失败
public static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
public class A28 {
public static void main(String[] args) throws IOException, NoSuchMethodException, HttpMediaTypeNotAcceptableException {
// test1();
// test2();
// test3();
test4();
/*
学到了什么
a. MessageConverter 的作用, @ResponseBody 是返回值处理器解析的, 但具体转换工作是 MessageConverter 做的
b. 如何选择 MediaType
- 首先看 @RequestMapping 上有没有指定
- 其次看 request 的 Accept 头有没有指定
- 最后按 MessageConverter 的顺序, 谁能谁先转换
*/
}
private static void test4() throws IOException, HttpMediaTypeNotAcceptableException, NoSuchMethodException {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
ServletWebRequest webRequest = new ServletWebRequest(request, response);
request.addHeader("Accept", "application/xml");
response.setContentType("application/json");
RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(
List.of(
new MappingJackson2HttpMessageConverter(), new MappingJackson2XmlHttpMessageConverter()
));
processor.handleReturnValue(
new User("张三", 18),
new MethodParameter(A28.class.getMethod("user"), -1),
new ModelAndViewContainer(),
webRequest
);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
@ResponseBody
@RequestMapping(produces = "application/json")
public User user() {
return null;
}
private static void test3() throws IOException {
MockHttpInputMessage message = new MockHttpInputMessage("""
{
"name":"李四",
"age":20
}
""".getBytes(StandardCharsets.UTF_8));
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
if (converter.canRead(User.class, MediaType.APPLICATION_JSON)) {
Object read = converter.read(User.class, message);
System.out.println(read);
}
}
private static void test2() throws IOException {
MockHttpOutputMessage message = new MockHttpOutputMessage();
MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();
if (converter.canWrite(User.class, MediaType.APPLICATION_XML)) {
converter.write(new User("李四", 20), MediaType.APPLICATION_XML, message);
System.out.println(message.getBodyAsString());
}
}
public static void test1() throws IOException {
MockHttpOutputMessage message = new MockHttpOutputMessage();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
if (converter.canWrite(User.class, MediaType.APPLICATION_JSON)) {
converter.write(new User("张三", 18), MediaType.APPLICATION_JSON, message);
System.out.println(message.getBodyAsString());
}
}
public static class User {
private String name;
private int age;
@JsonCreator
public User(@JsonProperty("name") String name, @JsonProperty("age") int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
@Configuration
public class WebConfig {
@ControllerAdvice
static class MyControllerAdvice implements ResponseBodyAdvice<Object> {
// 满足条件才转换
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getMethodAnnotation(ResponseBody.class) != null ||
AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null) {
// returnType.getContainingClass().isAnnotationPresent(ResponseBody.class)) {
return true;
}
return false;
}
// 将 User 或其它类型统一为 Result 类型
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) {
return body;
}
return Result.ok(body);
}
}
// @Controller
// @ResponseBody
@RestController
public static class MyController {
public User user() {
return new User("王五", 18);
}
}
public static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
public class ExceptionHandlerDemo {
public static void main(String[] args) throws NoSuchMethodException, UnsupportedEncodingException {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.setMessageConverters(ListUtil.of(new MappingJackson2HttpMessageConverter()));
// 默认参数解析器、结果解析器
resolver.afterPropertiesSet();
/*
测试json
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerMethod handler = new HandlerMethod(new Controller1(),Controller1.class.getMethod("foo"));
resolver.resolveException(request,response,handler,new ArithmeticException("被零整除了"));
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
*/
/*
测试mav
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerMethod handler = new HandlerMethod(new Controller2(),Controller2.class.getMethod("foo"));
ModelAndView modelAndView = resolver.resolveException(request, response, handler, new ArithmeticException("被零整除"));
System.out.println(modelAndView.getViewName());
System.out.println(modelAndView.getModelMap());
*/
/*
嵌套异常
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerMethod handler = new HandlerMethod(new Controller3(),Controller3.class.getMethod("foo"));
RuntimeException e = new RuntimeException("e2", new IOException("e3"));
resolver.resolveException(request,response,handler,e);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
*/
// 异常处理方法参数解析
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
HandlerMethod handler = new HandlerMethod(new Controller4(), Controller4.class.getMethod("foo"));
Exception e = new Exception("a");
resolver.resolveException(request, response, handler, e);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
static class Controller1 {
public void foo() {
}
@ExceptionHandler
@ResponseBody
public Map<String, Object> handler(ArithmeticException e) {
return MapUtil.of("error", e.getMessage());
}
}
static class Controller2 {
public void foo() {
}
@ExceptionHandler
public ModelAndView handler(ArithmeticException e) {
return new ModelAndView("test2", MapUtil.of("error", e.getMessage()));
}
}
static class Controller3 {
public void foo() {
}
@ExceptionHandler
@ResponseBody
public Map<String, Object> handler(IOException e3) {
return MapUtil.of("error", e3.getMessage());
}
}
static class Controller4 {
public void foo() {
}
@ExceptionHandler
@ResponseBody
public Map<String, Object> handler(Exception e, HttpServletRequest request) {
System.out.println(request);
return MapUtil.of("error", e.getMessage());
}
}
}
// 全局通用异常
@Configuration
public class WebConfig {
@ControllerAdvice
static class MyControllerAdvice {
@ExceptionHandler
@ResponseBody
public Map<String, Object> handle(Exception e) {
return Map.of("error", e.getMessage());
}
}
@Bean
public ExceptionHandlerExceptionResolver resolver() {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
return resolver;
}
}
public class A31 {
public static void main(String[] args) throws NoSuchMethodException {
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
// ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
// resolver.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter()));
// resolver.afterPropertiesSet();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
ExceptionHandlerExceptionResolver resolver = context.getBean(ExceptionHandlerExceptionResolver.class);
HandlerMethod handlerMethod = new HandlerMethod(new Controller5(), Controller5.class.getMethod("foo"));
Exception e = new Exception("e1");
resolver.resolveException(request, response, handlerMethod, e);
System.out.println(new String(response.getContentAsByteArray(), StandardCharsets.UTF_8));
}
static class Controller5 {
public void foo() {
}
}
}
@Configuration
public class WebConfig {
// 内嵌web容器工厂
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
// 创建DispatcherServlet
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
// 注册DispatcherServlet到tomcat
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
return registrationBean;
}
@Bean
public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter(){
return new SimpleControllerHandlerAdapter();
}
@Bean
public BeanNameUrlHandlerMapping beanNameUrlHandlerMapping(){
return new BeanNameUrlHandlerMapping();
}
@Component("/test1")
public static class Controller1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().println("this is Controller1");
return null;
}
}
@Component("/test2")
public static class Controller2 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().println("this is Controller2");
return null;
}
}
@Bean("/test3")
public Controller Controller3() {
return (request, response) -> {
response.getWriter().println("this is Controller3");
return null;
};
}
}
public class A4Application {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig1.class);
}
}
自定义HandlerMapping与HandlerAdapter
package com.liubo.springmvc.a4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.*;
import org.springframework.web.servlet.mvc.Controller;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author lb
* @date 2023/1/2 1:36 下午
*/
@Configuration
public class WebConfig {
// 内嵌web容器工厂
@Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
// 创建DispatcherServlet
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
// 注册DispatcherServlet到tomcat
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registrationBean = new DispatcherServletRegistrationBean(dispatcherServlet, "/");
return registrationBean;
}
// SimpleUrlHandlerMapping
@Component
static class MyHandlerMapping implements HandlerMapping {
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
String uri = request.getRequestURI();
Controller controller = controllerMap.get(uri);
if (controller == null){
return null;
}
return new HandlerExecutionChain(controller);
}
@Autowired
private ApplicationContext applicationContext;
private Map<String, Controller> controllerMap;
@PostConstruct
public void init(){
controllerMap = applicationContext.getBeansOfType(Controller.class)
.entrySet().stream().filter(e -> e.getKey().startsWith("/")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
}
// SimpleControllerHandlerAdapter
@Component
static class MyHandlerAdapter implements HandlerAdapter{
@Override
public boolean supports(Object handler) {
return handler instanceof Controller;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof Controller){
((Controller) handler).handleRequest(request, response);
}
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
return 0;
}
}
@Component("/test1")
public static class Controller1 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().println("this is Controller1");
return null;
}
}
@Component("/test2")
public static class Controller2 implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().println("this is Controller2");
return null;
}
}
@Bean("/test3")
public Controller Controller3() {
return (request, response) -> {
response.getWriter().println("this is Controller3");
return null;
};
}
}
public class A4Application {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig1.class);
}
}
RouterFunctionMapping与HandlerFunctionAdapter
@Configuration
public class WebConfig1 {
@Bean // ⬅️内嵌 web 容器工厂
public TomcatServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory(8080);
}
@Bean // ⬅️创建 DispatcherServlet
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean // ⬅️注册 DispatcherServlet, Spring MVC 的入口
public DispatcherServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean
public RouterFunctionMapping routerFunctionMapping() {
return new RouterFunctionMapping();
}
@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {
return new HandlerFunctionAdapter();
}
@Bean
public RouterFunction<ServerResponse> r1() {
return route(GET("/r1"), request -> ok().body("this is r1"));
}
@Bean
public RouterFunction<ServerResponse> r2() {
return route(GET("/r2"), request -> ok().body("this is r2"));
}
}
public class A4Application {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig1.class);
}
}
当浏览器发送一个请求 http://localhost:8080/hello
后,请求到达服务器,其处理流程是:
服务器提供了 DispatcherServlet,它使用的是标准 Servlet 技术
/
,即会匹配到所有请求 URL,可作为请求的统一入口,也被称之为前控制器
DispatcherServlet 会利用 RequestMappingHandlerMapping 查找控制器方法
例如根据 /hello 路径找到 @RequestMapping(“/hello”) 对应的控制器方法
控制器方法会被封装为 HandlerMethod 对象,并结合匹配到的拦截器一起返回给 DispatcherServlet
HandlerMethod 和拦截器合在一起称为 HandlerExecutionChain(调用链)对象
DispatcherServlet 接下来会: