Spring MVC学習ノートのSpring MVCコンポーネントHandlerMapping(二)
66786 ワード
1、はじめに
前の「Spring MVCコンポーネントHandlerMapping(一)」では、HandlerMappingコンポーネントの全体論理とAbstractUrlhandlerMappingシリーズの実現方法を分析した.このセクションでは、Abstracthandler MethodMappingシリーズの実現方法を分析します.
2、Abstract Handler MethodMappingシステム
Abstract Handler MethodMappingシステムにおいて、Abstracthandleingは三つの種類しかありません.それぞれAbstract Handler MethMapping、Request MappingInfoler Mapping、Request MappingHandlerMappingという三つの種類が順次前の種類に継承されています.InitializingBeanインターフェースを実現すると、Beaビンの実装後にafterPropertiesset()メソッドを呼び出します.
Abstracthandler MethodMappingシステムでは、クラスの階層構造は比較的簡単で明確ですが、SpringはAbstracthandlerMethodMappingシステムの柔軟性を保証するために、論理は複雑です.Abstract Handler MethMapping類を分析する前に、私達はまずその中のいくつかの核心の基礎種類を認識します. HandlerMethodは、プロセッサに対応する方法およびインスタンスBeanを含む方法に基づくプロセッサをカプセル化し、いくつかのアクセス方法パラメータ、方法リターン値、方法注釈などの方法を提供する. Request MappingInfoは要求情報を表し、マッピング関係の整合条件をカプセル化した.@Request Mappingの注釈を使う時、配置の情報は最後にすべてRequest MappingInfoに設定しました.@Request Mappingの注釈の異なる属性は対応するXREquest Coditionにマッピングされます. Abstracthandler MethodMapping-内部種類MatchはHandlerMethodと汎型Tをカプセル化しました.汎型Tは実際にRequest MappingInfoを表しています. 内部クラスMappingRegistrationは、マッピング関係登録時の情報を記録する.HandlerMethod、汎型T、mappingName、directUrls属性(保存urlとRequest MappingInfoの対応関係、複数のurlは同一のmappingInfoに対応しているかもしれません) を実装しました.内部クラスのMappingRegistryは主にいくつかのMapを維持し、マッピングされた情報を格納するために使用されます.この内部クラスとその定義のマッピング関係を詳細に説明する.
3、Abstract Handler MethodMapping抽象類
上記のMatch、MappingRegistration、MappingRegistryはこのクラスにあります.その中でMappingRegistryが最も重要です.まずこの内部種類を分析します.これらの中でいくつかのMappingRegistryが含まれています.
3.1、MappingRegistry内部類
MappingRegistry内部類は主にT(Request MappingInfo)、mappingName、HandlerMethod、CorsCorsCofigrationなどの対象間のマッピング関係を維持し、同時に読み書きロックreadWriteLockの対象を提供しました.
1、属性
3.2、初期化
前に述べたAbstract HandlerMethodMappingはInitializingBeanインターフェースを実現していますので、Beanの実装後にafterPropertiessetを呼び出すことができます.実は、Abstract Handler MethodMapping類の初期化作業はこの場所から始まっています.コードは以下の通りです
3.3、get Handler Internal()方法
前にAbstract Handler MethodMapping類の初期化方法を分析しましたが、今からこの種類がどのように親類のgetHandler Internalを実現するかを分析します.
Request MappingInfolerMapping抽象類はAbstractHandlerMethodMapping類から継承され、その中の汎型がRequest MappingInfoであることを明らかにしました.
_; はRequest MappingInfoHandlerMapping抽象類において、以下のいくつかのことをした.
1、Optionsのデフォルトの処置方法を定義していますが、HTTP_に関するものがあります.OPTIONHANDLE_METHOD定数、内部クラスHttpOptionshandler、および静的コードブロック初期化定数.
2、コンストラクタは、コンストラクタにマッピングのネーミングポリシー、すなわちRequest MappingInfoHandlerMethodMappingNamingStrategyを設定して実現する方式です.
3、親類のいくつかの方法を実現しました.
5、親類を書き換えたhandleNoMatch()方法では、内部クラスのPartar Match Helperを使用して不一致の原因を判断し、指定された異常を投げる.コードを貼り付けていません.
5、Request MappingHandlerMapping類
レイクマットMappling HandlerMapping類は、Request MappingInfo HandlerMappingを継承したほか、Match able HandlerMappingとEmbededValueResourAwareの2つのインターフェースを実現しました.この中で、Match atleHandlerMappingインターフェースを実現する方法は、まだ使われていません.EmbodValue ResourAwareインターフェースを実現しました.解析String文字列をサポートするということです.
定義された属性:
前に、afterPropertiesset()方法が初期化を実現する方法であることを知っています.Abstracthandler MethodMappingの抽象的な種類の中で、hander種類と方法の検査と登録を実現しました.Request MappingHandlerMapping類では、Request MappingInfo構築構成構成の初期化が追加されました.コードは以下の通りです.
6、まとめ
この文章の中で、私達はAbstracthandlerMethMappingシステムが実現したHandlerMappingの中の3つの種類の基本的な実現を分析しただけです.その中にRequest CoditionとRequest MappingInfo(Request Coditionのサブクラスでもあります.)やHandleMethodなどの具体的な学習過程が含まれています.
前の「Spring MVCコンポーネントHandlerMapping(一)」では、HandlerMappingコンポーネントの全体論理とAbstractUrlhandlerMappingシリーズの実現方法を分析した.このセクションでは、Abstracthandler MethodMappingシリーズの実現方法を分析します.
2、Abstract Handler MethodMappingシステム
Abstract Handler MethodMappingシステムにおいて、Abstracthandleingは三つの種類しかありません.それぞれAbstract Handler MethMapping、Request MappingInfoler Mapping、Request MappingHandlerMappingという三つの種類が順次前の種類に継承されています.InitializingBeanインターフェースを実現すると、Beaビンの実装後にafterPropertiesset()メソッドを呼び出します.
Abstracthandler MethodMappingシステムでは、クラスの階層構造は比較的簡単で明確ですが、SpringはAbstracthandlerMethodMappingシステムの柔軟性を保証するために、論理は複雑です.Abstract Handler MethMapping類を分析する前に、私達はまずその中のいくつかの核心の基礎種類を認識します.
3、Abstract Handler MethodMapping抽象類
上記のMatch、MappingRegistration、MappingRegistryはこのクラスにあります.その中でMappingRegistryが最も重要です.まずこの内部種類を分析します.これらの中でいくつかのMappingRegistryが含まれています.
3.1、MappingRegistry内部類
MappingRegistry内部類は主にT(Request MappingInfo)、mappingName、HandlerMethod、CorsCorsCofigrationなどの対象間のマッピング関係を維持し、同時に読み書きロックreadWriteLockの対象を提供しました.
1、属性
// mapping MappingRegistration
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// mapping HandlerMethod
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// URL (mapping) ,key URL,value list 。
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
// name HandlerMethod ( name HandlerMethod)
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
// HandlerMethod
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
//
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
2、register()方法、unregister()方法は主にregister()方法、unregister()方法でT(Request MappingInfo)、mappingName、HandlerMethod、CorsCorsCorsConsCofigrationなどのオブジェクト間のマッピング関係を登録します.ここで、register()の方法コードは以下の通りである.public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
//
this.readWriteLock.writeLock().lock();
try {
// HandlerMethod ( )
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// handlerMethod mapping , HandlerMethod handlerMethod , “Ambiguous mapping. Cannot map”
validateMethodMapping(handlerMethod, mapping);
// mappingLookup , mapping handlerMethod
this.mappingLookup.put(mapping, handlerMethod);
// mapping, URL( URL), URL getMappingPathPatterns() , PatternsRequestCondition , directUrls ? 。
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
// mappingName, handlerMethod
if (getNamingStrategy() != null) {
//namingStrategy RequestMappingInfoHandlerMethodMappingNamingStrategy ( RequestMappingInfoHandlerMapping ), : +“#”+
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
// CorsConfiguration , handlerMethod 。initCorsConfiguration() , RequestMappingHandlerMapping 。
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// mapping MappingRegistration
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
一方、unregister()方法は主にmapping整合条件を除去し、他のオブジェクトとのマッピング関係を行います.ここではコードを貼り付けません.3.2、初期化
前に述べたAbstract HandlerMethodMappingはInitializingBeanインターフェースを実現していますので、Beanの実装後にafterPropertiessetを呼び出すことができます.実は、Abstract Handler MethodMapping類の初期化作業はこの場所から始まっています.コードは以下の通りです
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
// bean
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// bean ,
processCandidateBean(beanName);
}
}
// ,
handlerMethodsInitialized(getHandlerMethods());
}
afterPropertiesset()方法では、inithandler Methods()メソッドを呼び出し、実際の初期化ロジックはこの方法で実現される.まずget CanddateBenNames()法により、全ての候補のBeanインスタンスのnameを取得する.コードは以下の通りである.protected String[] getCandidateBeanNames() {
//detectHandlerMethodsInAncestorContexts bean
return (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
}
その後、get Canda teBeabin Names()方法で取得したBeanのインスタンスを巡回して、processCadidateBean()方法を呼び出して処理します.コードは以下の通りです.protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
// Class
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// beanType , isHandler() , RequestMappingHandlerMapping , Controller RequestMapping
if (beanType != null && isHandler(beanType)) {
// Bean , mappingRegistry Map
detectHandlerMethods(beanName);
}
}
_; processCadidateBean()方法では、beanNameに対応するBeanインスタンスがプロセッサタイプかどうかを判断し、そうであればdetectHandlerMethods()メソッドを呼び出し、この例に対応する処理方法を取得し、mappingRegistryで対応するMap関係に登録します.protected void detectHandlerMethods(Object handler) {
// Class
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// , , cglib , cglib ;
Class<?> userType = ClassUtils.getUserClass(handlerType);
// userType , getMappingForMethod()
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
// , registerHandlerMethod()
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
_; 以来、初期化作業は完了しました.つまり、Mappingマッチング条件とHandlerMethodのマッピング関係を登録しました.3.3、get Handler Internal()方法
前にAbstract Handler MethodMapping類の初期化方法を分析しましたが、今からこの種類がどのように親類のgetHandler Internalを実現するかを分析します.
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// lookupPath
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// “org.springframework.web.servlet。HandlerMapping.lookupPath”
request.setAttribute(LOOKUP_PATH, lookupPath);
//
this.mappingRegistry.acquireReadLock();
try {
// request
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// bean name, createWithResolvedBean() , Bean
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
get Handler Internal()の方法では、lookuhandler Method()の方法は本当にプロセッサを検索する方法であることを知っています.コードは以下の通りです.@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// MappingRegistry.urlLookup , lookupPath mapping
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// mapping, matches
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// lookupPath , mapping, mapping
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// mapping,
// , getMappingComparator() ,
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
//
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
//
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
//OPTIONS ,
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
// ,
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
// “.bestMatchingHandler”
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// , ".pathWithinHandlerMapping" , 。
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// bean , handleNoMatch() , , 。
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
4、Request MappingInfoHandlerMapping抽象類Request MappingInfolerMapping抽象類はAbstractHandlerMethodMapping類から継承され、その中の汎型がRequest MappingInfoであることを明らかにしました.
_; はRequest MappingInfoHandlerMapping抽象類において、以下のいくつかのことをした.
1、Optionsのデフォルトの処置方法を定義していますが、HTTP_に関するものがあります.OPTIONHANDLE_METHOD定数、内部クラスHttpOptionshandler、および静的コードブロック初期化定数.
2、コンストラクタは、コンストラクタにマッピングのネーミングポリシー、すなわちRequest MappingInfoHandlerMethodMappingNamingStrategyを設定して実現する方式です.
3、親類のいくつかの方法を実現しました.
// RequestMappingInfo URL
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
return info.getPatternsCondition().getPatterns();
}
// RequestMappingInfo request , RequestMappingInfo getMatchingCondition() , RequestMappingInfo
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
//
@Override
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
return (info1, info2) -> info1.compareTo(info2, request);
}
4、父親類のハンドルマッチ()方法は、この方法には主に処理変数(テンプレート変数とマトリックス変数)とメディアタイプの論理が追加されています.次にパラメータ処理の過程を詳しく分析します.5、親類を書き換えたhandleNoMatch()方法では、内部クラスのPartar Match Helperを使用して不一致の原因を判断し、指定された異常を投げる.コードを貼り付けていません.
5、Request MappingHandlerMapping類
レイクマットMappling HandlerMapping類は、Request MappingInfo HandlerMappingを継承したほか、Match able HandlerMappingとEmbededValueResourAwareの2つのインターフェースを実現しました.この中で、Match atleHandlerMappingインターフェースを実現する方法は、まだ使われていません.EmbodValue ResourAwareインターフェースを実現しました.解析String文字列をサポートするということです.
定義された属性:
//
private boolean useSuffixPatternMatch = true;
// ContentNegotiationManager 。
private boolean useRegisteredSuffixPatternMatch = false;
//
private boolean useTrailingSlashMatch = true;
// path
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
//
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
// , spring
@Nullable
private StringValueResolver embeddedValueResolver;
//RequestMappingInfo
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
afterPropertiesset()方法前に、afterPropertiesset()方法が初期化を実現する方法であることを知っています.Abstracthandler MethodMappingの抽象的な種類の中で、hander種類と方法の検査と登録を実現しました.Request MappingHandlerMapping類では、Request MappingInfo構築構成構成の初期化が追加されました.コードは以下の通りです.
@Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
ishandler()メソッド はAbstracthandler MethodMapping類において初期化を行う場合、processCadidateBen()メソッドにishandlerを使用して現在のbeanインスタンスがプロセッサかどうかを判断します.実際の判断ロジックはここで実現されます.@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
get MappingForMethodメソッド AbstractHandler MethodMappingクラスでは、初期化を行う際に、detectHandlerMethods()メソッドにおいて、この方法を呼び出してプロセッサ方法の判断を実現します.@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}
その他の方法は、レギターMapping()、レギターHandlerMethod()の方法の2つの方法であり、父の種類のメソッドを呼び出す以外に、主にConsmesRequest Coditionの判断条件を設定している.今後はクロスドメイン関連のパラメータも配置されていますが、ここでは詳しく分析していません.6、まとめ
この文章の中で、私達はAbstracthandlerMethMappingシステムが実現したHandlerMappingの中の3つの種類の基本的な実現を分析しただけです.その中にRequest CoditionとRequest MappingInfo(Request Coditionのサブクラスでもあります.)やHandleMethodなどの具体的な学習過程が含まれています.