本文着重說說springMVC項目中各個配置文件的一些細節。css
web.xml文件是web應用的部署描述。html
在上一節的springMVC示例中 ,idea下的Maven-webapp項目自動生成了web.xml文件,用的是webapp2.3的標準。文件頭聲明以下:前端
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
webapp2.3的標準的JSP頁面會默認地把EL表達式語言關閉。因此咱們須要把這個聲明替換掉,換成2.4及以上版本。來來來直接複製粘貼。java
webapp 2.4web
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:web="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd " version="2.4"> </web-app>
webapp2.5spring
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd " version="2.5"> </web-app>
在如此聲明以後,咱們的web應用就能夠默認地支持EL。express
(本文出自:http://my.oschina.net/happyBKs/blog/691502)apache
springMVC的核心是前端控制器。api
在上一節springMVC示例中,web.xml中僅僅定義了springMVC的前端控制器DispathcerServlet,而沒有定義spring容器。雖然這沒錯,可是在實際使用中,通常仍是會用到spring,因此咱們應該加入spring的聲明:在web.xml中需要配置spring的上下文環境配置文件的路徑和監聽器ContextLoaderListener。spring-mvc
這裏的上下文ApplicationContext是一個層次化的結構。
web.xml定義以下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd " version="2.5"> <display-name>Archetype Created Web Application</display-name> <!-- Spring應用上下文, 理解層次化的ApplicationContext --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value> </context-param> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- DispatcherServlet, Spring MVC的核心 --> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- DispatcherServlet對應的上下文配置, 默認爲/WEB-INF/$servlet-name$-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <!-- mvc-dispatcher攔截全部的請求--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
這裏面,咱們能夠到spring應用上下文有本身的一些配置文件:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/configs/spring/applicationContext*.xml</param-value> </context-param>
而springMVC的核心——DispatcherServllet也有本身相關的配置文件:
<servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- DispatcherServlet對應的上下文配置, 默認爲/WEB-INF/$servlet-name$-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
那麼,這兩組配置文件就構成了它們不一樣的應用上下文的層次。
咱們仍是經過一個圖來講明 springMVC的上下文層級:
圖中,咱們能夠看到兩塊web應用上下文(兩組WebApplicationContext)。
其中右邊那塊其實有多個WebApplicationContext。能夠理解爲咱們的根。
這個就是那個ContextLoaderListener所加載造成的上下文。它爲咱們提供了咱們全部應用公共所使用的組件或者服務如Service層、DAO層等等。這些組件或者服務應該是被整個應用所共享的,它不該該被侷限在某一個DispatcherServlet上下文之中。
另外一個WebApplicationContext(左邊的那個)就是與特定DispatcherServlet相關的上下文。好比說這裏的示例中配置文件中定義的mvc-dispatcher,與其先關的Controller、ViewResolver、HandlerMapping等等。
那麼,有沒有可能會存在多個DispatcherServlet呢?答案是確定的。前面提到了,既然這裏右邊的WebApplicationContext定義的是公共的通用的上下文,就是爲了在多個DispatcherServlet的狀況下所共有。
那麼爲何會出現須要DispatcherServlet的狀況呢?
用不一樣DispatcherServlet來分發不一樣類型的請求。好比,如今有一個網站,須要兩個DispatcherServlet,一個來處理通常用戶的訪問請求,另外一個用來是爲了處理一些特殊設備的如Webservice之類的請求服務。
在web.xml配置文件,對DispacherServlet經過 url-pattern指定了不一樣url來源請求的劃分依據。
<servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <!-- mvc-dispatcher攔截全部的請求--> <url-pattern>/</url-pattern> </servlet-mapping>
這個示例是一個dispatcherServelt攔截根「/」下的請求。好比,這個dispatcherServelt爲用戶提供一個相應的接口;咱們還能夠定義另外一個dispatcherServelt,將url-pattern設置爲 /ws,用來相應來自各種設備的webservice請求。從而實現了多個不一樣的dispatcherServelt對不一樣類型請求的分發。
以上是spring應用上下文的相關的配置文件的說明。
接下來咱們先看看DispatcherServlet相關的配置文件。
DispatcherServlet相關的配置文件
在上述web.xml的示例中指定了/WEB-INF/configs/spring/mvc-dispatcher-servlet.xml。以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 本配置文件是工名爲mvc-dispatcher的DispatcherServlet使用, 提供其相關的Spring MVC配置 --> <!-- 啓用Spring基於annotation的DI, 使用戶能夠在Spring MVC中使用Spring的強大功能。 激活 @Required @Autowired,JSR 250's @PostConstruct, @PreDestroy and @Resource 等標註 --> <context:annotation-config /> <!-- DispatcherServlet上下文, 只管理@Controller類型的bean, 忽略其餘型的bean, 如@Service --> <context:component-scan base-package="com.happyBKs.controller"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- HandlerMapping, 無需配置, Spring MVC能夠默認啓動。 DefaultAnnotationHandlerMapping annotation-driven HandlerMapping --> <!-- 擴充了註解驅動,能夠將請求參數綁定到控制器參數 --> <mvc:annotation-driven /> <!-- 靜態資源處理, css, js, imgs --> <mvc:resources mapping="/resources/**" location="/resources/" /> <!-- 配置ViewResolver。 能夠用多個ViewResolver。 使用order屬性排序。 InternalResourceViewResolver放在最後。 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--<property name="viewClass"--> <!--value="org.springframework.web.servlet.view.JstlView" />--> <property name="prefix" value="/WEB-INF/jsps/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
第一點:
<context:annotation-config />激活了spring基於註解的DI(依賴注入)。咱們Controller也是須要使用其餘的一些服務的,來調用業務邏輯,所以須要使用依賴注入的方式來獲取這些service,這項配置就完成了這項功能。
第二點:
下面配置component-scan。
這裏的include-filter項告訴咱們的DispatcherServlet的上下文,要找org.springframework.stereotype.Controller,(註解方式type="annotation"),只須要掃描component-scan的base-package定義的包就能夠了。或者也能夠說,當dispatcherServlet掃描component-scan的base-package定義的包時,只須要掃描包內的org.springframework.stereotype.Controller就能夠了,這裏不須要掃描Service,由於Service會交給咱們的spring上下文來進行處理。這正是咱們前面說的——層次化的上下文組織方式。
<context:component-scan base-package="com.happyBKs.controller"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
第三點:
上一節講到springMVC概念的時候說到了HandlerMapping。對它,這裏咱們不需要作什麼配置。由於springMVC的默認功能已經將其約定好了!若是咱們不在mvc-dispatcher-servlet.xml進行任何HandlerMapping的配置,springMVC將啓用其默認的配置。在這個默認配置中,springMVC會啓用一個DefaultAnnotationHandlerMapping的類,這個類就能夠幫助咱們解析一些基於註解的DefaultAnnotationHandlerMapping。正如mvc-dispatcher-servlet.xml中被註釋掉的一段:
<!-- HandlerMapping, 無需配置, Spring MVC能夠默認啓動。 DefaultAnnotationHandlerMapping annotation-driven HandlerMapping -->
第四點:
<mvc:annotation-driven />擴充了註解驅動,做用是能夠將請求參數綁定到控制器參數。也就說,你的url查詢參數中某一個變量,能夠映射到Controller的某個方法的輸入參數。能夠翻閱本系列我以前的幾篇文章,曾經介紹過。
第五點:
<mvc:resources mapping="/resources/**" location="/resources/" />可以幫助咱們得到靜態資源,如Js、Css文件、圖片等。
好比這裏,咱們將"/resources/** (請求resources下的各個url路徑的靜態資源)映射到location="/resources/"即本地項目的/resources目錄下。
第六點:
配置ViewResolver。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsps/" /> <property name="suffix" value=".jsp" /> </bean>
這裏須要強調的是,咱們能夠在一個DispatcherServlet中配置多個ViewResolver。多個ViewResolver之間可使用order屬性進行排序。
注意:InternalResourceViewResolver需要放在全部ViewResolver的最後,由於它一定會返回一個對象。(當按照順序的ViewResolver知足,則返回對象;不然看下一個ViewResolver。InternalResourceViewResolver一定返回)這個 最後返回的對象不必定是咱們所須要的,因此也從另外一個方面說明應該放在最後。
Spring上下文相關的配置文件
咱們在/WEB-INF/configs/spring/下新建applicationContext.xml。(在web.xml中咱們已經定義了spring上下文相關的配置文件爲/WEB-INF/configs/spring/applicationContext*.xml)
applicationContext.xml以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:annotation-config /> <context:component-scan base-package="com.happyBKs.controller"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> </beans>
applicationContext.xml就是用來放置它或者它的兄弟配置文件,共同組成了咱們的應用中通用的組件的spring的bean管理。
這個文件示例有兩點注意:
第一點:
<context:annotation-config />啓用基於註解的DI管理。
第二點:
exclude-filter告訴spring,這裏咱們不需要管理Controller了。
<context:component-scan base-package="com.happyBKs.controller"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan>
好本節的正片環節就這麼多。
——————————————————————————
接下來是一個問題的解決。當我在上述更改的基礎上,編譯運行這個webapp。發現請求http://localhost:8080/mvc/hello/world時,服務器崩了。
HTTP Status 500 - Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config type Exception report message Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config description The server encountered an internal error that prevented it from fulfilling this request. exception org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config org.springframework.web.servlet.DispatcherServlet.triggerAfterCompletionWithError(DispatcherServlet.java:1305) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:979) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) javax.servlet.http.HttpServlet.service(HttpServlet.java:624) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) javax.servlet.http.HttpServlet.service(HttpServlet.java:731) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) root cause java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config org.springframework.web.servlet.support.JstlUtils.exposeLocalizationContext(JstlUtils.java:101) org.springframework.web.servlet.view.JstlView.exposeHelpers(JstlView.java:135) org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:142) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1246) org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1029) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:973) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) javax.servlet.http.HttpServlet.service(HttpServlet.java:624) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) javax.servlet.http.HttpServlet.service(HttpServlet.java:731) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) root cause java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720) org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571) org.springframework.web.servlet.support.JstlUtils.exposeLocalizationContext(JstlUtils.java:101) org.springframework.web.servlet.view.JstlView.exposeHelpers(JstlView.java:135) org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:142) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1246) org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1029) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:973) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) javax.servlet.http.HttpServlet.service(HttpServlet.java:624) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) javax.servlet.http.HttpServlet.service(HttpServlet.java:731) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) note The full stack trace of the root cause is available in the Apache Tomcat/7.0.62 logs. Apache Tomcat/7.0.62
我分析應該是缺乏某個包。stackoverflow上也有人遇到相同的問題:有人對緣由做出瞭解答:
As the package name hints, the mentioned class is part of JSTL. The exception is clearly telling that the class definition file of the mentioned class cannot be found in the runtime classpath. I.e. the Config.class
file or at least the JAR file containing that class is missing in webapp's runtime classpath.
JSTL is normally already provided out the box by a full fledged Java EE container such as TomEE,JBoss AS/EAP/WildFly, Payara/GlassFish, WebLogic, etc but not by barebones JSP/Servlet containers such as Tomcat and Jetty. For them, you'd need to supply JSTL along with the web application yourself, exactly like as you'd do for JSF (which is also already provided out the box by full fledged Java EE containers).
You're facing this exception because Facelets has for its <c:xxx>
tags a dependency on the JSTL implementation JAR file, while you're using Tomcat which doesn't ship with JSTL out the box. JSTL is as a separate library available in flavor of jstl-1.2.jar. Just download and drop it in your webapp's /WEB-INF/lib
folder, along with the JSF JAR file(s) which you already placed there, and this exception should disappear. Maven users can achieve that by adding the below dependency (and performing a full rebuild/redeploy):
<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
Alternatively, just replace Tomcat by a real Java EE container.
看到了吧,緣由在tomcat自己不是一個真正的Java EE web容器。所以須要添加jstl的jar。我在Maven的POM中已經添加了相應的依賴包座標。pom.xml更新以下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>testIdea.happBKs.mvc</groupId> <artifactId>springMVC</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>springMVC Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <commons-lang.version>2.6</commons-lang.version> <slf4j.version>1.7.21</slf4j.version> <spring.version>4.2.6.RELEASE</spring.version> <jackson.version>2.7.4</jackson.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-framework-bom</artifactId> <version>${spring.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>${commons-lang.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> <exclusions> <exclusion> <artifactId>slf4j-api</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!--<dependency>--> <!--<groupId>javax.servlet</groupId>--> <!--<artifactId>jsp-api</artifactId>--> <!--<version>2.0</version>--> <!--</dependency>--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>springMVC</finalName> </build> </project>