Spring源码——SpringMVC测试工程搭建

版权声明:本文为博主原创文章,转载注明出处即可。 https://blog.csdn.net/bskfnvjtlyzmv867/article/details/84147723

前言

内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。

本文主要涉及书中第十一章的部分,依照书中内容以及个人理解对Spring源码进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework

本文作为正式研究SpringMVC的预热文章,主要目的是为了搭建SpringMVC源码的测试工程。为什么要单独的作为一篇文章,因为发现想要测试SpringMVC的源码和之前的工程搭建还是不太一样的,笔者也调试了很久才终于搭建成功。

本文主要对应书中的第一部分,为了演示SpringMVC的使用案例,完全是为了复现书上的测试过程,过程中出现很多问题,记录下来以便参考。

I. 新建模块

首先我们准备新建一个专门测试SpringMVC的 module,不再使用之前文章测试模块 spring-mytest,新建的模块名称我们定义为 spring-mymvc。具体步骤见图中红框。

新建模块

spring-mymvc

新建完毕后可以看到,一个新的模块就创建好了。

目录结构

II. 添加依赖

build.gradle 配置文件中添加SpringMVC的依赖,配置文件如下。

plugins {
    id 'java'
    id 'war'
}

group 'org.springframework'
version '5.1.0.BUILD-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile(project(":spring-context"))
    compile(project(":spring-context-support")) // freemarker需要,不加可能报错
    compile(project(":spring-webmvc"))

    providedCompile 'javax.servlet:servlet-api:2.5'
    compile 'javax.servlet.jsp.jstl:jstl:1.2'
    compile 'org.apache.taglibs:taglibs-standard-spec:1.2.5'
    compile 'org.apache.taglibs:taglibs-standard-impl:1.2.5'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

依赖关系图如下。

依赖关系图

III. 编写示例案例

新建Web.xml

打开项目的 Project Structure 窗口,打开左侧的 FacetsArtifacts 两个窗口,删除之前创建好的内容,我们手动亲自创建一遍。

先进入 Facets 窗口,点击 + 号,选择 Web,然后会让我们选择模块。选中我们创建的 spring-mymvc-main,点击 OK

新建web.xml

新建成功后,会出现一个 Web 选项卡,点击选中,右侧会显示 web.xml 的位置。注意查看路径,IDEA默认的路径是在 .idea 目录下的,而我们最终是要在自己的测试模块的 webapp/WEB-INF/ 下的,所以要进行修改。

新建成功

点击两个路径右侧的绿色铅笔进行修改。如果没有 webapp/WEB-INF/ 目录则自行新建,最后路径末尾别忘记 web.xml,这样IDEA就会在 webapp/WEB-INF/ 目录下帮我们新建一个 web.xml

修改新建web.xml

然后修改 Web Resource Pathwebapp 目录即可。

Web Resource Path

创建完 Facets,随后创建 Artifacts

创建 Artifacts

选中我们的 spring-mymvc-main 模块,结果如下。之后我们解决问题都是在这个地方进行调整。

新建完毕

回到工程目录,此时 webapp/WEB-INF/ 目录下就存在 web.xml 文件了。修改文件内容,至于何种含义,相信会使用Spring的人不用多说。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

   <!--Spring配置文件-->
   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-config.xml</param-value>
   </context-param>

   <servlet>
      <servlet-name>mvc-test</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!--SpringMVC配置文件-->
      <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:springmvc-config.xml</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>mvc-test</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>

   <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   
</web-app>

创建配置文件

resources 目录下创建配置文件,分别是Spring配置文件 spring-config.xml,还有SpringMVC配置文件 springmvc-config.xml

spring-config.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

springmvc-config.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/userlist">userController</prop>
            </props>
        </property>
    </bean>

    <bean id="userController" class="guo.ping.mvctest.controller.UserController" />

</beans>

编写代码

① 实体类创建

这里我们用用户类作为示例。

public class User {

    private String username;
    private int age;

    public User(String username, int age) {
        this.username = username;
        this.age = age;
    }

    /** setter and getter */
}

② Controller

编写用户控制器 UserController 类。

public class UserController extends AbstractController {

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("hahha");
        List<User> userList = new ArrayList<>();
        User user1 = new User("张三", 12);
        User user2 = new User("李四", 21);
        userList.add(user1);
        userList.add(user2);
        return new ModelAndView("userlist", "users", userList);
    }
}

③ 创建页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<h2>This is SpringMVC demo page</h2>
<c:forEach items="${users}" var="user">
      <c:out value="${user.username}"/><br/>
      <c:out value="${user.age}"/><br/>
</c:forEach>

配置Tomcat

创建完成后,整个目录结构为:

目录结构

新建 Run/Debug Configurations,选择本地 Tomcat 服务器。

Run/Debug Configurations

新建 Deployment,点击 Apply,创建成功。

Deployment

IV. 运行测试

点击运行,发现没有启动成功,查看报错信息。server窗口报错信息如下:

server窗口报错

再次查看Tomcat日志报错,发现输出找不到 org.springframework.web.context.ContextLoaderListener 类。

Tomcat日志

想想不可能啊,明明我们在 gradle 中导入了依赖,配置文件也没有报错,我们决定去打包的 war 中一看究竟。进入 out 目录,我们惊讶的发现,打包内容根本不包含Spring的任何类,不仅如此,我们自己的配置文件也没有打包进去,怪不得找不到 ContextLoaderListener 类。

war包内容

那该怎么解决呢,我们的目的是把Spring的 java 文件也要打进包,还要把我们配置文件打入包中。

再次打开 Project Structure 窗口,查看红框中的内容,可以发现这里可以管理需要打包进 war 的内容。

1542378446801

打包配置文件

选中 classes 行,点击 + 号,选择 Directory Content,添加上我们模块的 resources 目录。

打包配置文件

结果如下:

结果

打包Spring其他模块

从右侧的 Available Elements 中选择 spring-webmvc_main,右键选择 Put into Output Root

打包Spring其他模块

结果如下:

结果

这下感觉有点像那么回事情了。再次点击运行按钮,发现依然报错。

16-Nov-2018 22:37:43.372 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar [file:/E:/spring_src/spring-framework/out/artifacts/spring_mymvc_main_war_exploded/WEB-INF/lib/javax.json.bind-api-1.0.jar] for annotations

······

16-Nov-2018 22:37:45.030 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar [file:/E:/spring_src/spring-framework/out/artifacts/spring_mymvc_main_war_exploded/WEB-INF/lib/money-api-1.0.3.jar] for annotations

······

16-Nov-2018 22:37:45.137 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar [file:/E:/spring_src/spring-framework/out/artifacts/spring_mymvc_main_war_exploded/WEB-INF/lib/jaxws-api-2.3.0.jar] for annotations

·····

16-Nov-2018 22:37:45.164 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar [file:/E:/spring_src/spring-framework/out/artifacts/spring_mymvc_main_war_exploded/WEB-INF/lib/javax.xml.soap-api-1.4.0.jar] for annotations

······

16-Nov-2018 22:37:45.175 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.startup.ContextConfig.processAnnotationsJar Unable to process Jar entry [module-info.class] from Jar [file:/E:/spring_src/spring-framework/out/artifacts/spring_mymvc_main_war_exploded/WEB-INF/lib/jaxb-api-2.3.0.jar] for annotations

这次报的错好像是部分 jar 包引起的,没办法,把报错的 jar 包都删了。

示例

好了,再次运行。果然不出所料,还是报错,这次报的又不一样了。报错的原因,表明不能加载 ContextLoader.properties 文件。

缺失ContextLoader.properties

那么这个文件哪来的呢,报错中显示是 org/springframework/web/context/ContextLoader.properties,其实这是 spring-web 模块中的配置文件。

ContextLoader.properties

我们再去 war 包中瞅一眼,发现确实没有。有了我们自己配置文件的打包经验,看来我们只要将 ContextLoader.properties 文件加入即可。

加入war中

再次运行。发现其实还需要将 spring-webmvc 模块中的 DispatcherServlet.properties 文件加入。

DispatcherServlet.properties

其实这时候已经可以发现 UserController 已经拦截到用户请求了,但是我们还要将 jstl 相关jar包加入。加入好了,这下终于成功了。
成功

V. 总结

  • bug总是能解决的,就是看解决多久了。
  • 源码是要看的,这样才能知道错在哪里,否则看着报错信息只能一头雾水。

猜你喜欢

转载自blog.csdn.net/bskfnvjtlyzmv867/article/details/84147723