當(dāng)前位置:
首頁(yè) >
前端技术
> javascript
>内容正文
javascript
springmvc dao怎么可以不写实现类_SpringMVC(一)细聊ContextLoaderListener 是怎么被加载的...
生活随笔
收集整理的這篇文章主要介紹了
springmvc dao怎么可以不写实现类_SpringMVC(一)细聊ContextLoaderListener 是怎么被加载的...
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
點(diǎn)擊上方“藍(lán)字”關(guān)注我們,每天點(diǎn)亮一個(gè)技能點(diǎn)。
本文作者:burgxun
云析學(xué)院VIP學(xué)員
前言
為什么要寫博客呢?其實(shí)還是源于我在知乎上面的 看到的一個(gè)回答。問題是這樣的:生活中最浪費(fèi)時(shí)間的事情有哪些?我看到下面的一個(gè)貼子回答是:學(xué)而不思,猶豫不決,看到這個(gè) 我想到 哇偶 總結(jié)的好到位!好了 廢話不多說,為什么要寫Spring MVC 呢 當(dāng)然是因?yàn)槲也┲魑易罱诳碨pring MVC 的源碼了!為什么開篇想講 2個(gè)容器呢?隨著Spring Boot 的越來越流行 我們之前配置XML 的方式也逐漸 被舍棄。我們剛使用Spring MVC的時(shí)候 一定也知道怎么去標(biāo)準(zhǔn)的配置 比如ContextLoaderListener DispatcherServlet等等 那為什么要有這些配置,這些配置又去怎么運(yùn)行的呢?今天 我就從自己的理解 去分析下 , 理解不當(dāng)?shù)牡胤?請(qǐng)各位見諒,留言指出 多謝!PS: 這篇文章 有點(diǎn)跑題 重點(diǎn)講了一個(gè)程序 tomcat 是怎么去加載web配置 又是怎么樣將我們的ContextLoaderListener監(jiān)聽類加入到監(jiān)聽列表中的 最后串聯(lián)了下整個(gè)Servlet 容器怎么去啟動(dòng)一個(gè)應(yīng)用程序的~下面一片文章 我會(huì)重點(diǎn)去看下ContextLoaderListener 里面怎么去創(chuàng)建WebApplication容器的,這里面我也走了一點(diǎn)彎路 一開始以為 我們的ContextLoaderListene 是通過ApplicationContext中的addListener添加的 最終 我一步步發(fā)現(xiàn) 我想錯(cuò)了!標(biāo)準(zhǔn)配置
來,首先回顧下我們的標(biāo)準(zhǔn)配置: org.springframework.web.context.ContextLoaderListener contextConfigLocation WEB-INF/configs/spring/applicationContext.xml mvc-dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/configs/spring/mvc-dispatcher-servlet.xml 1 mvc-dispatcher / 從上面的我的注釋中 也可以看看出 一個(gè)是Spring 的 一個(gè)是Spring MVC 的Spring Root容器
Spring Root容器 一般都是配置了非Controller的Bean 比如 Dao,Service 等等Bean都會(huì)在這個(gè)容器里面。我們看到 這個(gè)配置contextparam 中的節(jié)點(diǎn) 設(shè)置了ApplicationContext的config路徑 這個(gè)后面在源碼中 我會(huì)講到,先過~~ContextLoaderListener 是被怎么執(zhí)行的
listener我們看到listener 這個(gè)節(jié)點(diǎn)配置了一個(gè)ContextLoaderListener類,那我們來詳細(xì)聊下 這個(gè)類 是做什么的首先 我們知道 服務(wù)在啟動(dòng)的時(shí)候 會(huì)給我們應(yīng)用提供了一個(gè)容器的上下文環(huán)境ServletContext 這個(gè)上下文環(huán)境 就是我們應(yīng)用程序的宿主環(huán)境這邊簡(jiǎn)答的說下ServletContenxt: 當(dāng)我們的Servlet容器 也就是我們經(jīng)常用的Tomcat或者Jetty 等 在web應(yīng)用啟動(dòng)的時(shí)候 會(huì)創(chuàng)建一個(gè)Servlet對(duì)象,這個(gè)對(duì)象可以被 web應(yīng)用下面的所有的Servlet所訪問。也就是說一個(gè)web應(yīng)用只有一個(gè)ServletContent.記得這點(diǎn)很重要,到后面的代碼中 我們就能知道 我們的WebApplicationContext 其實(shí)就是ServletContent的一個(gè)屬性Attribute。我們可以從Spring 項(xiàng)目的的一個(gè)ContextLoaderTests 單元測(cè)試類中 可以看出 @Test public void testContextLoaderListenerWithDefaultContext() { MockServletContext sc = new MockServletContext(""); sc.addInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, "/org/springframework/web/context/WEB-INF/applicationContext.xml " + "/org/springframework/web/context/WEB-INF/context-addition.xml"); ServletContextListener listener = new ContextLoaderListener(); ServletContextEvent event = new ServletContextEvent(sc); listener.contextInitialized(event); WebApplicationContext context = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); ...忽略???}仔細(xì)看下 WebApplicationContext的獲取方式 當(dāng)然這個(gè)我后面也會(huì)在ContextLoaderListener的源碼中講到這邊配置的節(jié)點(diǎn) 就是把ContextLoaderListener 加入到 啟動(dòng)的監(jiān)聽列表里面 當(dāng)程序啟動(dòng)后 會(huì)用ServletContextEvent作為參數(shù) 去初始化listener 上面的單元測(cè)試代碼 也寫很清楚了ContextLoaderListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener{ public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } /** * Initialize the root web application context. */ @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); } /** * Close the root web application context. */ @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); }}?ContextLoaderListener 繼承了ContextLoader類 實(shí)現(xiàn)了ServletContextListener接口其中contextInitialized,contextDestroyed 方法都是ServletContextListener接口里面的方法ServletContextListener
那我們看下ServletContextListener 這個(gè)類public interface ServletContextListener extends EventListener { public void contextInitialized(ServletContextEvent sce); public void contextDestroyed(ServletContextEvent sce);}這個(gè)接口也是很簡(jiǎn)單 2個(gè)方法 而且接口繼承了 一個(gè)空的EventListener接口 這樣的寫法 是為了標(biāo)識(shí)某一類的接口,程序有的地方 判斷的時(shí)候 用到這個(gè)。這樣的寫法 源碼中很多地方用到過。這邊這樣寫是為了 程序在啟動(dòng)的時(shí)候 從xml 讀取到Listener節(jié)點(diǎn)的配置的類 只有實(shí)現(xiàn)了ServletContextListener接口的類 才能加入到監(jiān)聽列表中那又是怎么加入 怎么去調(diào)用的呢?帶著 這個(gè)問題 我們一步步去看看我在上面 提到過 Servlet容器會(huì)在每個(gè)web程序啟動(dòng)的時(shí)候會(huì)分配一個(gè)ServletContext的上下文,那下面我們看下這個(gè)ServletContext 是什么ServletContext首先我們看下 這個(gè)類 位于什么地方 javax.servlet-api-3.0.1-sources.jar!\javax\servlet\ServletContext.java這個(gè)類是位于javax.servlet-api 這個(gè)包里面的 我們都知道 這個(gè)包 定義了servlet的標(biāo)準(zhǔn) web容器都是根據(jù)這些標(biāo)準(zhǔn)去實(shí)現(xiàn)的public interface ServletContext { public void addListener(String className); public void addListener(Class extends EventListener> listenerClass); public void addListener(T t); }這個(gè)是我截取了 我關(guān)心的方法看到這個(gè)方法的時(shí)候 我們就能明白 為什么 上面的ServletContextListener 要繼承了一個(gè)空的EventListener接口了吧!容器在啟動(dòng)的時(shí)候 就是把web.xml 中配置的監(jiān)聽類 加入到 分配到上下文環(huán)境ServletContext中的那我懷著好奇心 繼續(xù)尋找ServletContext的實(shí)現(xiàn)類ApplicationContext此類位于tomcat-embed-core-8.5.45-sources.jar!\org\apache\catalina\core\ApplicationContext.java這個(gè)就是在tomcat中的了 tomcat本質(zhì)就是一個(gè)Servlet容器 所以一定是要按照javax.servlet-api里面的定義的標(biāo)準(zhǔn)接口去實(shí)現(xiàn)的,話不多說 我們?nèi)タ聪麓a是怎么寫的,我截取了部分有關(guān)我講的代碼 有興趣的對(duì)照著源碼 看看 private final StandardContext context; public ApplicationContext(StandardContext context) { super(); this.context = context; this.service = ((Engine) context.getParent().getParent()).getService(); this.sessionCookieConfig = new ApplicationSessionCookieConfig(context); // Populate session tracking modes populateSessionTrackingModes(); } public void addListener(T t) { if (!context.getState().equals(LifecycleState.STARTING_PREP)) { throw new IllegalStateException( sm.getString("applicationContext.addListener.ise", getContextPath())); } boolean match = false; if (t instanceof ServletContextAttributeListener || t instanceof ServletRequestListener || t instanceof ServletRequestAttributeListener || t instanceof HttpSessionIdListener || t instanceof HttpSessionAttributeListener) { context.addApplicationEventListener(t); match = true; } if (t instanceof HttpSessionListener || (t instanceof ServletContextListener && newServletContextListenerAllowed)) { // Add listener directly to the list of instances rather than to // the list of class names. context.addApplicationLifecycleListener(t); match = true; } if (match) return;????}我們看到 ApplicationContext 初始化的是傳入了一個(gè)StandardContext對(duì)象 而且最終addListener 也是調(diào)用的StandardContext類中的addApplicationEventListener方法 看了下上面的代碼 監(jiān)聽事件 還做了區(qū)分 放入了2個(gè)集合中StandardContext
那我們就來看下StandardContext 是怎么做的 public class StandardContext extends ContainerBase implements Context, NotificationEmitter { //這邊是一個(gè)對(duì)象鎖 后面添加applicationListeners的時(shí)候會(huì)用到 private final Object applicationListenersLock = new Object(); private String applicationListeners[] = new String[0]; private List applicationEventListenersList = new CopyOnWriteArrayList<>(); public void addApplicationEventListener(Object listener) { applicationEventListenersList.add(listener); } private Object applicationLifecycleListenersObjects[] = new Object[0]; public void addApplicationLifecycleListener(Object listener) { int len = applicationLifecycleListenersObjects.length; Object[] newListeners = Arrays.copyOf( applicationLifecycleListenersObjects, len + 1); newListeners[len] = listener; applicationLifecycleListenersObjects = newListeners; } @Override public void addApplicationListener(String listener) { synchronized (applicationListenersLock) { String results[] = new String[applicationListeners.length + 1]; for (int i = 0; i < applicationListeners.length; i++) { if (listener.equals(applicationListeners[i])) { log.info(sm.getString("standardContext.duplicateListener",listener)); return; } results[i] = applicationListeners[i]; } results[applicationListeners.length] = listener; applicationListeners = results; } fireContainerEvent("addApplicationListener", listener); } /** * Configure the set of instantiated application event listeners * for this Context. * @return true if all listeners wre * initialized successfully, or false otherwise. */ public boolean listenerStart() { // Instantiate the required listeners String listeners[] = findApplicationListeners();//就是返回 applicationListeners[]數(shù)組 Object results[] = new Object[listeners.length];//這邊是存儲(chǔ) 我們監(jiān)聽類的是實(shí)例化后的對(duì)象 boolean ok = true; for (int i = 0; i < results.length; i++) { String listener = listeners[i]; results[i] = getInstanceManager().newInstance(listener);//實(shí)例化 我們的監(jiān)聽類 } // 這個(gè)是吧 實(shí)例化的類 拆分成了2個(gè)監(jiān)聽數(shù)組對(duì)象 不知道為啥 ServletContextListener 是單獨(dú)的一個(gè) // 這個(gè)也對(duì)應(yīng)了 applicationContext中的方法 也是按照這樣的類型 拆分了存儲(chǔ)的 ArrayList eventListeners = new ArrayList<>(); ArrayList lifecycleListeners = new ArrayList<>(); for (int i = 0; i < results.length; i++) { if ((results[i] instanceof ServletContextAttributeListener) || (results[i] instanceof ServletRequestAttributeListener) || (results[i] instanceof ServletRequestListener) || (results[i] instanceof HttpSessionIdListener) || (results[i] instanceof HttpSessionAttributeListener)) { eventListeners.add(results[i]); } if ((results[i] instanceof ServletContextListener) || (results[i] instanceof HttpSessionListener)) { lifecycleListeners.add(results[i]); } } for (Object eventListener: getApplicationEventListeners()) { eventListeners.add(eventListener); } setApplicationEventListeners(eventListeners.toArray()); for (Object lifecycleListener: getApplicationLifecycleListeners()) { lifecycleListeners.add(lifecycleListener); if (lifecycleListener instanceof ServletContextListener) { noPluggabilityListeners.add(lifecycleListener); } } setApplicationLifecycleListeners(lifecycleListeners.toArray()); Object instances[] = getApplicationLifecycleListeners(); if (instances == null || instances.length == 0) { return ok; } ServletContextEvent event = new ServletContextEvent(getServletContext()); ServletContextEvent tldEvent = null; if (noPluggabilityListeners.size() > 0) { noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext()); tldEvent = new ServletContextEvent(noPluggabilityServletContext); } for (int i = 0; i < instances.length; i++) { if (!(instances[i] instanceof ServletContextListener)) continue; ServletContextListener listener = (ServletContextListener) instances[i]; fireContainerEvent("beforeContextInitialized", listener); if (noPluggabilityListeners.contains(listener)) { listener.contextInitialized(tldEvent); } else { listener.contextInitialized(event); } fireContainerEvent("afterContextInitialized", listener); } return ok; } }看到這邊 我們才知道 ContextLoaderListener#contextInitialized 是怎么被調(diào)用起來的 listenerStart的方法 就是處理監(jiān)聽類的地方最后 關(guān)注下 addApplicationListener 這個(gè)類方法 我的注釋 注釋上也說了 看到了這個(gè)方法 我才明白 我前面想的是錯(cuò)的,存放我們的XML的監(jiān)聽類的地方 其實(shí)是applicationListeners[] 數(shù)組那既然addApplicationListener是重寫的方法 他的父類是Context類最后我有查找了下addApplicationListener方法時(shí) 是在哪些地方使用的 這樣我們就知道我們的XML中配置的監(jiān)聽類是什么加進(jìn)入 剛才上面的數(shù)組的最后我在 tomcat-embed-core-8.5.45-sources.jar!\org\apache\catalina\startup\ContextConfig.java 類中找到了這個(gè)方法的使用 看名字要也能知道 這個(gè)是一個(gè)Context的配置類這也能符合我們的想法 監(jiān)聽類就是在配置Context的是加入的 那我們看下是否 值這樣的ContextConfig
configureContext public class ContextConfig implements LifecycleListener{ protected Context context = null; @Override public void lifecycleEvent(LifecycleEvent event) { context = (Context) event.getLifecycle(); if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { configureStart();//開始配置 } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { beforeStart(); } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { // Restore docBase for management tools if (originalDocBase != null) { context.setDocBase(originalDocBase); } } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) { configureStop(); } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) { init(); } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) { destroy(); } } /** * Process a "contextConfig" event for this Context. */ protected synchronized void configureStart() { webConfig();//開始webConfig配置 if (!context.getIgnoreAnnotations()) { applicationAnnotationsConfig(); } if (ok) { validateSecurityRoles(); } // Configure an authenticator if we need one if (ok) { authenticatorConfig(); } } protected void webConfig() { WebXml webXml = createWebXml();//創(chuàng)建WebXml對(duì)象 // Parse context level web.xml InputSource contextWebXml = getContextWebXmlSource();//獲取webXML的地址 if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) { ok = false; } ServletContext sContext = context.getServletContext(); // Step 9. Apply merged web.xml to Context if (ok) { configureContext(webXml);//這邊是拿到了webXml 開始配置 } } private void configureContext(WebXml webxml) { for (FilterDef filter : webxml.getFilters().values()) { if (filter.getAsyncSupported() == null) { filter.setAsyncSupported("false"); } context.addFilterDef(filter); } for (FilterMap filterMap : webxml.getFilterMappings()) { context.addFilterMap(filterMap); } for (String listener : webxml.getListeners()) { context.addApplicationListener(listener);//這邊就是 添加我們監(jiān)聽類的地方 } } }仔細(xì)看下這個(gè)ContextConfig這個(gè)類 從中我們可以看到 Context的創(chuàng)建也是從這個(gè)類中完成的 這個(gè)記住下在繼續(xù)深入下
到了這邊 其實(shí) 已經(jīng)講的差不多了 但是 小伙伴們 一定想知道Tomcat 究竟是怎么去調(diào)用到 上面的執(zhí)行方法的我們知道listenerStart 中處理了監(jiān)聽事件 那這個(gè)又是怎么去運(yùn)行的呢 和Tomcat 又有什么關(guān)系呢,由于篇幅問題 我就不一一列出代碼了 大概的描述下1.tomcat-embed-core-8.5.45-sources.jar!\org\apache\catalina\startup\Tomcat.java 中有一個(gè)Start()方法 protected Server server; public void start() throws LifecycleException { getServer(); getConnector(); server.start(); }??public?interface?Server?extends?Lifecycle這其中 server.start();方法是Lifecycle接口中
2. tomcat-embed-core-8.5.45-sources.jar!\org\apache\catalina\util\LifecycleBase.java
public abstract class LifecycleBase implements Lifecycle{ public final synchronized void start() throws LifecycleException{ startInternal();//這邊執(zhí)行startInternal方法 } protected abstract void startInternal() throws LifecycleException;}public abstract class LifecycleMBeanBase extends LifecycleBasepublic abstract class ContainerBase extends LifecycleMBeanBase implements Container3.StandardContext 類 我們應(yīng)該很熟悉了吧public class StandardContext extends ContainerBase implements Context, NotificationEmitter{ protected synchronized void startInternal() throws LifecycleException { // Configure and call application event listeners if (ok) { if (!listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); ok = false; } } }}看到這個(gè)listenerStart 應(yīng)該清楚了吧 ~結(jié)合我上面講的 就能串聯(lián)上了~到此 我們應(yīng)該知道了 從Tomcat 到ContextLoaderListener.java 是怎么運(yùn)行起來的吧最后還是給大家畫個(gè)小圖吧 簡(jiǎn)單明了 方便記憶 畫圖工具用的markdown 自帶的 有點(diǎn)low 見諒哈總結(jié)一下
通過以上的分析 我們知道 ContextLoaderListener 是怎么被加入監(jiān)聽列表 contextInitialized 方法時(shí)怎么執(zhí)行的。●帶你領(lǐng)略史上最全—編譯部署EasyDarwin源碼【二次開發(fā)】【Linux】
●Thread類源碼(2)
●Thread類源碼分析(1)
●線程池(1)——線程池的使用
Java技術(shù)直播
點(diǎn)擊圖片直達(dá)課堂
覺得有幫助的話,點(diǎn)個(gè)“在看”吧!總結(jié)
以上是生活随笔為你收集整理的springmvc dao怎么可以不写实现类_SpringMVC(一)细聊ContextLoaderListener 是怎么被加载的...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 周三多管理学第七版pdf_为什么说管理学
- 下一篇: gradle idea java ssm