计算机毕设项目源码获取:
📢链接点击直达:Java 项目合作-CSDN博客
一、源码解析
这是spring 4.2新加的两个接口
1、RequestBodyAdvice类
public interface RequestBodyAdvice {
boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType);
HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;
Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
@Nullable
Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
}
查看一下谁调用了这个接口的这些方法,可以看到AbstractMessageConverterMethodArgumentResolver的readWithMessageConverters()方法调用了这个接口的方法。
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message = null;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
"RequestBodyAdvice方法调用"
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
finally {
if (message != null && message.hasBody()) {
closeStreamIfNecessary(message.getBody());
}
}
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType,
getSupportedMediaTypes(targetClass != null ? targetClass : Object.class));
}
MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
});
return body;
}
//
private final RequestResponseBodyAdviceChain advice;
RequestResponseBodyAdviceChain getAdvice() {
return this.advice;
}
可以看到这接口的方法,主要是在HttpMessageConverter处理request body的前后做一些处理和body为空的时候做处理。
从这个可以看出,我们可以在使用这些HandlerMethodArgumentResolver的时候,我们能对request body进行前处理和解析后处理。
2、ResponseBodyAdvice类
ublic interface ResponseBodyAdvice<T> {
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}
调用解析:
AbstractMessageConverterMethodProcessor.class
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
// 处理请求协商类型
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
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)) {
// 调用ResponseBodyAdvice
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
if (body != null) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
此可以对@ResponseBody的返回结果在输出到响应之前做处理。通过泛型,指定需要被“拦截”的响应体对象类型。该接口的实现会在 Controller 方法返回数据,并且匹配到了 HttpMessageConverter 之后,HttpMessageConverter 进行序列化之前执行。可以通过覆写 beforeBodyWrite 来统一的对响应体进行修改。
二、为什么说要加上@ControllerAdvice注解,且实现RequestBodyAdvice或ResponseBodyAdvice接口
现在来分析为什么需要实现RequestBodyAdvice接口的同时要加上ControllerAdvice注解。
1、为什么要实现RequestBodyAdvice或ResponseBodyAdvice接口?简单来说,要想执行afterBodyRead方法,必须实现ResponseBodyAdvice接口。RequestResponseBodyAdviceChain的afterBodyRead方法:调用getMatchingAdvice方法,获取RequestBodyAdvice类型的advice其中:class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
// 此advice就是我们定义的类
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
//如果supports方法的返回值为true,则执行RequestBodyAdvice的afterBodyRead方法
if (advice.supports(parameter, targetType, converterType)) {
request = advice.beforeBodyRead(request, parameter, targetType, converterType);
}
}
return request;
}
getMatchAdvice:获取RequestBodyAdvice类型的advice(此advice是我们定义的),如果不是RequestBodyAdvice类型就不会加到结果集,所以这就是我们实现RequestBodyAdvice的原因
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {
;//获取RequestBodyAdvice类型的advice(此advice是我们定义实现RequestBodyAdvice接口的类)
List<Object> availableAdvice = getAdvice(adviceType);
if (CollectionUtils.isEmpty(availableAdvice)) {
return Collections.emptyList();
}
List<A> result = new ArrayList<>(availableAdvice.size());
for (Object advice : availableAdvice) {
if (advice instanceof ControllerAdviceBean) {
ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {
continue;
}
// 返回的是我们定义的Advice,即根据Bean的名称从BeanFactory中获取Bean对象
advice = adviceBean.resolveBean();
}
// 判断这个类是否是RequestBodyAdvice类型,如果不是就不会加到结果集,所以这就是我们实现RequestBodyAdvice的原因
if (adviceType.isAssignableFrom(advice.getClass())) {
result.add((A) advice);
}
}
return result;
}
getAdvice方法:
private List<Object> getAdvice(Class<?> adviceType) {
if (RequestBodyAdvice.class == adviceType) {
return this.requestBodyAdvice;
} else if (ResponseBodyAdvice.class == adviceType) {
return this.responseBodyAdvice;
} else {
throw new IllegalArgumentException("Unexpected adviceType: " + adviceType);
}
}
resolveBean方法:this.beanOrName是string类型的,从beanFactory中再拿到对应的bean对象。
public Object resolveBean() {
if (this.resolvedBean == null) {
Object resolvedBean = this.obtainBeanFactory().getBean((String)this.beanOrName); // obtainBeanFactory()返回BeanFactory对象
if (!this.isSingleton) {
return resolvedBean;
}
this.resolvedBean = resolvedBean;
}
return this.resolvedBean;
}
2、为什么要加上@ControllerAdvice注解?简单说,只有加上@ControllerAdvice,才能找到ControllerAdviceBean。HandlerAdapter字面上的意思就是处理适配器,它的作用用一句话概括就是调用具体的方法对用户发来的请求来进行处理。当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据controller对应的controller类型来调用相应的HandlerAdapter来进行处理。
RequestMappingHandlerAdapter就比较复杂了,可以说,该类是整个SpringMVC中最复杂的类了,却也是目前SpringMVC中使用到的频率最高的类。目前在SpringMVC的使用过程中,对请求的处理主要就是依赖RequestMappingHandlerMapping和RequestMappingHandlerAdapter类的配合使用。下面重点介绍下RequestMappingHandlerAdapter类
RequestMappingHandlerAdapter初始化流程:
RequestMappingHandlerAdapter实现了InitializingBean接口,Spring容器会自动调用其afterPropertiesSet方法。
public void afterPropertiesSet() {
this.initControllerAdviceCache(); // 获取ControllerAdviceBean的集合
List handlers;
if (this.argumentResolvers == null) {
"把获取的ControllerAdviceBean集合赋值到调用的对象中,调用RequestBodyAdvice的地方"
handlers = this.getDefaultArgumentResolvers();
this.argumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
}
if (this.initBinderArgumentResolvers == null) {
handlers = this.getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
}
if (this.returnValueHandlers == null) {
"把获取的ControllerAdviceBean集合赋值到调用的对象中,调用ResponseBodyAdvice的地方"
handlers = this.getDefaultReturnValueHandlers();
this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(handlers);
}
}
- argumentResolvers:用于给处理器方法设置参数
- initBinderArgumentResolvers:用于给注释了@InitBinder方法设置参数
- returnValueHandlers:用于对处理器返回值进行相应处理
(1) 获取@ControllerAdvice注解的类封装为ControllerAdviceBean的集合,遍历ControllerAdviceBean集合
提取没有@RequestMapping注解标注的,但有@ModelAttribute注解标注的方法
提取@InitBinder注解标注的方法
提取实现了RequestBodyAdvice或ResponseBodyAdvice接口的类
(2) 没有设置argumentResolvers则获取默认的HandlerMethodArgumentResolver
(3) 没有设置initBinderArgumentResolvers则获取默认的处理参数绑定的HandlerMethodArgumentResolver
(4) 没有设置returnValueHandlers则获取默认的HandlerMethodReturnValueHandler
RequestMappingHandlerAdapter的getDefaultReturnValueHandlers方法:初始化了RequestResponseBodyMethodProcessor
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
// requestResponseBodyAdvice 赋值
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
if (KotlinDetector.isKotlinPresent()) {
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
}
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
// requestResponseBodyAdvice赋值
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ServletModelAttributeMethodProcessor(false));
// requestResponseBodyAdvice赋值
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ServletModelAttributeMethodProcessor(true));
}
return handlers;
}
requestResponseBodyAdvice:用来保存实现了RequestBodyAdvice或ResponseBodyAdvice接口的类
RequestMappingHandlerAdapter类的initControllerAdviceCache方法:初始化List集合requestResponseBodyAdvice
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 获取有@ControllerAdvice注解的Bean的集合
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean); // 将ControllerAdviceBean放入List集合中
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
// 将存有ControllerAdviceBean的集合存入requestResponseBodyAdvice集合中
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
if (logger.isDebugEnabled()) {
int modelSize = this.modelAttributeAdviceCache.size();
int binderSize = this.initBinderAdviceCache.size();
int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
logger.debug("ControllerAdvice beans: none");
}
else {
logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
}
}
}
ControllerAdviceBean(有@ControllerAdvice注解的Bean)的findAnnotatedBeans方法:获得所有的ControllerAdvice类,之后封装为ControllerAdviceBean
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
List<ControllerAdviceBean> adviceBeans = new ArrayList<>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)) {
if (!ScopedProxyUtils.isScopedTarget(name)) {
ControllerAdvice controllerAdvice = context.findAnnotationOnBean(name, ControllerAdvice.class);
if (controllerAdvice != null) {
// Use the @ControllerAdvice annotation found by findAnnotationOnBean()
// in order to avoid a subsequent lookup of the same annotation.
adviceBeans.add(new ControllerAdviceBean(name, context, controllerAdvice));
}
}
}
OrderComparator.sort(adviceBeans);
return adviceBeans;
}
会从applicationContext中获取有ControllerAdvice注解的bean