過去2日間、私はspringmvcのソースコードを学び、主に起動と呼び出しのプロセスを学びました。メモを記録するために、主に次の2つの部分に分かれています
。1。起動プロセス
2.呼び出しプロセス
springmvcソースコード、簡単に説明します。最初にそれについて話します
1. handlerMappingオブジェクトを初期化し、handlerAdapterオブジェクトを初期化します。handlerMappingオブジェクトを初期化すると、すべてのBeanが解析され、コントローラーと対応するURLが対応するマップコレクションに格納されます
。2。呼び出されると、次のように呼び出されます。 org.springframework .web.servlet.DispatcherServlet#doDispatch方法
3. GetHandler()は、リクエスト内のURLに係る地図内の対応するメソッドを見つけることである現在の要求を処理handlerMappingを取得する。4. GetAdapter
()を取得し適切なhandlerAdapterオブジェクト。したがって、コントローラーメソッドごとに処理ロジックが異なるため適切です
。5。実行ターゲットメソッド。
ここに詳細なポイントがあります。1つはリフレクションによってメソッド呼び出しを完了することであり、もう1つはメソッドを実装することです。インターフェイスを呼び出してクラス呼び出し
6を完了するには、次のように判断します。ビューにジャンプする必要がありますか、それともストリームを介してデータをブラウザに直接書き込む必要がありますか。つまり、@ ResponseBodyとModelAndViewの違いです。
springmvcアプリケーション
コントローラを実装する方法
コントローラを宣言する方法は3つあります
- @Controllerアノテーションをクラスに追加し、メソッドの@RequestMappingアノテーションを介してURLを指定します
- このようにコントローラーインターフェイスを実装するには、クラス名に@Component( "/ mapping address")を追加する必要があります。
- HttpRequestHandlerインターフェースを実装し、@ Component( "/ mapping address")をクラスに追加します
後者の2つの原則は基本的に同じです。Springには2つのデフォルトhandlerMappingsがあります:RequestMappingHandlerMappingとBeanNameUrlHandlerMapping; @Controllerアノテーション付きコントローラーの場合、前者は前者によって処理され、コントローラーインターフェイスまたはhttpRequestHandlerインターフェイスは後者によって実装されます
。handlerAdapterも同様です。
後者の2つは、1つのタイプと見なすことができ、どちらも要求されたURLとしてbeanNameを使用します。メソッドが実際に呼び出されると、2つのタイプのメソッドは異なります。
@ Controllerこのメソッドはリフレクションを介して呼び出しを完了するために、
後者の2つはインターフェイス実装クラスのメソッドを呼び出してメソッド呼び出しを完了する
上の写真はインターネットで何気なく撮ったスクリーンショットです。これはおおよそspringmvcのプロセスです。
プロセスを開始します
スタートアッププロセスについては、私たちのブログは注意を払う必要があります
RequestMappingHandlerMapping
BeanNameUrlHandlerMapping
これらの2つのBeanの初期化は私たちのブログの焦点であるため、これらの2つのBeanの初期化で十分です:URLとメソッドがどのように対応するか
RequestMappingHandlerMapping
このクラスはInitializingBeanインターフェースを間接的に実装しているため、クラスが初期化されると呼び出されます。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
これは初期化メソッドのコールチェーンです。ここにはコールチェーンのみが掲載されています。中央のコードは重要ではなく、数行のコードです。キーコードは最後のメソッドです。
/**
* 在初始化时,会调到这里,然后会获取到beanDefinitionMap中的bean,判断当前bean是否是@Controller或者@RequestMapping修饰的类
* 如果是,就调用detectHandlerMethods方法,解析方法的@RequestMapping注解对应的path,然后存入到map集合中
*/
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
/**
* 这里涉及到父子容器
* spring容器和springmvc容器
*/
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
/**
* 我们姑且可以理解为:这里获取到spring容器中所有的对象
*/
for (String beanName : beanNames) {
/**
* 如果是以"scopedTarget."开头,就跳过,不做处理
*/
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
/**
* 判断当前bean是否是@Controller或者@RequestMapping修饰的bean
* 如果当前类是这两个注解修饰的,就在下面的方法中,会解析@RequestMapping对应的URL
*/
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
このメソッドは、@ Controllerまたは@RequestMappingによって変更されたすべてのBeanを処理してから、分析のためにBeanに対応するメソッドを取得します。
/**
* @param handler
* 在这里其实是根据bean,获取到bean中所有添加了@RequestMapping注解的method,然后把method和url进行映射,并把映射关系存到map中
*/
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
//userType是当前的类名
Class<?> userType = ClassUtils.getUserClass(handlerType);
/**
* 根据类名获取到所有的方法,这里的key是method,value是@RequestMapping对应的path
* key: public void com.springmvc.TestController.test()
* value: {[/test]}
*/
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
/**
* 根据method,获取到当前method上添加的@RequestMapping注解的path属性信息
*/
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
/**
* 遍历依次解析bean所有的method以及对应的url
*/
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
これは、メソッドに対応するURLとマップに格納されて
いるコードを解析するメソッドです。その中で、getMappingForMethod(method、userType);は、メソッドに従ってメソッドの@RequestMapping情報を解析するためのコードです。ここに投稿されているように、内部のロジックは比較的単純です
registerHandlerMethod(handler、invocableMethod、mapping);メソッドはメソッドを順番にトラバースし、メソッドとURLをマップコレクションに格納します
BeanNameUrlHandlerMapping
このクラスの場合、これはBeanのポストプロセッサを介して行われ、URLとメソッドのマッピングが完了し
ます。ご覧のとおり、このクラスは間接的にApplicationContextAwareインターフェイスを実装しています。
org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization
BeanNameUrlHandlerMapping Beanが初期化されるとき、ポストプロセッサのpostProcessBeforeInitializationメソッドが呼び出されると、org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#determineUrlsForHandlerに呼び出されます。
ここでデバッグを見るだけで、真ん中のジャンプロジックについてはあまり説明されません。
/**
* BeanNameUrlHandlerMapping该bean在初始化的时候,会调用到该方法,和该接口实现了ApplicationContextAware接口有关系
* @param beanName the name of the candidate bean
* @return
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
/**
* 只处理以/开头的beanName
* 这个bean处理的是实现了Controller接口或者HttpRequestHandler接口的controller,这两种方式
* 都是需要在bean上添加@Component注解,并制定beanName,beanName就是url,所以,beanName要以/开头
*/
if (beanName.startsWith("/")) {
urls.add(beanName);
}
//处理别名
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
これはコアコードであり、他のコードは投稿されません。これはほぼ同じロジックです。解析されたURLのbeanNameとメソッドをマップコレクションに格納します。
転送
コールコントローラ、org.springframework.web.servlet.DispatcherServletからの直接の入り口#doDispatch
読み始めました
/**
* 找到对应的handlerMapping,并将interceptor封装到HandlerExecutionChain
* 如果handlerMapping为null,就表示没有找到对应的映射器,返回404 notFound
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//获取到处理请求的处理器适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
// 这里应该也是和缓存有关系
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//在调用目标方法之前调用拦截器,拦截器预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.对modelAndView的处理
/**
* 实际的处理器处理请求,返回结果视图对象
* 如果是@Controller注解的这种方式,是通过反射实现的
* 如果是实现了Controller接口或者实现了HttpRequestHandler接口这种方式,是通过调用实现类的方法来完成的
*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
コードの一部のみを投稿しました。これ
が呼び出しプロセスです。次のブログに入れましょう。コンテンツが多すぎて、混乱しやすいです。