看了尚硅谷雷神的SpringBoot2源码分析,颇有感触,请求处理这部分的源码真的是太精彩了,决定自己在好好的debug几次,再记录下笔记!以前不理解什么叫作“框架 = 设计模式 + 反射 + 注解”,觉得设计模式不就是一些代码风格,反射不就是运行时动态获取类的内部信息并执行其的一些方法,注解不就是定义补充么。但看了SpringBoot源码后才知道如果没有设计模式,有些复杂逻辑的代码不做一些封装处理的话就会变成山代码,效率低、难理解、难扩展。而通过反射,可以使得框架有更高的灵活性和扩展性。使用注解则可以使代码更加简洁。
SpringBoot的特点开箱即用,即内部已经为我们做了大量的配置,包括请求参数解析、数据响应、内容协商等等,如果是用传统的servlet(SpringBoot底层已经封装了tomcat)会变得极其复杂,而springboot我们只需要几个注解就可以完成一个简单的接口。
Example:
@GetMapping("/girl")
public String getGirlFriend(@RequestParam String girlfriend){
return String.format("Get a girlfriend: %s", girlfriend);
}
这样就成功创建了一个简单的接口,使用GetMapping来说明请求方式(“get”)和请求路径(“/girl”), 可以用url拼接参数的方式传参数(“/girl?girlfriend=xxx”)。按照以前sevlet,获取参数需要request.getParameter(“girlfriend”),post方法则要从request.getInputStream()获取BufferReader后再做处理;返回值想要返回json格式,需要用工具类,将对象拼接成json格式,然后用response.getWriter().println(girlfriend)写出,如果是中文还需要进行编码处理。而现在这些繁琐并且重复的操作springboot全做了处理,使我们在开发的过程中只需要注意业务部分的代码实现。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 找到当前请求使用哪个Handler(Controller的方法)处理
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
//HandlerMapping:处理器映射。
....
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
// 挨个尝试HandlerMapping看看是否有请求信息
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter: this.handlerAdapters) {
// 遍历系统中的适配器,看看哪种支持处理当前请求的handler
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// 调用处理器执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
}
ha.handle(…) → AbstractHandlerMethodAdapter.handleInternal(…) →protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
...
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
...
// 判断解析器是否支持解析该参数
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
}
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
...
// 解析指定参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
...
}
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取可以解析该参数的参数解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
// 解析方法的参数
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
又。。。在getArgumentResolver()里获取合适的解析器,感觉这边做的有点重复了(里面校验是否支持参数解析器),找到了参数解析器后再执行解析器的resolveArgument(…)public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 参数名称信息对象
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 获取参数名称
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 根据参数名获取参数值
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
....
return arg;
}
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
...
// 获取参数名称
String name = ModelFactory.getNameForParameter(parameter);
...
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}else {
// 创建属性实例, 即需要绑定属性的对象
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
...
}
}
if (bindingResult == null) {
// 创建数据绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
// 将webRequest中的请求数据绑定在对象中
bindRequestParameters(binder, webRequest);
}
...
}
...
}
// 将数据绑定在ModelAndViewContainer中
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
这里面有个WebDataBinder,即数据绑定器,用于将请求参数的值绑定到指定的JavaBean里面,利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中@Override
public void addFormatters(FormatterRegistry registry) {
// 用lambda会报错
// 添加一个格式转换器
registry.addConverter(new Converter<String, Pet>() {
@Override
public Pet convert(String source) {
if (StringUtils.hasLength(source)){
Pet pet = new Pet();
String[] args = source.split(",");
pet.setName(args[0]);
pet.setAge(Integer.valueOf(args[1]));
return pet;
}
return null;
}
});
WebMvcConfigurer.super.addFormatters(registry);
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
// 获取目标方法
Method method = getBridgedMethod();
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
// 执行目标方法
return method.invoke(getBean(), args);
}
catch (InvocationTargetException ex) {
...
}
}
数据响应分为响应页面和响应数据,页面一般都是用重定向或者请求转发,响应数据就是将指定的字符串、json、xml等特定格式返回
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//执行目标方法,并获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
...
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
...
// 处理返回参数
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
...
}
HandlerMethodReturnValueHandlerComposite.handleReturnValue(…)public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 获取匹配的返回值处理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 用得到的返回值处理器处理返回结果
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
// 使用MessageConverters转换数据后将数据写出
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
由于我们这个是@ResponseBody的返回值处理器,这个处理器会调用RequestResponseBodyMethodProcessor的writeWithMessageConverters(…)来处理参数protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
// 获取请求需要返回的媒体类型
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders()
// 服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
...
// SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
// 判断该转换器能否转换数据
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) {
...
if (body != null) {
...
if (genericConverter != null) {
// 使用指定的转换器将数据写入body中
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
// 使用默认的转换器写入body
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
...
return;
}
}
}
...
}
每个converter实现了HttpMessageConverter接口,接口里有read()、writer()方法等,convter需要重写这些方法,将制定参数按照特定的方法写出。一般可以重写的都可以让我们自定义,我们可以自定义一些Convter放到SpringMVC的配置中,会在内容协商部分详细say。内容协商主要做的就是根据客户端的需求返回制定格式的数据(json、xml。。。)
在分析内容原理之前,得特别提一下springboot对内容协商的配置
spring:
mvc:
contentnegotiation:
favor-parameter: true #开启请求参数内容协商模式
做了这样的配置之后只需要在url的参数中带着format参数,如
部分原理在上述数据响应的有提过,所以会有一点重复
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
...
// 进行内容协商的最佳匹配
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
...
}
实现多协议数据兼容: json、xml、x-rex。从原理上看,只需要在WebMvcConfigurer加入自定义转换器。代码如下
MyMessageConverter.java
/**
* Description: 自定义Converter
*
* @author rex
* @date 2022-09-25 16:37
*/
public class MyMessageConverter implements HttpMessageConverter<Person> {
/**
* 读取数据的规则
*/
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
/**
* 写入数据的规则
*/
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Person.class);
}
/**
* 服务器要统计MessageConverter都能写出那些类型
* application/x-rex
*/
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-rex");
}
/**
* 读取数据
*/
@Override
public Person read(Class<? extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
/**
* 写入数据
*/
@Override
public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//自定义内容写出
String data = person.getName() + ";" + person.getAge() + ";" + person.getBirth();
outputMessage.getBody().write(data.getBytes());
}
}
WebConfig.java
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 加入自定义的消息转换器
converters.add(new MyMessageConverter());
}
}