springMVC笔记系列(18)——配置文件细节详解

本文着重说说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/WildFlyPayara/GlassFishWebLogic, 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>
相关文章
相关标签/搜索