Springソースコード解析(四):Spring MVC
ここではSpring MVCフレームコードを分析します.webAppliationConteetに関する分析は以前の文書を参照してください.Spring Web MVCフレームの実現を重点的に分析します.Displatch Servletの分析から始めます.
具体的な初期化過程については分かりやすいです.inithandler Mappingsを持ってみます.
HandlerMaapingでは、一連のマッピング関係を持つmapを定義しています.
Displaptcher Servletは、HandlerMappingを通じてWebアプリケーションに実行経路を決定させ、HanderMappingで見たように、HandlerMappingは単なる言い訳である:
このように、HandlerMapping全体の初期化プロセスを明確に見ることができます.また、具体的なhandlerマッピングがどのように記憶され、検索されているかを見ました.ここでExecution Chinを生成して、私たちが見つけたhandlerとbeanを定義するときに定義されたInterceptorsを保存します.
Displatch Servletに戻ります.初期化が完了したら、実際のウェブ要求はdoService()方法で処理されます.Displatch Servletは普通のServletだけだと知っています.
まずどのようにCommandオブジェクトを取得するかを見てください.私たちにとってはHandlerです.以下はget Handlerのコードです.
もしServletContectの中でhandlerを見つけられないなら、持っているhandler Mappingを通じて一つを生成します.今持っているすべてのhandler Mappingを繰り返します.一つだけではないと定義できますので、彼らは定義する時も順序を指定して、最初を見つけてから戻ります.まずハンドルMappingを見つけて、このハンドルMappingを通じて実行チェーンに戻ります.中には最終的なHandlerと私達が定義した一連のInterceptorが含まれています.具体的には上のSimpleUrlhandlerMappingのコード分析を参考にして、getHandlerがどのようにHandlerExecution Charinを得たかを知ることができます.
HandlerExecution Charinを得てから、私達はHandlerAdapterを通じてこのHandlerの合法性を判断します.
私達はもう一度Displatch Servletに戻ってコードを見ましょう.
いずれにしても、ビューオブジェクトを取得した後、ビューオブジェクトのrenderを呼び出してデータの表示プロセスを完了します.具体的なJstlViewがどのように実現されているかを確認できます.
過程は長いですか?私たちは今どこにいますか?へへへ,私達はちょうど完成した事MVCの中でViewのrender、InternalResource Viewのrender過程について比較的に簡単でただ1つの資源のリダイレクト処理を完成します.必要なのは実際にviewのinternalResourceパスを得て、そのリソースに転送することです.どうやってリソースのパスを取得しますか?呼び出しを通じて:
このView Resoliveはいつ呼び出されましたか?ハッハッ、私達はこのようにまたDisplatch Servletに戻って一体を見に行きます.Displatch Servletで:
そして、私たちはview.renderに戻り、データの最終的なhttpResonseへの書き込みを完了します.例えば、AbstractExcel Viewでの実現:
これはSpring Web MVCフレーム全体の大まかな流れであり、全体のMVCプロセスはDispactch Servletによって制御される.MVCのキープロセスは以下を含む.
handler Mappingは、handlerMappingがチェーンを実行することによって達成されるが、具体的なマッピング関係は、bean定義ファイルで定義され、HandlerMappingがコンテキストをロードするときに配置される.そして、Displatch Servletは、HandlerMappingを呼び出して対応する実行チェーンを得て、最後に図を通してモデルデータを表示しますが、ビューオブジェクトは、ビュー名を解析する際に配置されたものです.これらはコアクラスのHanderMapping、View Resolover、View、Handlerの緊密な協力としてMVCの機能を実現しました.
// DispatcherServlet , Spring MVC
protected void initFrameworkServlet() throws ServletException, BeansException {
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
}
// DispatcherServlet , Spring MVC
protected void initFrameworkServlet() throws ServletException, BeansException {
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
}
は注釈を見て分かります.これはDispactch Serlvetの初期化過程です.WebAppliation Contectがすでに存在している場合に行われます.すなわち初期化の時にIOC容器はすでに作動しているはずです.これもweb.xmlにSpringを配置する時です.Displatch Servletのload-on-startupの属性を2に配置する必要がある理由.具体的な初期化過程については分かりやすいです.inithandler Mappingsを持ってみます.
private void initHandlerMappings() throws BeansException {
if (this.detectAllHandlerMappings) {
// HandlerMapping,
// handlerMapping,
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
getWebApplicationContext(), HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// order handlerMapping list
Collections.sort(this.handlerMappings, new OrderComparator());
}
}
else {
try {
Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// , BeanNameUrlHandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
........
}
}
private void initHandlerMappings() throws BeansException {
if (this.detectAllHandlerMappings) {
// HandlerMapping,
// handlerMapping,
Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
getWebApplicationContext(), HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList(matchingBeans.values());
// order handlerMapping list
Collections.sort(this.handlerMappings, new OrderComparator());
}
}
else {
try {
Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// , BeanNameUrlHandlerMapping
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
........
}
}
どのように文脈環境を獲得するかは、私たちの前のIOC容器がweb環境にロードされている分析を参照してください.Displatch Servletは定義されたすべてのHandlerMappingを一つのListに置いて後で使用します.このチェーンの各要素は一つのhandle Mappingの構成です.一般的に各handler Mappingは一連のURL要求からSpring Controllerへのマッピングを持っています.HandlerMaapingでは、一連のマッピング関係を持つmapを定義しています.
Displaptcher Servletは、HandlerMappingを通じてWebアプリケーションに実行経路を決定させ、HanderMappingで見たように、HandlerMappingは単なる言い訳である:
public interface HandlerMapping {
public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
// HandlerExecutionChain, Command , handler
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
public interface HandlerMapping {
public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
// HandlerExecutionChain, Command , handler
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
彼の具体的な実現には一つのインターフェース方法だけが必要です.このインターフェース方法はHandlerExectionChinで、実際には実行チェーンです.Commandモードで説明したように、この種類は簡単です.つまりInterceptorチェーンと一つのControllerを持つことです.public class HandlerExecutionChain {
private Object handler;
private HandlerInterceptor[] interceptors;
........
}
public class HandlerExecutionChain {
private Object handler;
private HandlerInterceptor[] interceptors;
........
}
これらのHandlerとIntercepterは私達がHandlerMappingを定義する必要がある時に配置してください.例えば具体的なSimpleURLHandlerMappingに対して、彼がやるべきことはURLマッピングの方式によってHandlerとInterceptorを登録して、自分でマッピングされたhandleMapを維持します.この登録の過程はIOC容器初期化SimpleUrlhandler Mappingの時に完成されました.これからの解析はmapのマッピング情報を使うことができます.ここの情報とbeanファイルの情報は等価です.以下は具体的な登録プロセスです.protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
// SimpleUrlHandlerMapping
Iterator it = urlMap.keySet().iterator();
while (it.hasNext()) {
// url
String url = (String) it.next();
// url bean handler
Object handler = urlMap.get(url);
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// AbstractHandlerMapping
registerHandler(url, handler);
}
}
}
protected void registerHandlers(Map urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
// SimpleUrlHandlerMapping
Iterator it = urlMap.keySet().iterator();
while (it.hasNext()) {
// url
String url = (String) it.next();
// url bean handler
Object handler = urlMap.get(url);
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// AbstractHandlerMapping
registerHandler(url, handler);
}
}
}
AbstractMappingHandlerに登録するコード:protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
// handlerMap handler, Url
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
........
}
// bean handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
handler = getApplicationContext().getBean(handlerName);
}
}
// handler.
if (urlPath.equals("/*")) {
setDefaultHandler(handler);
}
else {
// url handler handlerMap
this.handlerMap.put(urlPath, handler);
........
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
// handlerMap handler, Url
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
........
}
// bean handler
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) {
handler = getApplicationContext().getBean(handlerName);
}
}
// handler.
if (urlPath.equals("/*")) {
setDefaultHandler(handler);
}
else {
// url handler handlerMap
this.handlerMap.put(urlPath, handler);
........
}
}
handleMapはHashMapを持っています.中には具体的なマッピング情報が保存されています.private final Map handlerMap = new HashMap();
private final Map handlerMap = new HashMap();
SimpleUrl HandlerMappingのインターフェースHandlerMappingの実装は、このgetHandlerは初期化時に得られたマッピングテーブルに基づいてDisplatch Servletを生成するために必要な実行チェーンです.public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// request handler, AbstractUrlHandlerMapping
Object handler = getHandlerInternal(request);
// , handler
if (handler == null) {
handler = this.defaultHandler;
}
// ,
if (handler == null) {
return null;
}
// handler handler,
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// HandlerExecutionChain, handler , HandlerExecutionChain , handler 。
return new HandlerExecutionChain(handler, this.adaptedInterceptors);
}
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// request handler, AbstractUrlHandlerMapping
Object handler = getHandlerInternal(request);
// , handler
if (handler == null) {
handler = this.defaultHandler;
}
// ,
if (handler == null) {
return null;
}
// handler handler,
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// HandlerExecutionChain, handler , HandlerExecutionChain , handler 。
return new HandlerExecutionChain(handler, this.adaptedInterceptors);
}
私たちは具体的なハンドル検索過程を見てみます.protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// HTTP Request , 。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
.......//
return lookupHandler(lookupPath, request);
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) {
// SimpleUrlHandlerMapping , 。
Object handler = this.handlerMap.get(urlPath);
if (handler == null) {
// map handler , Jre Matcher 。
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
//
handler = this.handlerMap.get(registeredPath);
bestPathMatch = registeredPath;
}
}
if (handler != null) {
exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
}
}
else {
exposePathWithinMapping(urlPath, request);
}
//
return handler;
}
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// HTTP Request , 。
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
.......//
return lookupHandler(lookupPath, request);
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) {
// SimpleUrlHandlerMapping , 。
Object handler = this.handlerMap.get(urlPath);
if (handler == null) {
// map handler , Jre Matcher 。
String bestPathMatch = null;
for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
String registeredPath = (String) it.next();
if (this.pathMatcher.match(registeredPath, urlPath) &&
(bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
//
handler = this.handlerMap.get(registeredPath);
bestPathMatch = registeredPath;
}
}
if (handler != null) {
exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
}
}
else {
exposePathWithinMapping(urlPath, request);
}
//
return handler;
}
私たちは常にハンドルMapというHashMapの中で探していますが、直接見つけられなければ、Match Patternのパターンを通じて探してもいいですか?Hnader Mappingを配置する時にANT文法で配置できると覚えています.その中の処理はここにあります.このように、HandlerMapping全体の初期化プロセスを明確に見ることができます.また、具体的なhandlerマッピングがどのように記憶され、検索されているかを見ました.ここでExecution Chinを生成して、私たちが見つけたhandlerとbeanを定義するときに定義されたInterceptorsを保存します.
Displatch Servletに戻ります.初期化が完了したら、実際のウェブ要求はdoService()方法で処理されます.Displatch Servletは普通のServletだけだと知っています.
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.......
//
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
//
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
.......
//
Map attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
try {
//
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
要求に対する処理は実際にdoDisplatch()に完成させるものであることを見ました.この方法は長いですが、プロセスは簡単で分かります.protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// handlerMapping
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
........
try {
// ModelAndView 。
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);
// handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Interceptor
if (mappedHandler.getInterceptors() != null) {
for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// handler , HandlerAdapter handler : Spring 。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Interceptor
if (mappedHandler.getInterceptors() != null) {
for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
........
// Did the handler return a view to render?
//
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
.......
}
protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// handlerMapping
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
........
try {
// ModelAndView 。
ModelAndView mv = null;
try {
processedRequest = checkMultipart(request);
// handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Interceptor
if (mappedHandler.getInterceptors() != null) {
for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// handler , HandlerAdapter handler : Spring 。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Interceptor
if (mappedHandler.getInterceptors() != null) {
for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
........
// Did the handler return a view to render?
//
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
.......
}
私たちはMVCフレームと密接に関連したコードがどのようにhttp要求に対応する実行チェーンを得て、どのように実行チェーンを実行しますか?まずどのようにCommandオブジェクトを取得するかを見てください.私たちにとってはHandlerです.以下はget Handlerのコードです.
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
// ServletContext - , ServletContext 。
HandlerExecutionChain handler =
(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if (handler != null) {
if (!cache) {
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
return handler;
}
// initHandlerMapping HandlerMapping
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
.......
// handler , HandlerMapping handler
handler = hm.getHandler(request);
// handler ServletContext
if (handler != null) {
if (cache) {
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
}
return handler;
}
}
return null;
}
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
// ServletContext - , ServletContext 。
HandlerExecutionChain handler =
(HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
if (handler != null) {
if (!cache) {
request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
}
return handler;
}
// initHandlerMapping HandlerMapping
Iterator it = this.handlerMappings.iterator();
while (it.hasNext()) {
HandlerMapping hm = (HandlerMapping) it.next();
.......
// handler , HandlerMapping handler
handler = hm.getHandler(request);
// handler ServletContext
if (handler != null) {
if (cache) {
request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
}
return handler;
}
}
return null;
}
Servlet Contectでhandleerを取得すれば、直接に戻ってきます.実は、このhandlerは前回の処理の結果をバッファしています.これは初めてServlet Contextに置くべきです.もしServletContectの中でhandlerを見つけられないなら、持っているhandler Mappingを通じて一つを生成します.今持っているすべてのhandler Mappingを繰り返します.一つだけではないと定義できますので、彼らは定義する時も順序を指定して、最初を見つけてから戻ります.まずハンドルMappingを見つけて、このハンドルMappingを通じて実行チェーンに戻ります.中には最終的なHandlerと私達が定義した一連のInterceptorが含まれています.具体的には上のSimpleUrlhandlerMappingのコード分析を参考にして、getHandlerがどのようにHandlerExecution Charinを得たかを知ることができます.
HandlerExecution Charinを得てから、私達はHandlerAdapterを通じてこのHandlerの合法性を判断します.
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
// adapter
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
return ha;
}
}
........
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
// adapter
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
return ha;
}
}
........
}
判断によって、私達はこのhandleControllerがControllerインターフェースの実現であるかどうかを知っています.例えば、具体的なHandlerAdapter-SimpleController Handler Adapter:public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
.......
}
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
.......
}
簡単に判断してください.handlerがControllerインターフェースを実現しましたか?これは構成ファイルを検証するための機構をも具現している.私達はもう一度Displatch Servletに戻ってコードを見ましょう.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
これはハンドルの具体的な呼び出しです.CommandモードのCommand.executeに相当します.もちろんModelAndViewに戻ります.次はViewを処理するプロセスです.if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
}
呼び出されたのはレンダーの方法です.protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {response.setLocale(locale);
View view = null;
// ModelAndView 。
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
//
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
.......
}
else {
// ModelAndView View , 。
view = mv.getView();
........
}
// View , 。
view.render(mv.getModelInternal(), request, response);
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {response.setLocale(locale);
View view = null;
// ModelAndView 。
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
//
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
.......
}
else {
// ModelAndView View , 。
view = mv.getView();
........
}
// View , 。
view.render(mv.getModelInternal(), request, response);
}
全体の過程から私達は先にModelAndViewの中でビューの論理名を探して、見つけられないならデフォルトのビューを使って、ビューの名前が見つけられたら、彼に解析して実際に必要なビューオブジェクトを得ることができます.もう一つは、ModelAndViewに実際のビューオブジェクトが含まれている可能性があります.このビューオブジェクトは直接使用できます.いずれにしても、ビューオブジェクトを取得した後、ビューオブジェクトのrenderを呼び出してデータの表示プロセスを完了します.具体的なJstlViewがどのように実現されているかを確認できます.
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
......
// Map
Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
mergedModel.putAll(this.staticAttributes);
if (model != null) {
mergedModel.putAll(model);
}
// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
}
// 。
renderMergedOutputModel(mergedModel, request, response);
}
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
......
// Map
Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
mergedModel.putAll(this.staticAttributes);
if (model != null) {
mergedModel.putAll(model);
}
// Expose RequestContext?
if (this.requestContextAttribute != null) {
mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
}
// 。
renderMergedOutputModel(mergedModel, request, response);
}
注書きはよく分かりました.まずすべてのデータモデルを一つのMap-mergdModelに統合して、それからレンデMergdOutputModelを呼び出します.このrendeMergdOutputModelはテンプレートの方法であり、彼の実現はInternal Resource Viewで、つまりJstlViewの父のクラスである:protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// InternalResource 。
String dispatcherPath = prepareForRendering(request, response);
// 。
RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);
if (rd == null) {
throw new ServletException(
"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
}
.......
protected void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// InternalResource 。
String dispatcherPath = prepareForRendering(request, response);
// 。
RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);
if (rd == null) {
throw new ServletException(
"Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
}
.......
}
まずモデルデータを処理します.exposemodel Arequest AttributesはAbstractViewで実現しました.この方法はModelAndViewのモデルデータと他のrequestデータをすべてServlet Contextに入れます.このようにモデルデータ全体はServlet Contextを通じて暴露して共有されます.protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
Iterator it = model.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
..........
String modelName = (String) entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
...........
}
else {
request.removeAttribute(modelName);
.......
}
}
}
protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
Iterator it = model.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
..........
String modelName = (String) entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
...........
}
else {
request.removeAttribute(modelName);
.......
}
}
}
データ処理部分のexposeHelperに戻りましょう.これはテンプレートの方法ですが、現在はJstlViewで実装されています.public class JstlView extends InternalResourceView {
private MessageSource jstlAwareMessageSource;
protected void initApplicationContext() {
super.initApplicationContext();
this.jstlAwareMessageSource =
JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
}
protected void exposeHelpers(HttpServletRequest request) throws Exception {
JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
}
}
public class JstlView extends InternalResourceView {
private MessageSource jstlAwareMessageSource;
protected void initApplicationContext() {
super.initApplicationContext();
this.jstlAwareMessageSource =
JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
}
protected void exposeHelpers(HttpServletRequest request) throws Exception {
JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
}
}
JstlUtilsには他のjstlに対する特殊なデータ処理と設定が含まれています.過程は長いですか?私たちは今どこにいますか?へへへ,私達はちょうど完成した事MVCの中でViewのrender、InternalResource Viewのrender過程について比較的に簡単でただ1つの資源のリダイレクト処理を完成します.必要なのは実際にviewのinternalResourceパスを得て、そのリソースに転送することです.どうやってリソースのパスを取得しますか?呼び出しを通じて:
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return getUrl();
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
throws Exception {
return getUrl();
}
このurlはどこで生成されますか?私達はView関連のコードの中で見つけられませんでした.実際に彼はView Rosoliveの時に生成しました.protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
ここはViewを生成するところです.自然に生成されたurlや他のいくつかのviewに関する属性も配置されています.このView Resoliveはいつ呼び出されましたか?ハッハッ、私達はこのようにまたDisplatch Servletに戻って一体を見に行きます.Displatch Servletで:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
........
View view = null;
//
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
// , 。
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
..........
}
......
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
........
View view = null;
//
if (!mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
if (mv.isReference()) {
// , 。
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
..........
}
......
}
以下はビュー名を解析するための具体的なプロセスです.protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {
//
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
// 。
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {
//
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
// 。
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
ここでは具体的なView Resoloverを呼び出して、ビューの名前を解析します.単純な解析以外に、私たちの要求に基づいて実際に必要なビューオブジェクトを作成しました.具体的なview Resoloverはbean定義ファイルで定義されています.initView Resolover()メソッドではview Resolover変数に初期化されています.具体的なInternal Resource View Resoloverはどのようにビュー名を処理するかを見てみます.Vビューオブジェクトとして生成されます.public View resolveViewName(String viewName, Locale locale) throws Exception {
// ,
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
// No synchronization, as we can live with occasional double caching.
synchronized (this.viewCache) {
//
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
// ,
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
........
}
return view;
}
}
}
public View resolveViewName(String viewName, Locale locale) throws Exception {
// ,
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
// No synchronization, as we can live with occasional double caching.
synchronized (this.viewCache) {
//
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
// ,
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
........
}
return view;
}
}
}
これらのcreateView()、loadView()、buildView()の関係について、Eclipseのcall hiearhyを見てみます.そして、私たちはview.renderに戻り、データの最終的なhttpResonseへの書き込みを完了します.例えば、AbstractExcel Viewでの実現:
protected final void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
.........
// response.setContentLength(workbook.getBytes().length);
response.setContentType(getContentType());
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
out.flush();
}
protected final void renderMergedOutputModel(
Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
.........
// response.setContentLength(workbook.getBytes().length);
response.setContentType(getContentType());
ServletOutputStream out = response.getOutputStream();
workbook.write(out);
out.flush();
}
このように、私たちの前の分析と一致しました.Displatch Servletは、解析ビュー名の時に、要求に応じてビューオブジェクトを生成しました.InternalResource Viewに使用するurlと他の各種およびHTTP reponseに関する属性は、生成したビューオブジェクトにそのまま書き込み、ビューオブジェクトのrenderを直接呼び出してデータの展示を完了します.これはSpring Web MVCフレーム全体の大まかな流れであり、全体のMVCプロセスはDispactch Servletによって制御される.MVCのキープロセスは以下を含む.
handler Mappingは、handlerMappingがチェーンを実行することによって達成されるが、具体的なマッピング関係は、bean定義ファイルで定義され、HandlerMappingがコンテキストをロードするときに配置される.そして、Displatch Servletは、HandlerMappingを呼び出して対応する実行チェーンを得て、最後に図を通してモデルデータを表示しますが、ビューオブジェクトは、ビュー名を解析する際に配置されたものです.これらはコアクラスのHanderMapping、View Resolover、View、Handlerの緊密な協力としてMVCの機能を実現しました.