摘要:可以發(fā)現(xiàn),這兩個(gè)類都是可以被實(shí)例化的,且構(gòu)造器不需要參數(shù)。這段代碼的后半部分其實(shí)沒有什么新意,但下半部分的第一行非常關(guān)鍵接受一個(gè)作為構(gòu)造器參數(shù)這實(shí)際上解決了我們?cè)诘谒恼聹y(cè)試失敗后反思的可能的疑惑我們配置的容器實(shí)際上并沒有和融合起來。
如何向一個(gè)WebApp引入Spring與Spring MVC 1
在Servlet 3.0環(huán)境中,容器(加載運(yùn)行webapp的軟件,如Tomcat)會(huì)在類路徑中查找實(shí)現(xiàn)==javax.servlet.ServletContainerInitializer==接口的類(這一行為本質(zhì)上是Java EE標(biāo)準(zhǔn)和協(xié)定所要求的,Tomcat是基于該協(xié)定的一種實(shí)現(xiàn)),如果能發(fā)現(xiàn)的話,就會(huì)用它來配置Servlet容器。
Spring提供了這個(gè)接口的實(shí)現(xiàn),名為SpringServletContainerInitializer,因此一個(gè)引入的SringMVC的web項(xiàng)目在沒有其它設(shè)置的情況下會(huì)被Tomcat找到SpringServletContainerInitializer。
SpringServletContainerInitializer
2==SpringServletContainerInitializer==又會(huì)查找實(shí)現(xiàn)==WebApplicationInitializer==接口的類并調(diào)用其onStartup(ServletContext servletContext)方法,其中ServletContext對(duì)象由其負(fù)責(zé)將服務(wù)器生成的唯一的ServletContext實(shí)例傳入。
WebApplicationInitializer
Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically -- as opposed to (or possibly in conjunction with) the traditional web.xml-based approach.
ServletContext
Defines a set of methods that a servlet uses to communicate with its servlet container, for example,
到目前位置,我們已經(jīng)可以使用SpringMVC來增設(shè)Servlet了,雖然這看起來并不美觀也不簡(jiǎn)便。代碼如下所示。
package spittr.config; import org.springframework.web.WebApplicationInitializer; import spittr.web.AServlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; public class SpittrWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { //增加一個(gè)Servelt 其中AServlet是Servlet接口的實(shí)現(xiàn)類,我的實(shí)現(xiàn)直接繼承了HttpServlet ServletRegistration.Dynamic aServlet = servletContext.addServlet("AServlet", AServlet.class); //為AServlet增設(shè)映射路徑,其作用等同于@WebServlet(urlPatterns={"/AServlet"}) aServlet.addMapping(new String[]{"/AServlet"}); } }
package spittr.web; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class AServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=utf-8"); PrintWriter writer = resp.getWriter(); writer.write("我收到了你的GET"); } }
現(xiàn)在我們可以向?yàn)g覽器直接訪問AServlet
然而,這樣的實(shí)現(xiàn)在美觀和便利上遠(yuǎn)遠(yuǎn)不如使用Servlet3.0引入和更新的@WebServlet等機(jī)制。
并且完全沒有涉及Spring和Spring MVC,只是按照Servlet3.0的標(biāo)準(zhǔn)的一種添加Servlet的方式罷了。
那么接下來我們就要開始引入Spring和Spring MVC了。
3第一步肯定是引入Spring,也即引入一個(gè)Spring的容器。
這很簡(jiǎn)單,在onStartup中實(shí)例化一個(gè)ApplicationContext的實(shí)例即可。查詢ApplicationContext的javadoc,看到目前所有的ApplicationContext實(shí)現(xiàn)類:
All Known Implementing Classes:
AbstractApplicationContext, AbstractRefreshableApplicationContext, AbstractRefreshableConfigApplicationContext, AbstractRefreshableWebApplicationContext, AbstractXmlApplicationContext, AnnotationConfigApplicationContext, AnnotationConfigWebApplicationContext, ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, GenericApplicationContext, GenericGroovyApplicationContext, GenericWebApplicationContext, GenericXmlApplicationContext, GroovyWebApplicationContext, ResourceAdapterApplicationContext, StaticApplicationContext, StaticWebApplicationContext, XmlWebApplicationContext
而我們打算使用基于Java代碼的配置并開啟基于注解的自動(dòng)掃描,同時(shí)應(yīng)用場(chǎng)景為webapp,所以應(yīng)該使用AnnotationConfigWebApplicationContext實(shí)現(xiàn)類。
綜上所述,可以得到如下代碼:
package spittr.config; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.web.WebApplicationInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; public class SpittrWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(AppConfig.class); } }
@Configuration @ComponentScan("spittr.web") public class AppConfig { }
至此,我們已經(jīng)在這個(gè)webapp中集成了Spring容器,從理論上講,我們應(yīng)該可以對(duì)一個(gè)Servlet標(biāo)注@Controller后使其自動(dòng)被注冊(cè)和使用。但是由于@RequestMapping我們還不知道能不能用,實(shí)際上無法對(duì)其進(jìn)行測(cè)試(因?yàn)榧幢銓⒎?wù)器注冊(cè)到了Spring容器中,我們也無法為它配置映射路徑)。
那么現(xiàn)在就該去解決@RequestMapping了。
4javadoc:@RequestMapping。
@RequestMapping javadoc這一注解做了如下解讀
Annotation for mapping web requests onto methods in request-handling classes with flexible method signatures.
Both Spring MVC and Spring WebFlux support this annotation through a RequestMappingHandlerMapping and RequestMappingHandlerAdapter in their respective modules and package structure. For the exact list of supported handler method arguments and return types in each, please use the reference documentation links below:
Spring MVC Method Arguments and Return Values
Spring WebFlux Method Arguments and Return Values
Note: This annotation can be used both at the class and at the method level. In most cases, at the method level applications will prefer to use one of the HTTP method specific variants @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, or @PatchMapping.
NOTE: When using controller interfaces (e.g. for AOP proxying), make sure to consistently put all your mapping annotations - such as @RequestMapping and @SessionAttributes - on the controller interface rather than on the implementation class.
其中最重要的在第二段,它說明了Spring MVC通過使用RequestMappingHandlerMapping 和RequestMappingHandlerAdapter 得以支持@RequestMappin注解。
javadoc:RequestMappingHandlerMapping
javadoc:RequestMappingHandlerAdapter
可以發(fā)現(xiàn),這兩個(gè)類都是可以被實(shí)例化的,且構(gòu)造器不需要參數(shù)。
既然如此,我們可以試著在AppConfig中配置這兩個(gè)類。
@Configuration @ComponentScan("spittr.web") public class AppConfig { @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){ return new RequestMappingHandlerAdapter(); } @Bean public RequestMappingHandlerMapping requestMappingHandlerMapping(){ return new RequestMappingHandlerMapping(); } }
然后使用帶@Controller和@RequestMapping的類
package spittr.web; @Controller @RequestMapping("/BServlet") public class BServlet{ @RequestMapping(method = RequestMethod.GET) public void doGet() { System.out.println("BServlet:我收到了你的GET"); } }
不過測(cè)試結(jié)果是糟糕的,我們沒有如愿實(shí)現(xiàn)訪問BServlet。
失敗的原因沒有官方文檔直接告知,但結(jié)合之后進(jìn)一步的學(xué)習(xí),不難猜測(cè)理由應(yīng)該是:我們AppConfig的Spring-beans容器其實(shí)沒有和Servlet容器結(jié)合起來。我們只是在onStartUp方法中實(shí)例化了一個(gè)Spring-beans容器,甚至可以認(rèn)為在方法的生命周期結(jié)束之后,這個(gè)實(shí)例就直接沒了。如若真的如此,我們就連實(shí)際上把Spring集成到這個(gè)WebApp中都沒有做到,怎么可能做到開啟Spring MVC注解呢。
5事已至此,就只能閱讀官方文檔了。官方文檔
開門見山地:
Spring MVC, as many other web frameworks, is designed around the front controller pattern where a central Servlet, the DispatcherServlet, provides a shared algorithm for request processing, while actual work is performed by configurable delegate components. This model is flexible and supports diverse workflows.
→Spring MVC圍繞一個(gè)前線控制器模式(front controller pattern)而設(shè)計(jì),在這種模式下一個(gè)核心Servlet,也就是DispatchereServlet(由Spring實(shí)現(xiàn)的Servlet類),會(huì)為處理客戶端請(qǐng)求提供了算法,而真正的工作(處理請(qǐng)求)由可配置的代理組件來執(zhí)行。
因此可以認(rèn)為,要充分利用SpringMVC,必然要加載SpringMVC自行實(shí)現(xiàn)的Servlet類:org.springframework.web.servlet.DispatcherServlet。
org.springframework.web.servlet.DispatcherServlet
官方文檔給出了一段初始化代碼:
public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletCxt) { // Load Spring web application configuration AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); //AppConfig是自定義的帶@Configuration注解的類 ac.register(AppConfig.class); ac.refresh(); // Create and register the DispatcherServlet // 將Spring容器與DispatcherServlet綁定 DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
這段代碼的前半部分,我們是很熟悉的。第三章就做過。
這段代碼的后半部分其實(shí)沒有什么新意,但下半部分的第一行非常關(guān)鍵
DispatcherServlet servlet = new DispatcherServlet(ac);
接受一個(gè)AnnotationConfigWebApplicationContext作為構(gòu)造器參數(shù)!這實(shí)際上解決了我們?cè)诘谒恼聹y(cè)試失敗后反思的可能的疑惑——我們配置的Spring容器實(shí)際上并沒有和tomcat融合起來。
那么現(xiàn)在,將官方代碼中的ac換成我們自己的,是不是就能成功了呢?不妨一試
public class SpittrWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(AppConfig.class); DispatcherServlet dispatcher = new DispatcherServlet(ac); ServletRegistration.Dynamic d = servletContext.addServlet("dispatcher", dispatcher); d.setLoadOnStartup(1); d.addMapping("/"); } } /* AppConfig BServlet 相較之前完全沒有變化,所以不展示 */
結(jié)果是喜人的,我們嘗試成功了。可以看到輸出BServlet:我收到了你的GET
官方文檔進(jìn)一步說明:
The DispatcherServlet, as any Servlet, needs to be declared and mapped according to the Servlet specification by using Java configuration or in web.xml. In turn, the DispatcherServlet uses Spring configuration to discover the delegate components it needs for request mapping, view resolution, exception handling, and more.The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config):
這段話應(yīng)該分成這兩個(gè)部分:
The DispatcherServlet, as any Servlet, needs to be declared and mapped according to the Servlet specification by using Java configuration or in web.xml.The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config):
這一部分上來先說,DispatcherServlet就像任何Servlet一樣,也是需要做好聲明和映射的。下面的代碼介紹了使用Servlet container提供的自動(dòng)探測(cè)注冊(cè)功能來注冊(cè)和初始化DispatcherServlet。這里所謂的Servlet container的自動(dòng)探測(cè),其實(shí)就是指之前提到的1,2兩個(gè)階段。
In turn, the DispatcherServlet uses Spring configuration to discover the delegate components it needs for request mapping, view resolution, exception handling, and more.
這一部分說,DispatcherServlet被配置注冊(cè)好之后,也可以反過來使用Spring配置來發(fā)現(xiàn)和委派為它為請(qǐng)求映射,視圖渲染,異常處理所需要的組件。
那么,DispatcherServlet要如何反過來配置它自己的組件呢?帶著這一疑問,我們繼續(xù)往下看。
官方文檔緊接著提到了一個(gè)WebApplicationInitializer的Spring實(shí)現(xiàn)類AbstractAnnotationConfigDispatcherServletInitializer,它可以避免直接使用ServletContext(它自己已經(jīng)用了),通過重寫特定的方法完成配置。
In addition to using the ServletContext API directly, you can also extendAbstractAnnotationConfigDispatcherServletInitializer and override specific methods (see the example under Context Hierarchy).
跟隨Context Hierarchy超鏈接一探究竟。先放上example code:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class>[] getRootConfigClasses() { return new Class>[] { RootConfig.class }; } @Override protected Class>[] getServletConfigClasses() { return new Class>[] { App1Config.class }; } @Override protected String[] getServletMappings() { return new String[] { "/app1/*" }; } }
再看文字說明
DispatcherServlet expects a WebApplicationContext (an extension of a plain ApplicationContext) for its own configuration.
DispatcherServlet為它自己的配置需要一個(gè)WebApplicationContext(ApplicationContext的子接口)即一個(gè)Spring容器的配置實(shí)現(xiàn)類。
WebApplicationContext has a link to the ServletContext and the Servlet with which it is associated. It is also bound to the ServletContext such that applications can use static methods on RequestContextUtils to look up the WebApplicationContextif they need access to it.
一個(gè)Spring容器與ServletContext和與它共生的Servlet又關(guān)聯(lián)。這個(gè)Spring容器因?yàn)榻壎⊿ervletContext,所以也可以通過類RequestContextUtils的靜態(tài)方法去得到。
For many applications, having a single WebApplicationContext is simple and suffices. It is also possible to have a context hierarchy where one root WebApplicationContext is shared across multiple DispatcherServlet (or other Servlet) instances, each with its own child WebApplicationContext configuration. See Additional Capabilities of the ApplicationContext for more on the context hierarchy feature.
絕大部分應(yīng)用來說,一個(gè)Spring容器就夠用了。但也可以有一個(gè)有層級(jí)的容器結(jié)構(gòu)——一個(gè)根Spring容器在多個(gè)(全部)Servlet實(shí)例中共享,同時(shí)每個(gè)Servlet實(shí)例也有自己的WebApplicationContext配置。
Java EE和Servlet3.0標(biāo)準(zhǔn)的Servlet接口其實(shí)是不支持Servlet實(shí)例共生一個(gè)ApplicationContext的,因?yàn)楹笳弋吘故荢pring的專屬。所以這里的Servlet實(shí)例考慮為像DispatcherServlet這樣由Spring實(shí)現(xiàn)并提供的類,而不包括用戶自定義的符合Java EE和Servlet3.0標(biāo)準(zhǔn)的Servlet接口的Servlet。
The root WebApplicationContext typically contains infrastructure beans, such as data repositories and business services that need to be shared across multiple Servlet instances. Those beans are effectively inherited and can be overridden (that is, re-declared) in the Servlet-specific child WebApplicationContext, which typically contains beans local to the given Servlet.
在層級(jí)話的Spring容器結(jié)構(gòu)中,根Spring容器通常包含基礎(chǔ)設(shè)施的組件,比如數(shù)據(jù)持久化層,商業(yè)服務(wù)層這種需要在各種Servlet中共享的組件。這些組件能夠被有效地繼承地同時(shí),也可以被在Servlet相關(guān)的子Spring容器中被重新配置,使得組件可以針對(duì)給定的Servlet因地制宜。
到這里再回看代碼。
protected Class>[] getRootConfigClasses() { return new Class>[] { RootConfig.class }; }
顯然,這里的RootConfig.class是用戶自定義的帶@Configuration注解的Spring容器配置類,用以實(shí)現(xiàn)根Spring容器。
@Override protected Class>[] getServletConfigClasses() { return new Class>[] { App1Config.class }; }
這個(gè)就是AbstractAnnotationConfigDispatcherServletInitializer默認(rèn)實(shí)現(xiàn)的那個(gè)DispatcherServlet的伴生Spring容器配置。
protected String[] getServletMappings() { return new String[] { "/app1/*" }; }
這個(gè)則是確定AbstractAnnotationConfigDispatcherServletInitializer默認(rèn)實(shí)現(xiàn)的那個(gè)DispatcherServlet所要管理的request URI映射。
至此1.1.1 Context Hierarchy結(jié)束,我們之前就是根據(jù)超鏈接跳到這一章節(jié)的,這一章節(jié)結(jié)束后,我們返回之前的位置繼續(xù)閱讀文檔。
發(fā)現(xiàn)緊接著就又是1.1.1 Context Hierarchy,直接跳過讀下一章。
71.1.2. Special Bean TypesThe DispatcherServlet delegates special beans to process requests and render the appropriate responses. By “special beans” we mean Spring-managed Object instances that implement framework contracts. Those usually come with built-in contracts, but you can customize their properties and extend or replace them.
1.1.3. Web MVC ConfigApplications can declare the infrastructure beans listed in Special Bean Types that are required to process requests. The DispatcherServlet checks the WebApplicationContext for each special bean. If there are no matching bean types, it falls back on the default types listed in DispatcherServlet.properties.
In most cases, the MVC Config is the best starting point. It declares the required beans in either Java or XML and provides a higher-level configuration callback API to customize it.
這兩個(gè)部分回答了我們的問題——DispatcherServlet要如何反過來配置它自己的組件——DispatcherServlet將會(huì)搜索它可以訪問的WebApplicationContext(這包括根Spring容器和它自己伴生的子Spring容器)來查找每個(gè)special bean——即被委派來處理請(qǐng)求渲染回應(yīng)等工作的組件——的設(shè)置。如果沒有的話,它將使用默認(rèn)的,保存在DispatcherServlet.properties中的設(shè)定。
很好理解的,我們之前所寫的AppConfig中的兩個(gè)Bean,它們是那么的基礎(chǔ)——由Spring提供和實(shí)現(xiàn),我們只是new出來什么自定義也沒有——以至于使用DispatcherServlet的默認(rèn)配置也不會(huì)更糟糕。所以我們?nèi)サ糁暗腁ppConfig的配置,僅僅留下一個(gè)空的AppConfig,其它代碼不變。
再次測(cè)試,仍然能夠收到BServlet的輸出。
總結(jié)到這里,對(duì)于如何將Spring和Spring MVC集成到一個(gè)WebApp中的過程以及為什么可以集成進(jìn)來已經(jīng)分析得差不多了。
更進(jìn)一步得學(xué)習(xí)Spring MVC,就繼續(xù)仔細(xì)閱讀官方文檔吧!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/75500.html
摘要:概述回顧上兩章,我們主要分析了的概念作業(yè)以及如何使用的組件,本節(jié)以及后續(xù)幾章,將介紹為我們提供的的具體實(shí)現(xiàn)類,基于源碼和設(shè)計(jì)層面進(jìn)行介紹,歡迎大家關(guān)注。本系列文章是基于。 概述 回顧上兩章,我們主要分析了HandlerAdapter的概念、作業(yè)以及Spring MVC如何使用的HandlerAdapter組件,本節(jié)以及后續(xù)幾章,將介紹Spring為我們提供的HandlerAdapter...
摘要:前情提要最近做了一個(gè)項(xiàng)目,項(xiàng)目是結(jié)構(gòu)的,但是在發(fā)布生產(chǎn)環(huán)境的時(shí)候又需要用打成包,但是一開始的默認(rèn)配置都不成功。壹項(xiàng)目結(jié)構(gòu)貳異常現(xiàn)象使用的為,版本為打成的只包含文件,沒有見資源文件引入。 【前情提要】最近做了一個(gè)項(xiàng)目,項(xiàng)目是springboot+jsp結(jié)構(gòu)的,但是在發(fā)布生產(chǎn)環(huán)境的時(shí)候又需要用maven打成jar包,但是一開始的默認(rèn)配置都不成功。下面的文章就是具體的解決過程。 壹、項(xiàng)目結(jié)...
摘要:最后創(chuàng)建了群方便大家交流,可掃描加入,同時(shí)也可加我,共同學(xué)習(xí)共同進(jìn)步,謝謝 創(chuàng)建項(xiàng)目 創(chuàng)建web項(xiàng)目,使用maven webapp模板進(jìn)行構(gòu)建,創(chuàng)建完成后,在pom中引入Spring MVC依賴,如下: org.springframework spring-webmvc 5.0.5.RELEASE javax.servlet ja...
摘要:更多相關(guān)博文參考前一篇博文講了的方式創(chuàng)建應(yīng)用,用過的童鞋都知道,早就沒有什么事情了,其實(shí)的版本,就已經(jīng)支持,不用再寫本篇將介紹下,如何利用取代配置本篇博文,建議和上一篇對(duì)比看,貼出上一篇地址之基于配置的應(yīng)用構(gòu)建構(gòu)建項(xiàng)目依賴對(duì)于依賴 更多spring相關(guān)博文參考: http://spring.hhui.top 前一篇博文講了SpringMVC+web.xml的方式創(chuàng)建web應(yīng)用,用過S...
閱讀 1258·2021-09-04 16:41
閱讀 2416·2021-09-02 10:18
閱讀 924·2019-08-29 16:40
閱讀 2620·2019-08-29 16:14
閱讀 911·2019-08-26 13:41
閱讀 1307·2019-08-26 12:24
閱讀 737·2019-08-26 10:24
閱讀 2878·2019-08-23 17:54