上: spring5.x 宣言型トランザクションの原則とソース コードの実装
一連の記事:
spring5.x-宣言型トランザクションの原則とソースコードの実装
spring5.x-listener の原理とソースコードの実装
spring5.x とそれに対応する Spring ソースコード読み取り環境の紹介
基本知識
元の記事を参照してください: springmvc
ソースコードの学習
@RequestMapping("/{id}")
public String showUserInfo(ModelMap modelMap, @PathVariable("id")Integer id){
Student student = new Student();
student.setId(id);
student.setAge(100);
student.setName("test");
modelMap.addAttribute("name", student.getName());
modelMap.addAttribute("age", student.getAge());
modelMap.addAttribute("id", student.getId());
return "result";
}
Springmvc の初期化
-
DispatcherServlet の初期化: DispatcherServlet は Spring MVC のフロントエンド コントローラーであり、web.xml または WebApplicationInitializer で Servlet コンテナーを構成するときに初期化されます。DispatcherServlet の初期化ソース コードの場所は org.springframework.web.servlet.DispatcherServlet です。
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
コードの場所: org.springframework.web.servlet.DispatcherServlet#initStrategies
//用于初始化 Spring MVC 的各个策略组件。
protected void initStrategies(ApplicationContext context) {
//初始化处理文件上传的解析器,用于解析请求中的 multipart 数据。
this.initMultipartResolver(context);
//初始化处理国际化的解析器,用于解析请求中的语言区域信息。
this.initLocaleResolver(context);
//初始化处理主题的解析器,用于解析请求中的主题信息。
this.initThemeResolver(context);
//初始化处理器映射器,用于将请求映射到对应的处理器(Controller)。
this.initHandlerMappings(context);
//初始化处理器适配器,用于调用处理器的方法并从中获取 ModelAndView 对象。
this.initHandlerAdapters(context);
//初始化处理器异常解析器,用于处理请求过程中发生的异常。
this.initHandlerExceptionResolvers(context);
//初始化请求到视图名称的转换器,用于将处理器返回的逻辑视图名称转换为实际的视图路径。
this.initRequestToViewNameTranslator(context);
//初始化视图解析器,用于将视图名称解析为具体的视图类型。
this.initViewResolvers(context);
//初始化 FlashMap 管理器,用于处理请求间的数据传递。
this.initFlashMapManager(context);
}
-
ServletContextListener の登録: サーブレット コンテナが起動すると、通常、Spring MVC コンテキストを初期化するためにリスナー (ServletContextListener) が登録されます。設定ファイルの読み込みやApplicationContextの作成など、Spring MVCの初期化をリスナーのcontextInitializedメソッドに実装します。
コードの場所: org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
//执行应用程序事件监听器
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//调用监听器的 并传递当前发生的应用程序事件作为参数。
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass().getName())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, var6);
}
}
}
-
WebApplicationContext の初期化: Spring MVC は、ServletContextListener の初期化時に作成および設定される独自のコンテナ (WebApplicationContext) を使用します。WebApplicationContext の初期化の詳細については、org.springframework.web.context.support.XmlWebApplicationContext または org.springframework.web.context.support.AnnotationConfigWebApplicationContext ソース コードを参照してください。
-
HandlerMapping と HandlerAdapter の初期化: HandlerMapping はリクエストを対応するプロセッサー (コントローラー) にマッピングする役割を果たし、HandlerAdapter は対応するプロセッサーのメソッドを呼び出す役割を担います。これらのコンポーネントの初期化には、構成ファイル、アノテーション スキャン、その他のプロセスが含まれます。関連するソース コードの場所は、org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping、org です。 .springframework.web .servlet.handler.SimpleUrlHandlerMapping、org.springframework.web.servlet.handler.BeanNameUrlHandlerAdapter、org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter など。
リスナーに注目してください: org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
-
ビュー リゾルバーの初期化: ビュー リゾルバー (ViewResolver) は、プロセッサ メソッドの戻り値を特定のビューに解析する役割を果たします。Spring MVC は、InternalResourceViewResolver、FreeMarkerViewResolver など、複数のタイプのビュー リゾルバーをサポートしています。初期化プロセス中に、ビューのプレフィックス、サフィックスなど、ビュー リゾルバーの関連プロパティと場所が構成されます。関連するソース コードの場所は org.springframework.web.servlet.view.InternalResourceViewResolver です。
構成したビューは次のとおりです。
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
したがって、次のように解析されます。
代数値の位置:org.springframework.beans.factory.BeanFactoryUtils#beansOfTypeincludeAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>, boolean, boolean)
最終的には、onRefresh() を呼び出して初期化を完了します。コードの場所: org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
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);
}
springmvc ディストリビューションの実装
スプリングローディングが完了したら呼び出しですが、httpやtcpなどの呼び出し方法によって配信が行われることに注意してください。それは最終的に doDispatch に転送されます。
コードの場所: org.springframework.web.servlet.DispatcherServlet#doDispatch
//分发方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
//初始化请求信息
HttpServletRequest processedRequest = request;
//用于存储根据请求对象匹配到的处理器对象
HandlerExecutionChain mappedHandler = null;
//标识是否已解析多部分请求
boolean multipartRequestParsed = false;
//获取当前WebAsyncManager对象(异步处理)
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
//初始化视图模型
ModelAndView mv = null;
Exception dispatchException = null;
try {
//检查请求是否为多部分请求(文件或),通过检查请求头中的 "Content-Type" 是否以 "multipart/" 开头来判断。
processedRequest = checkMultipart(request);
//判断是否一致,如果是则为true
multipartRequestParsed = (processedRequest != request);
// 获取处理对象
mappedHandler = getHandler(processedRequest);
//为空就是没找着路劲 返回404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 获取处理对
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 获取请求方法
String method = request.getMethod();
//判断是否为get请求
boolean isGet = "GET".equals(method);
//如果是 或头为HEAD
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;
}
// 调用适配器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//检查异步处理是否已经开始
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//设置默认的视图名到ModelAndView中
applyDefaultViewName(processedRequest, mv);
//调用后置处理器方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
//如果出象则创建一个错误的异常
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//返回客户端
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
// 方法检查异步处理是否已经开始
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
//用于在异步处理开始后执行相关的清理操作或其他逻辑处理
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
//用于清理多部分请求中使用的资源
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
次に、詳細に進みます
処理済みリクエスト = checkMultipart(リクエスト);
コードの場所: org.springframework.web.servlet.DispatcherServlet#checkMultipart
//将请求转换为分段请求,并使分段解析程序可用。如果未设置多部分解析程序,则只需使用现有请求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//不为空 且 请求是否是multipart类型
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
//将请求转换为 multipart类型的请求对象,
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
"this typically results from an additional MultipartFilter in web.xml");
}
//检查当前请求是否已经发生过多部分请求解析失败的异常
else if (hasMultipartException(request) ) {
logger.debug("Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
try {
//转换为HttpServletRequest 并返回
return this.multipartResolver.resolveMultipart(request);
}
catch (MultipartException ex) {
if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
logger.debug("Multipart resolution failed for error dispatch", ex);
// Keep processing error dispatch with regular request handle below
}
else {
throw ex;
}
}
}
}
// If not returned before: return original request.
return request;
}
上記のメソッドは、マルチパート タイプのリクエストを解析します。
次: mappedHandler = getHandler(processedRequest); これは、現在のリクエストのハンドラーを決定するために使用されます。
Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//不为空
if (this.handlerMappings != null) {
//循环调用每个 HandlerMapping 的 getHandler(request) 方法,传入当前的 HttpServletRequest 对象作为参数,来获取对应的处理器。
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
これは、実際にはリクエスト プロセッサ (ハンドラー) のメソッドである HandlerExecutionChain を取得するために使用されます。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
取得したばかりのプロセッサに基づいて HandlerAdapter を取得すると、実際には、それを使用して modelandview またはその他のビューを呼び出すことが決定されます。
-
RequestMappingHandlerAdapter: プロセッサー機能、アノテーション付きコントローラー、およびその他のタイプのプロセッサーを適応させます。
-
SimpleControllerHandlerAdapter: コントローラー インターフェイスの実装に基づいてコントローラーを適応させます。
-
HttpRequestHandlerAdapter: HttpRequestHandler インターフェイスを実装するように適合されたプロセッサ。元の HttpServletRequest および HttpServletResponse を処理するために使用されます。
-
HandlerAdapter のデフォルト実装である DefaultHandlerAdapter: は、明示的なアダプターを持たないプロセッサー・タイプを処理するために使用されます。デフォルトでは、ServletInvocableHandlerMethod がプロセッサー・メソッドの実行に使用されます。
-
非同期リクエスト用のアダプター: AsyncHandlerInterceptor や Callable に対応するアダプターなど。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
//循环判断是哪种类型匹配,匹配就返回
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
//获取最终的HandlerAdapter
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");
}
mappedHandler.applyPreHandle(processedRequest, response)、このメソッドはインターセプターの実行場所を記録するために使用されます。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//调用前置拦截器方法 如果返回值为 false,表示拦截器不允许继续执行后续的处理逻辑
if (!interceptor.preHandle(request, response, this.handler)) {
//方法进行拦截器链的后置处理,并直接返回 false
triggerAfterCompletion(request, response, null);
return false;
}
//记录位置
this.interceptorIndex = i;
}
}
return true;
}
//用于触发拦截器链的后置处理(afterCompletion
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
//不为空
if (!ObjectUtils.isEmpty(interceptors)) {
//循环执行拦截器的后置处理逻辑,通常用于资源清理、日志记录等操作。
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
mv = ha.handle(processedRequest、response、mappedHandler.getHandler());
3 つのパラメータを受け入れます。processedRequest はプリプロセッサ チェーンによって処理されるリクエスト オブジェクト、response はレスポンス オブジェクト、mappedHandler.getHandler() はマップされるリクエスト ハンドラ オブジェクトです。
handle() メソッドが実行されると、リクエスト プロセッサの種類に応じて、対応する処理ロジックが呼び出されます。異なるリクエスト ハンドラーは、Controller、HttpRequestHandler、HttpMessageConverter など、異なるタイプのオブジェクトである場合があります。
注: 一般的な HTTP リクエストにはさまざまなタイプのパラメータを含めることができ、次のタイプが一般的です。
-
クエリ パラメータ: URL 内にあり、? で始まり、キーと値のペアは key=value の形式で表現され、複数のパラメータは & で区切られます。例: http://hong.com/api?key1=value1&key2=value2。クエリパラメータは、URL を解析することで取得できます。
-
パス パラメーター (パス パラメーター): URL パス内にあり、特定のリソースの識別子または属性を表すために使用されます。パス パラメーターは RESTful ルーティングでよく使用され、プレースホルダーを使用してパラメーター値を表します。例: http://hong.com/api/users/{userId}。ここで、{userId} はパス パラメーターです。
-
リクエスト本文パラメータ (リクエスト本文パラメータ): リクエスト本文にあり、通常はフォーム データまたは JSON 形式を使用して渡します。パラメータのタイプは、HTTP リクエストの Content-Type ヘッダー フィールドによって決定できます。一般的なパラメータのタイプは次のとおりです。
-
-
フォームパラメータ: フォームデータ形式、つまり key=value の形式を使用して渡されます。
-
JSON パラメータ: JSON 形式で渡され、リクエスト本文のデータは正当な JSON オブジェクトです。
-
ファイルパラメータ: ファイルのアップロードに使用され、リクエスト本文にはファイルのバイナリデータが含まれます。
-
リクエストパラメータを決定する方法は、使用するサーバー側フレームワークまたはプログラミング言語によって異なります。ほとんどのフレームワークは、リクエスト パラメーターを解析して取得するためのツールまたはライブラリを提供します。一般に、リクエスト パラメータは、リクエスト オブジェクトから対応するパラメータを取得することで取得できます。たとえば、Java の Spring フレームワークでは、@RequestParam アノテーション、HttpServletRequest オブジェクトなどを使用してリクエスト パラメータを取得できます。
この場所は非常に複雑です。興味があればさらに深く掘り下げることができます。
したがって、学生の中には、springmvc はどのようなパラメータをサポートできるのかと疑問に思う人もいるかもしれません。
-
クエリパラメータ: クエリパラメータをメソッドパラメータとして受け取ります。@RequestParam アノテーションを使用して、リクエスト内のクエリパラメータにパラメータをバインドできます。また、デフォルト値やそれらが必須かどうかなどの属性を指定することもできます。
-
パス パラメーター: リクエスト パス内のプレースホルダーを使用してパラメーターを受信します。@PathVariable アノテーションを使用して、パス パラメーターとメソッド パラメーターをバインドします。
-
リクエストボディパラメータ: 通常、POST または PUT リクエストでデータを受信するために使用されます。@RequestBody アノテーションを使用して、リクエスト本文のデータをメソッドのパラメータにバインドできます。サポートされているデータ形式には、JSON、XML などが含まれます。
-
ヘッダー情報 (リクエスト ヘッダー): @RequestHeader アノテーションを使用して、特定のリクエスト ヘッダー情報をメソッド パラメーターにバインドできます。
-
Cookie パラメータ: @CookieValue アノテーションを使用して、特定の Cookie 値をメソッド パラメータにバインドします。
-
フォームパラメータ: フォーム送信を受信するためのパラメータ。@RequestParam アノテーションまたは @ModelAttribute アノテーションを使用して、フォーム フィールドをメソッド パラメータにバインドできます。
-
ファイル アップロード: ファイル アップロード リクエストを受信するとき、MultipartFile タイプのメソッド パラメーターを使用して、アップロードされたファイル データを受信できます。
もちろん、上記は私がソースコードから学んだことですが、他にまだ見ていないことはありますか? CanHandlerMethodArgumentResolver
mappedHandler.applyPostHandle(processedRequest、response、mv);
上記の段落はインターセプターの実装の一部であり、リクエストや未開封のインターフェイス許可の一部をこれと組み合わせてインターセプトできます。
コードの場所: org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
//调用拦截器
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
processDispatchResult(processedRequest、response、mappedHandler、mv、dispatchException);
これはビューの最終的な実装ですが、次のようにさまざまな種類のビューがあります。
-
JSP ビュー (InternalResourceView): ビュー テクノロジとして JSP (JavaServer Pages) を使用します。InternalResourceViewResolver ビュー リゾルバーを通じて、論理ビュー名が JSP ファイルにマップされ、モデル データがレンダリングのために JSP に渡されます。
-
Thymeleaf ビュー (ThymeleafView): ビューのレンダリングに Thymeleaf テンプレート エンジンを使用します。Thymeleaf は、HTML、XML、JavaScript、その他のファイルと統合できる最新の Java テンプレート エンジンです。
-
FreeMarker View (FreeMarkerView): ビューのレンダリングに FreeMarker テンプレート エンジンを使用します。FreeMarker は、テンプレート ファイルとデータ モデルを通じて最終出力を生成するテンプレート エンジンです。
-
Velocity View (VelocityView): ビューのレンダリングには Apache Velocity テンプレート エンジンを使用します。Velocity は、テキスト、HTML、XML などの出力を生成するために使用できる Java ベースのテンプレート エンジンです。
-
JSON ビュー (MappingJackson2JsonView): モデル データを JSON 形式でクライアントに返します。モデル データは、Jackson ライブラリを通じて JSON 文字列にシリアル化され、HttpServletResponse を通じてクライアントに返されます。
-
XML ビュー (MarshallingView): モデル データを XML 形式でクライアントに返します。モデル データは、JAXB (Java Architecture for XML Binding) を通じて XML に変換され、HttpServletResponse を通じてクライアントに返されます。
-
など: 上記のビューに加えて、Spring はカスタム ビュー リゾルバーとカスタム ビュー タイプもサポートしており、ビジネス ニーズに応じてビューのレンダリングに他のビュー テクノロジを使用できます。
コードの場所: org.springframework.web.servlet.DispatcherServlet#processDispatchResult
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
//不为空
if (exception != null) {
//匹配类型为ModelAndViewDefiningException
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
//转换成ModelAndViewDefiningException
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
//获取自定义异常
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
//转换
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 不为空且 非被清除
if (mv != null && !mv.wasCleared()) {
//进行视图创建
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else { //证明被解析过了
//打印日志
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
//若请求是异步处理的(Concurrent handling started during a forward),则直接返回,不做后续处理。
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
//不为空打印日志
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
その後、いくつかの例外があり、最後に処理が行われますが、これらはすべてキャッシュをクリアするための処理です。あまり詳しく見ないでください、興味があれば調べてください。
やっと
Springmvc は非常に重要で、特にソース コードには、ビューの解析やインターセプト方法などのロジックが含まれます。これらのコアは、春に改善したい学生に特に適しています。詳細にデバッグし、コア ロジックを確認することをお勧めします。深く掘り下げてアーキテクチャを実行したり、いくつかのフレームワークを統合したりしたい場合は、これらのプロセスの順序を理解する必要があり、そうでないと大きな損失を被る可能性があります。もちろん上記はあくまで私の意見です。
参考記事:
https://www.yii666.com/blog/452442.html
https://blog.csdn.net/weixin_56644618/article/details/127594065