SpringMVCプロファイル解析(三)
49736 ワード
前言
前の2つの記事では、プロファイル登録beanの手順と、注釈登録Beanの手順について説明します.
では、SpringMVCプロファイルの具体的な構成に目を向けましょう.
一般的に、SpringMVCプロファイルを作成するときに、以下のプロファイルを追加することを推奨されます.
<mvc:annotation-driven/>
では、なぜこの配置を加えたのか、このラベルがどのような役割を果たしているのか、今日探ってみましょう.
mvc:annotation-drivenの解析
前回ではcontext:component-scanの解析過程について説明しました.どちらのラベルも拡張ラベルに属します.
彼らの解析はBeanDefinitionParserの対応する実装クラスを用いて解析されたが,彼らは異なる実装クラスに対応しているだけである.
context:component-scan対応はC o m p o n e n t ScanBeanDefinitionParser、mvc:annotation-driven対応はBeanDefinitionParserのサブクラスorg.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
どのようにこのクラスを見つけてこのラベルを解析するかについては、あまり言わないで、前と同じでしょう.直接parseメソッドを見て
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
// RequestMappingHandlerMapping
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
else if (element.hasAttribute("enableMatrixVariables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
RuntimeBeanReference validator = getValidator(element, source, parserContext);
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
ManagedList> messageConverters = getMessageConverters(element, source, parserContext);
ManagedList> argumentResolvers = getArgumentResolvers(element, parserContext);
ManagedList> returnValueHandlers = getReturnValueHandlers(element, parserContext);
String asyncTimeout = getAsyncTimeout(element);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
ManagedList> callableInterceptors = getCallableInterceptors(element, source, parserContext);
ManagedList> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
// RequestMappingHandlerAdapter
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
addResponseBodyAdvice(handlerAdapterDef);
if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
// "ignoreDefaultModelOnRedirect" spelling is deprecated
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriCompContribDef.setSource(source);
uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedCsInterceptorDef.setSource(source);
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);
//
RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
exceptionHandlerExceptionResolver.setSource(source);
exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(exceptionHandlerExceptionResolver);
String methodExceptionResolverName =
parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);
//
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
responseStatusExceptionResolver.setSource(source);
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
String responseStatusExceptionResolverName =
parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);
//
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExceptionResolverName =
parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
parserContext.popAndRegisterContainingComponent();
return null;
}
この方法は巨大な長さではないか,実は彼の主な仕事は登録することだ.
RequestMappingHandlerMapping
BeanNameUrlHandlerMapping
RequestMappingHandlerAdapter
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
この8つのインスタンスはBeanFactory、すなわちSpringコンテナにあります.
最初の2つは,要求マッピングを処理するためのHandlerMappingインタフェースの実装クラスである.最初に@RequestMapping注記を処理します.2つ目はcontrollerクラスの名前をリクエストurlにマッピングします.中間の3つはリクエストを処理するために使用されます.具体的には、現在のリクエストを処理するためにどのコントローラを呼び出す方法を決定することである.@Controller注記を最初に処理するプロセッサで、カスタムメソッドパラメータと戻り値をサポートします.2つ目は、HttpRequestHandlerを継承するプロセッサを処理することです.3番目の処理は、Controllerインタフェースから継承されたプロセッサです.後ろの3つは異常を処理するための解析器です.
ここでは、前に述べたDispatchServeret初期化プロセスに戻る必要があります.
DispatchServicelet初期化中のhandlerMappingsとhandlerAdapters属性の注入
DispatchServiceletの初期化時のFrameWorkServiceletのinitWebApplicationContextメソッドに戻ります
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//...
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
return wac;
}
この方法のcreateWebApplicationContext法はこのシリーズの最初の編で分析しました.次にonRefresh法に着きます.この方法はFrameWorkServiceで空の方法であり、DispatchServiceで具体的に実現されていることがわかりました.
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
initHandlerMappings法とinitHandlerAdapters法が見られた.
この2つの方法はhandlerMappingsプロパティとhandlerAdaptersプロパティを注入する場所です.
まずinitHandlerMappingsを見てみましょう
initHandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
1.まず私たちのBeanfactoryに行って、HandlerMappingタイプのBeanがSpring容器に注入されたかどうかを探して、もしあればhandlerMappings属性に入れて、入れる前に、ソートする必要があります.ソートの根拠はorder属性で、order値が小さいほど優先度が高くなります.ここには2つのHandlerMappingタイプのBean注入があります.それぞれRequestMappingHandlerMapping(orderは0)です.BeanNameUrlHandlerMapping(orderは2).
2.既に注入されたbeanがない場合は、Springコンテナに行って、beanNameがhandlermappingであり、タイプがHandlerMappingのBeanであるかどうかを探し、ある場合は、このBeanをhandlerMappingプロパティに注入します.
3.上記の2つの方法で対応するbeanが見つからない場合は、デフォルトの登録方法を使用します.
デフォルトの登録方法は具体的には言いませんが、ソースコードを見てもいいです.DispatchServiceletを注入します.propertiesが指定したHandlerMappingタイプのクラスはhandlerMappingsプロパティにあります.
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
ここまで分析すると、少なくとも私たちはこのラベルを使用して構成していなくても、私たちのプログラムに影響を与えることはありません.同じように、空のhandlerMappingsを得ることはできません.デフォルトの初期化方法があります.このラベルの構成もspring 3です.1以降は、上記のプロファイルをそのまま使用してデフォルト初期化を行うので、このラベルを追加すると処理要求を変更したクラスになります.
handlerAdaptersを注入するには具体的な分析はありません.ただ、orderソートも必要ですが、デフォルトではorderはIntegerです.MAXは、order値が明示的に与えられていません.一般的には、adapterはhandlerを1つしかサポートしていないため、優先度も必要ありません.
実はここまで、DispatchServeretの初期化プロセスの差は多くないと思います.では、リクエストの処理を簡単に分析してみましょう.
RequestMappingHandlerMappingとRequestMappingHandlerAdapterを使用してリクエストを処理
HandlerMappingの優先度を上で解析したが,RequestMappingHandlerMappingのorderが0で最も高く,すなわちuriが1つで行えばRequestMappingHandlerMappingが一致すれば,彼が処理するに違いない.
まずRequestMappingHandlerMappingの初期化プロセスを見てみましょう.
RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping {
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
注意、彼の親は特殊なインタフェースを実現しました:InitializingBean
このインタフェースを実装したクラスは、このBeanがSpringに注入されたときにafterPropertiesSetメソッドを呼び出す(これは明らかにInitializingBean定義のメソッドである).
では、このメソッドを呼び出すタイミングは何でしょうか.
では、これは私たちがBeanをインスタンス化した文章、つまりこのシリーズの最初の文章にさかのぼります.
AbstractAutowireCapableBeanFactoryのdoCreateBeanメソッドを見つけます
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
//..
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);//
if (exposedObject != null) {
// afterPropertiesSet initMethod
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
return exposedObject;
}
注目すべきはinitializeBeanメソッドです
initializeBean
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction
まずinvokeInitMethodsに注目し,ここでafterPropertiesSetを実行した.
invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction
方法は簡単で、あまり説明しません.注意afterPropertiesSetはinit-methodより先に実行されます.
はい、明らかに、私たちは次に彼のafterPropertiesSetが何をしたのかを見てみましょう.
親AbstractHandlerMethodMappingで実装されています
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
// Spring beanName
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
1.Spring容器に注入したBeanのbeanNameを全て得る
2.条件を満たすBean(@Controllerと@RequestMapping注記付きBean)をフィルタし、それらを
....
まず、どのようにフィルタリングされているかを少し見てみましょう.
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
//RequestMappingHandlerMapping
@Override
protected boolean isHandler(Class> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
isHandlerは先祖の親AbstractHandlerMethodMappingで定義されており、実装はRequestMappingHandlerMappingで、ついに自分でやったことがある.簡単にこのbeanがこの2つの注釈を持っているかどうかを見ることです.
条件を満たすbeanが得られた後、detectHandlerMethods(beanName)を呼び出します.登録します.
AbstractHandlerMethodMapping\
/** * Look for handler methods in a handler. * @param handler the bean name of a handler or a handler instance */
protected void detectHandlerMethods(final Object handler) {
Class> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map mappings = new IdentityHashMap();
final Class> userType = ClassUtils.getUserClass(handlerType);
Set methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}
1.Bean内のすべてのメソッドをフィルタし、@RequestMapping注記のあるMethodを処理します.
2.メソッドをHandlerMethodに登録し、最後にAbstractHandlerMappingというベースクラスのurlMap属性に関連付けます.
まず、どのようにフィルタリングしたかを見てみましょう.
Set methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class> handlerType) {
RequestMappingInfo info = null;
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
やっと久しぶりにコールバック方法が出てきましたが、ここではコールバック方法を採用してフィルタリング方法を採用しました.
getMappingForMethodの具体的な実装はRequestMappingHandlerMappingで、彼はついに自分で動いた.
方法は難しくない.
1.@RequestMapping注記を含むメソッドを見つけてから、@RequestMappingInfo注記の関連情報をこのインスタンスに注入するRequestMappingInfoインスタンスを作成します.
2.このbeanに@RequestMapping注記がある場合は、この2つの@RequestMapping注記の関連情報を結合し、1つのRequestMappingInfoインスタンスに注入して戻り、メソッドに@RequestMapping注記がない場合はnullを返し、フィルタリングされます.
続いて登録しました.
registerHandlerMethod
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
// HandlerMethod
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method
" + newHandlerMethod + "
to " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method
" + oldHandlerMethod + " mapped.");
}
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
}
Set patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);
}
}
1.HandlerMethodインスタンスを作成し、同じインスタンスがすでに作成されているかどうかを確認し、ある場合はエラーを報告します.
2.新しいインスタンスをhandlerMethodsプロパティに追加し、mappingをkeyとします.
3.getMappingPathPatternsを使用して、mapping(ここではRequestMappingInfoインスタンス)の情報が一致するuri patternを得る.
4.次にpatternをkey、mappingをvalueとしてAbstractHandlerMappingのurlMap属性に注入し、これによりurlMapとHandlerMethodの関係を確立することに成功した.
では、このクラスの初期化はここまでです.
getHandler
次にRequestMappingHandlerMappingのgetHandlerメソッドがhandlerをどのように得たかを見てみましょう.
getHandlerメソッドはベースクラスAbstractHandlerMappingでのみ実現される.
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
return getHandlerExecutionChain(handler, request);
}
この方法の中で最も重要な方法getHandlerInternal(request);テンプレートメソッドです.
明らかにサブクラス自身が実現する必要があり、ここでの実現はAbstractHandlerMethodMappingにある.
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// request
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
// uri urlMap HandlerMethod.
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
実は登録の仕方を話し終わったら、どのように取るかを言う必要はありません.登録したMapの中でデータを出しているからです.
Handlerを手に入れた後、私たちは来ました.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
前にhandlerAdaptersの登録についてお話ししたように、彼らのorderは同じですが、サポートされているHandlerのタイプはそれぞれ異なり、ここで得られたのはRequestMappingHandlerAdapterです.対応するsupportsメソッド:
AbstractHandlerMethodAdapter
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
対応するHandlerAdapterを取得すると、
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
これが本当に要求を処理する方法です.
AbstractHandlerMethodAdapter
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//RequestMappingHandlerAdapter
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandleMethod(request, response, handlerMethod);
}
}
}
return invokeHandleMethod(request, response, handlerMethod);
}
RequestMappingHandlerAdapterのhandleInternalはリクエストを正式に処理します.
具体的な処理要求プロセスについては、非常に複雑で、次回詳しく説明します.