HandlerMethod、InvocableHandlerMethodの使用を原理的に把握【Spring MVCを共に学ぶ】
ひとつひとつ
火影になりたい人は近道がなく、火影になった人も退く道がない.
前言
私がこのように「ぶらぶら」していることを通じて、あなたはそれが相対的に重要なクラスだと思いますか?あなたが信じても信じなくても、どうせ私はそう思っています.
HandlerMethod
その継承木を見てみましょう.主に2つのサブクラスがあります.
InvocableHandlerMethod
これは
最後の
このサブクラスが主に提供する能力は、
ServletInvocableHandlerMethod
これは対 の戻り値 非同期処理結果に対する処理 使用例
実行します.印刷結果は次のとおりです.
この結果は異なるタイプに対応するデフォルトの
まとめ
最後に、あなたが関心を持っていないかもしれない小さな詳細をヒントにします. である. に存在します
関連読書
【小家Spring】Spring MVCコンテナのweb 9大コンポーネントの---HandlerAdapterソースコードの詳細---戻り値プロセッサHandlerMethodReturnValueHandlerを読む記事
知識交流
=The last:本文があなたに役に立つと思ったら、いいねを押してもいいですよ.もちろんあなたの友达の輪を分かち合ってもっと多くの友达に見せても
**技術内容に興味がある場合は、wx群交流:
記事
原文リンク-原文リンク-原文リンク
火影になりたい人は近道がなく、火影になった人も退く道がない.
前言
HandlerMethod
それはSpring MVC
の非公開APIとして、ほとんどのパートナーはそれに慣れていないかもしれませんが、私はあなたがそれに対してそんなに疎かではないと信じています.あなたは使ったことがないかもしれませんが、見たことがあるかもしれません.例えばSpring MVC
のブロックHandlerInterceptor
のブロック方法の3番目のパラメータObject handler
は、Objectタイプであるが、ほとんどの場合、HandlerMethod
として使用される.例えば、私の前のこのRequestMappingHandlerMappingの記事はもHandlerMethod
というクラスに大量に言及しました.私がこのように「ぶらぶら」していることを通じて、あなたはそれが相対的に重要なクラスだと思いますか?あなたが信じても信じなくても、どうせ私はそう思っています.
HandlerMethod
それはSpring MVC
を理解するのに欠かせないクラスであり、Spring MVC
のカスタマイズに参加して無視できない重要なAPIと言えるでしょう.HandlerMethod
HandlerMethod
インタフェースでも抽象クラスでもpublicでもありません.HandlerMethod
には多くの属性がカプセル化されており、リクエストメソッドにアクセスする際にメソッド、メソッドパラメータ、メソッド上の注釈、所属クラスなどに容易にアクセスでき、メソッドパラメータの注釈などの情報にも容易にアクセスできる.// @since 3.1
public class HandlerMethod {
// Object , Bean, BeanName
private final Object bean;
// BeanName, Bean ~
@Nullable
private final BeanFactory beanFactory;
private final Class> beanType; //
private final Method method; //
private final Method bridgedMethod; // , method , method
// ,** MethodParameter **
// MethodParameter Spring
private final MethodParameter[] parameters;
@Nullable
private HttpStatus responseStatus; // http ( )
@Nullable
private String responseStatusReason; // , null
// createWithResolvedBean() handlerMethod handlerMethod。
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
// ** ** ( ,List+ )
@Nullable
private volatile List interfaceParameterAnnotations;
//
public HandlerMethod(Object bean, Method method) {
...
this.beanType = ClassUtils.getUserClass(bean);
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
...
evaluateResponseStatus();
}
// NoSuchMethodException
public HandlerMethod(Object bean, String methodName, Class>... parameterTypes) throws NoSuchMethodException {
...
this.method = bean.getClass().getMethod(methodName, parameterTypes);
this.parameters = initMethodParameters();
...
evaluateResponseStatus();
}
// BeanName
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
...
// : BeanName
Class> beanType = beanFactory.getType(beanName);
if (beanType == null) {
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
}
this.parameters = initMethodParameters();
...
evaluateResponseStatus();
}
// copy
protected HandlerMethod(HandlerMethod handlerMethod) { ... }
// :initMethodParameters evaluateResponseStatus
// , HandlerMethodParameter
// : ~~~
private MethodParameter[] initMethodParameters() {
int count = this.bridgedMethod.getParameterCount();
MethodParameter[] result = new MethodParameter[count];
for (int i = 0; i < count; i++) {
HandlerMethodParameter parameter = new HandlerMethodParameter(i);
GenericTypeResolver.resolveParameterType(parameter, this.beanType);
result[i] = parameter;
}
return result;
}
// @ResponseStatus ( )
// ,
// , code reason , ~~~
// code HttpStatus.INTERNAL_SERVER_ERROR-->(500, "Internal Server Error")
private void evaluateResponseStatus() {
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
this.responseStatus = annotation.code();
this.responseStatusReason = annotation.reason();
}
}
... // get ( set )
// MethodParameter
public MethodParameter getReturnType() {
return new HandlerMethodParameter(-1);
}
// 。 : Object, return “fsx”
// Object.class,
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return new ReturnValueMethodParameter(returnValue);
}
// void
public boolean isVoid() {
return Void.TYPE.equals(getReturnType().getParameterType());
}
//
// ServletInvocableHandlerMethod ~~~
@Nullable
public A getMethodAnnotation(ClassannotationType) {
return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType);
}
publicboolean hasMethodAnnotation(ClassannotationType) {
return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
}
//resolvedFromHandlerMethodは するしかありませんが、 には ドラム び しの の で を けています。
@Nullable
public HandlerMethod getResolvedFromHandlerMethod() {
return this.resolvedFromHandlerMethod;
}
//stringタイプのBeanNameによってBeanを して、もう1つのHandlerMethodを して~~~これが りですか
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
public String getShortLogMessage() {
return getBeanType().getName() + "#" + this.method.getName() + "[" + this.method.getParameterCount() + " args]";
}
//この は クラスHandlerMethodParameterに されています~~データ は です
private List getInterfaceParameterAnnotations() {
List parameterAnnotations = this.interfaceParameterAnnotations;
if (parameterAnnotations == null) {
parameterAnnotations = new ArrayList<>();
//このメソッドが するクラスのすべての を るインタフェースたち(N のインタフェースを できますか)
for (Class> ifc : this.method.getDeclaringClass().getInterfaces()) {
//getMethods: インタフェースを むすべてのpublicメソッドは されません。
for (Method candidate : ifc.getMethods()) {
//このインタフェースメソッドがちょうど のmethod であるかどうかを ~~~
//ちょうど の ですので、 してインタフェースの としてマークします~~~
if (isOverrideFor(candidate)) {
//getParameterAnnotationsは2 を します~~~~
//パラメータが あるため、 パラメータの に の を けることができます
parameterAnnotations.add(candidate.getParameterAnnotations());
}
}
}
this.interfaceParameterAnnotations = parameterAnnotations;
}
return parameterAnnotations;
}
// クラスのキーステップを る
protected class HandlerMethodParameter extends SynthesizingMethodParameter {
@Nullable
private volatile Annotation[] combinedAnnotations;
...
// は でしか てませんが、ここではインタフェースレベルをサポートしています~~~
@Override
public Annotation[] getParameterAnnotations() {
Annotation[] anns = this.combinedAnnotations;
if(anns==null){//いずれも だけ する がある
anns = super.getParameterAnnotations();
int index = getParameterIndex();
if(index>=0){// があってこそ する があるのか
for (Annotation[][] ifcAnns : getInterfaceParameterAnnotations()) {
if (index < ifcAnns.length) {
Annotation[] paramAnns = ifcAnns[index];
if (paramAnns.length > 0) {
List merged = new ArrayList<>(anns.length + paramAnns.length);
merged.addAll(Arrays.asList(anns));
for (Annotation paramAnn : paramAnns) {
boolean existingType = false;
for (Annotation ann : anns) {
if (ann.annotationType() == paramAnn.annotationType()) {
existingType = true;
break;
}
}
if (!existingType) {
merged.add(adaptAnnotation(paramAnn));
}
}
anns = merged.toArray(new Annotation[0]);
}
}
}
}
this.combinedAnnotations = anns;
}
return anns;
}
}
// り の のタイプ~~~
private class ReturnValueMethodParameter extends HandlerMethodParameter {
@Nullable
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);//ここに わる-1よ~~~0より さいのは がある
this.returnValue = returnValue;
}
...
// り タイプreturnValueを えばいい~~~
@Override
public Class> getParameterType() {
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
}
}
}
HandlerMethod
は属性が非常に多く、提供する能力も強いことがわかります.しかし、パートナーが発見したかどうかは分かりませんが、目標のMethod
を持っていますが、invoke
を実行する能力を提供していません.もしあなたが実行するなら、自分でMethod
を自分で実行しなければなりません.HandlerMethod
データの準備、データのカプセル化だけを担当し、具体的な使用方法を提供しません.その継承木を見てみましょう.主に2つのサブクラスがあります.
InvocableHandlerMethod
とServletInvocableHandlerMethod
です.名前から彼ら2人はinvoke
の呼び出し能力を持っていることがわかります.InvocableHandlerMethod
これは
HandlerMethod
の拡張であり、呼び出し能力を増加させた.この能力はSpring MVC
で非常に重要であり、呼び出すときに、方法のパラメータをカプセル化することができる(HTTP request
から、もちろんHandlerMethodArgumentResolver
を借りている)// @since 3.1
public class InvocableHandlerMethod extends HandlerMethod {
private static final Object[] EMPTY_ARGS = new Object[0];
// , 、 ~~~
// 、
@Nullable
private WebDataBinderFactory dataBinderFactory;
// HandlerMethodArgumentResolver
private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
//
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
... // super
// set ~~~ get
// : ~~~
// 。 : ( path ,requestParam 、 HttpSession )
// providedArgs : , doInvoke()
//( , ~)
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// , , `HandlerMethodArgumentResolver`
// ~~~
// : ParameterNameDiscoverer, 。
// value , ok ~
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) { // trace , ~
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
// doInvoke() ,
// ReflectionUtils.makeAccessible(getBridgedMethod());
// return getBridgedMethod().invoke(getBean(), args);
}
最後の
invoke()
について、ここで実行するターゲットメソッドgetBean()ですよ~~~このサブクラスが主に提供する能力は、
invoke
がターゲットBean
を呼び出すターゲットメソッドを提供する能力であり、この呼び出しの過程で多くの文章を書くことができ、もちろん最も核心的な論理は様々なHandlerMethodArgumentResolver
が完成し、詳細は以下を参照してください.InvocableHandlerMethod
というサブクラスは呼び出し能力を提供しているが、Servlet
のAPIとバインドされていない.結局、Spring
自身が通用するNativeWebRequest
を使用しているので、soはもう一つのサブクラスがこのことをしていると考えやすい.ServletInvocableHandlerMethod
これは
InvocableHandlerMethod
への拡張であり、戻り値と応答ステータスコードを増加させる処理であり、またServletInvocableHandlerMethod
に内部クラスConcurrentResultHandlerMethod
が継承され、異常呼び出し結果処理をサポートし、Servlet
コンテナの下でController
がアダプタを検索する際に呼び出しを開始する最終的にServletInvocableHandlerMethod
である.public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
//
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
//
// HandlerMethodReturnValueHandler
public void setHandlerMethodReturnValueHandlers(HandlerMethodReturnValueHandlerComposite returnValueHandlers) {
this.returnValueHandlers = returnValueHandlers;
}
// , invokeForRequest invokeForRequest
// ~~~
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// HttpServletResponse @ResponseStatus#code()
setResponseStatus(webRequest);
// :mavContainer.setRequestHandled(true);
if (returnValue == null) {
// Request NotModified true @ResponseStatus RequestHandled=true ,
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
// null,@ResponseStatus reason
} else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// , RequestHandled=false
// HandlerMethodReturnValueHandlerComposite
// @ResponseStatus ~~~~~
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// , :https://blog.csdn.net/f641385712/article/details/90370542
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
// HttpServletResponse
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
HttpStatus status = getResponseStatus();
if (status == null) { // ResponseStatus.code()
return;
}
HttpServletResponse response = webRequest.getResponse();
if (response != null) {
String reason = getResponseStatusReason();
// : reason, sendError 200 ~
if (StringUtils.hasText(reason)) {
response.sendError(status.value(), reason);
} else {
response.setStatus(status.value());
}
}
// request , 。 redirect
// To be picked up by RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}
private boolean isRequestNotModified(ServletWebRequest webRequest) {
return webRequest.isNotModified();
}
// RequestMappingHandlerAdapter
ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
}
//
private class ConcurrentResultMethodParameter extends HandlerMethodParameter {
@Nullable
private final Object returnValue;
private final ResolvableType returnType;
public ConcurrentResultMethodParameter(Object returnValue) {
super(-1);
this.returnValue = returnValue;
// List
this.returnType = (returnValue instanceof ReactiveTypeHandler.CollectedValuesList ?
((ReactiveTypeHandler.CollectedValuesList) returnValue).getReturnType() :
ResolvableType.forType(super.getGenericParameterType()).getGeneric());
}
// List List
@Override
public Class> getParameterType() {
if (this.returnValue != null) {
return this.returnValue.getClass();
}
if (!ResolvableType.NONE.equals(this.returnType)) {
return this.returnType.toClass();
}
return super.getParameterType();
}
//
@Override
public Type getGenericParameterType() {
return this.returnType.getType();
}
// ResponseEntity>, @ResponseBody-style reactive
// reactive
@Override
public boolean hasMethodAnnotation(Class annotationType) {
// Ensure @ResponseBody-style handling for values collected from a reactive type
// even if actual return type is ResponseEntity>
return (super.hasMethodAnnotation(annotationType) ||
(annotationType == ResponseBody.class && this.returnValue instanceof ReactiveTypeHandler.CollectedValuesList));
}
}
// ( )
private class ConcurrentResultHandlerMethod extends ServletInvocableHandlerMethod {
//
private final MethodParameter returnType;
// handler Callable
// result
public ConcurrentResultHandlerMethod(final Object result, ConcurrentResultMethodParameter returnType) {
super((Callable
HandlerMethod
は、Handler
をカプセル化し、要求を処理するためのMethod
である.InvocableHandlerMethod
は、メソッドパラメータの解析およびメソッドの呼び出し能力を増加させる.ServletInvocableHandlerMethod
これに基づいて、次の3つの能力が追加されました.@ResponseStatus
注釈のサポート 1. `@ResponseStatus` ,** **。 ** , returnValue=null reason **( null “”), ( )
returnValue
に対する処理 1. `HandlerMethodReturnValueHandlerComposite`
HandlerMethod
は API
として、直接使うなら、少し苦労します.しかし、本稿ではDemo
を提供し、パートナーたちが最も関心を持っていることも最も有用なニーズです.ModelFactory.getNameForParameter(parameter)
という静的方法は、パラメータにデフォルト名を生成することです.もちろん、デフォルト処理スキームの最下層はConventions.getVariableNameForParameter(parameter)
に依存しています.この対象、Object、Listなどの一般的なデータ構造のデフォルト処理を検証するために、ここでは、HandlerMethod
を使用して、この結論を一度にすべて印刷します.@Getter
@Setter
@ToString
public class Person {
@NotNull
private String name;
@NotNull
@Positive
private Integer age;
public Object demoMethod(Person person, Object object,
List intList, List personList,
Set intSet, Set personSet,
Map myMap,
String name, Integer age,
int number, double money) {
return "hello parameter";
}
}
HandlerMethod
を使用して、このテストケースを完了します. public static void main(String[] args) {
// HandlerMethod
HandlerMethod handlerMethod = new HandlerMethod(new Person(), getPersonSpecfyMethod());
//
MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
for (MethodParameter parameter : methodParameters) {
Class> parameterType = parameter.getParameterType();
String nameForParameter = ModelFactory.getNameForParameter(parameter);
System.out.println(" " + parameterType.getName() + "---> modelKey :" + nameForParameter);
}
}
private static Method getPersonSpecfyMethod() {
for (Method method : Person.class.getMethods())
if (method.getName().equals("demoMethod"))
return method;
return null;
}
実行します.印刷結果は次のとおりです.
com.fsx.bean.Person---> modelKey :person
java.lang.Object---> modelKey :object
java.util.List---> modelKey :integerList
java.util.List---> modelKey :personList
java.util.Set---> modelKey :integerList // set List
java.util.Set---> modelKey :personList
java.util.Map---> modelKey :map
java.lang.String---> modelKey :string
java.lang.Integer---> modelKey :integer
int---> modelKey :int
double---> modelKey :double
この結果は異なるタイプに対応するデフォルトの
ModelKey
であり、これは`@SessionAttribute、@ModelAttribute`を理解し、正しく使用する上で重要であることを覚えておいてほしい.まとめ
HandlerMethod
接触は少ないが、その重要性には影響しない.Spring MVC
の処理フローを理解する上で重要であり、使用者との関係が大きいブロッカーHandlerInterceptor
のカスタマイズ処理を習得する際には、同様に使用することを学ぶ必要がある.最後に、あなたが関心を持っていないかもしれない小さな詳細をヒントにします.
HandlerMethod
はorg.springframework.web.method
包下に位置する、3.1後にしかないMethodParameter
は、org.springframework.core
コアパッケージ内に配置される.2.0
は関連読書
【小家Spring】Spring MVCコンテナのweb 9大コンポーネントの---HandlerAdapterソースコードの詳細---戻り値プロセッサHandlerMethodReturnValueHandlerを読む記事
知識交流
=The last:本文があなたに役に立つと思ったら、いいねを押してもいいですよ.もちろんあなたの友达の輪を分かち合ってもっと多くの友达に見せても
~
==**技術内容に興味がある場合は、wx群交流:
Java 、 3
に参加してください.グループQRコードが無効になった場合は、wx番号:fsx641385712
(または下のwx QRコードをスキャン)を追加します.さらに備考:"java "
の文字は、手動でグループ**に招待されます.記事
または
の場合は`:原文リンク-原文リンク-原文リンク