Spring+Velocity+Mybatis整合笔记(step by step)

一、开发工具

开发过程中使用的操作系统是OS X,关于软件安装的问题请大家移步高效的Mac环境设置。
本文是我对自己学习过程的一个回顾,应该还有不少问题待改进,例如目录的设置、编码习惯和配置文件的处理等,请大家多多指正。
文中用到的开发工具列举如下:

  • JDK 1.7.0_79
  • Intellij IDEA Ultimate 14
  • Mysql 5.6.25
  • Maven 3
  • Git、SourceTree

二、新建工程

1. 新建空的maven工程

第一步新建工程,选择Maven工程,如图1所示。注意,“create from archetype”是一些Maven的工程模板,在这里我们为了学习要从头开始自己配置。
Spring+Velocity+Mybatis整合笔记(step by step)_第1张图片
图1 新建Maven工程

点击next,出现设置工程坐标的页面,如图2所示。GroupId 是公司组织的标号;ArtifactId是项目名称;综合来看,在src/main/java目录下会新建对应的包结构:GroupId.ArtifactId。

Spring+Velocity+Mybatis整合笔记(step by step)_第2张图片
图2 设置工程坐标

点击next,出现设置工程名字的界面,如图3所示。这里跟之前的ArtifactId一样就可以,设置完后选择finish完成工程构建。我们这个示例项目采用的是单模块项目,我猜这里是设置模块相关的吧,还需继续学习。
Spring+Velocity+Mybatis整合笔记(step by step)_第3张图片
图3 设置工程名字

2. 编辑.gitignore文件

我们不需要从头开始写.gitignore文件,已经有人为我们准备好了模板文件,只需要在模板文件的基础上稍作修改即可。

首先要给IDEA安装.gitignore插件,然后在工程名字上右击建立.gitignore文件,通过插件可以根据项目的内容选择需要忽略的文件或者文件夹。在这里我选择了三个类型:Java、JetBrains、OSX三个模板的组合文件。因为Maven编译生成的目录名为target,我又增加了target文件夹的忽略,如图4所示。
Spring+Velocity+Mybatis整合笔记(step by step)_第4张图片
图4 .gitignore文件内容

3. 初始化仓库

通过SourceTree,创建本地仓库,将目标路径设置为usersDemo工程的根目录,如图5所示。仓库初始化完成后,我们的项目就在Git管理之下了,可以开始下一步了。
Spring+Velocity+Mybatis整合笔记(step by step)_第5张图片
图5 使用sourceTree

三个月后补充:极力推荐大家使用IDEA自带的git插件,非常棒,可以可视化解决冲突。

三、Spring MVC支持

1. 添加Spring MVC库以及servlet库

首先在pom.xml文件中修改配置,通过properties标签统一管理依赖库的版本,方便后续更新;通过dependencies标签管理所有的库依赖,本次增加的配置代码如下所示:

    
        4.1.7.RELEASE
    

    
        
        
            org.springframework
            spring-web
            ${spring.version}
        
        
            org.springframework
            spring-webmvc
            ${spring.version}
        
        
            org.springframework
            spring-core
            ${spring.version}
        

        
        
            javax.servlet
            servlet-api
            2.5
            provided 
        
    

接下来为项目增加Spring MVC框架支持,也就是每个web项目都应该有的web文件夹等等。具体操作如图6和图7所示。其中Spring MVC框架的库已经不用下载,使用我们之前在pom中下载好的库即可。
Spring+Velocity+Mybatis整合笔记(step by step)_第6张图片
图6 Add Framework Support

选择相应的Web框架和库文件
Spring+Velocity+Mybatis整合笔记(step by step)_第7张图片
图7 Add Framework Support

添加完成后,要对项目的目录结构做一些调整:将web文件夹移动到src/main/目录下,并重命名为webapp,调整后的目录结构如图8所示。
Spring+Velocity+Mybatis整合笔记(step by step)_第8张图片
图8 调整后的目录结构

2. web.xml

web.xml的作用是配置DispatcherServlet,在SpringMVC项目中DispatcherServlet作为前端控制器。服务器给用户的接口名并不是真正的servlet类的名字,只是一个逻辑名称,由DispatcherServlet完成这个逻辑名称到真正的servlet类的映射过程。

在web.xml的代码中,org.springframework.web.servlet.DispatcherServlet的实例名称为usersDemo,这个servlet-name 非常重要,默认情况下,DispatcherServlet在加载时会从一个机遇这个servlet名字的XML文件中加载Spring应用上下文,在这里,因为servlet-name是usersDemo,所以DispatcherServlet将会从usersDemo-servlet.xml文件中加载应用上下文。现在项目的目录结构图如图9所示。
Spring+Velocity+Mybatis整合笔记(step by step)_第9张图片
图9 当前项目目录结构

通过servlet-mapping标签指定由usersDemo这个DispatcherServlet实例处理哪些映射,在这里我们设置为“/”,即声明该DispatcherServlet实例会吹所有的请求,包括静态资源的请求。

最后,web.xml的代码列举如下:



    
        contextConfigLocation
        /WEB-INF/applicationContext.xml
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
        usersDemo
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            WEB-INF/usersDemo-servlet.xml
        
        1
    
    
        usersDemo
        /
    

3. usersDemo-servlet.xml

DispatcherServlet需要咨询一个或者多个处理器映射器来决定要将请求发送给哪个控制器,我们这里常用的处理器映射器是DefaultAnnotationHandlerMapping:即将请求映射给使用@RequestMapping注解的控制器和控制器方法。

最新的Spring的发展趋势是依靠注解来减少XML配置,因此我们在usersDemo.xml中添加下面一行配置,就可以得到Spring MVC提供的注解驱动测试


我们将会给控制器类添加@Controller来表明这是一个控制器类,这个类是@Component的子类,也就是说可以通过"context:component-scan标签"来查找控制器类并将其自动注册为Bean。需要再usersDemo.xml中添加下面一行配置:


4. 控制器

经过了上一步的铺垫,控制器的代码比较简单。@Controller注解告诉Spring这是一个控制器类,要将它注册为Bean;@RequestMapping注解告诉Spring将"/showUsers“接口,并且HTTP方法是GET的请求由showUser方法处理。

在showUser方法中我们使用servlet直接打印HTTP响应内容,很熟悉的hello系列。

UsersController的代码列举如下:

package com.alibaba.yunos.usersDemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created by duqi on 15/8/19.
 */

@Controller
public class UsersController {

    @RequestMapping(value = "/showUser",method = RequestMethod.GET)
    public void showUser(HttpServletResponse response) throws IOException {
        response.getWriter().print("

Hello SpringMVC

"); response.flushBuffer(); } }

5. 启动web服务

  • 配置maven的构建过程
    在pom文件中要增加两个配置,第一个是打包格式;第二个是build生成文件的名称。
    相关的配置代码为:
war

    usersDemo

  • 配置并运行tomcat
    在IDEA中配置tomcat的步骤有下面几步:
    (1)在屏幕右上角,选择”Edit Configuration“,如图10所示。
    Spring+Velocity+Mybatis整合笔记(step by step)_第10张图片
    图10 Edit Configuration

    (2)选择建立一个本地的tomcat容器,如图11所示。
    Spring+Velocity+Mybatis整合笔记(step by step)_第11张图片
    图11 Run/Debug Configuration

    (3)配置项目的发布方式为:usersDemo:war exploded,并且将应用程序上下文设置为"/usersDemo",如图12所示。
    Spring+Velocity+Mybatis整合笔记(step by step)_第12张图片
    图12 配置部署方式和上下文环境

    (4)配置tomcat容器的安装地址、启动服务器后是否需要自动启动浏览器、有文件修改或者检查到新的框架时容器如何反应,我们这里选择”update classes and resources“,如图13所示。
    ![图13 配置tomcat服务器信息](http://img.blog.csdn.net/20150819133940882)
    (5)启动tomcat容器,URL为:http://localhost:8080/usersDemo/showUser。
    最后的运行效果如图所示:
    Spring+Velocity+Mybatis整合笔记(step by step)_第13张图片
    图14 访问效果图

6. 代码提交

使用SourceTree对刚才修改和增加的代码进行提交,如图15所示,对于commit message要尽量简洁。
Spring+Velocity+Mybatis整合笔记(step by step)_第14张图片
图15 提交commit

四、Velocity支持

Velocity的存在是为了辅助前后端分离:后端接口开发人员可以专心于提供数据、前端人员可以使用占位符(模板文件)暂时代替数据。渲染:将占位符替换为真正的变量值,并生成最终的网页页面。

1. 添加Velocity支持库

首先在pom.xml中编辑,下载Velocity的支持库,包括三个支持:Velocity、Velocity-tool、spring-context-support。
添加的依赖代码如下:

        
        
            org.apache.velocity
            velocity
            ${velocity.version}
        
        
            org.apache.velocity
            velocity-tools
            ${velocity-tools.version}
        
        
            org.springframework
            spring-context-support
            ${spring.version}
        
    

2. 增加Velocity视图解析器

在usersDemo-servlet.xml文件中配置Velocity视图解析器。配置代码如下:

       
       
              
              
                     
                            utf-8
                            utf-8
                     
              
       
       
       
              
              
       

3. 修改控制器代码

控制器的作用是根据请求调用BLL层提供的Service实例,当服务接口返回处理结果后,由控制器将模型对象和逻辑视图名称返回。在这里还不涉及模型数据,因此只关注逻辑视图,解析器根据这个逻辑视图名称,再加上在usersDemo-servlet.xml文件中定义的视图解析器设置,找到对应的模板文件进行渲染。
控制器的代码如下:

package com.alibaba.yunos.usersDemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;


/**
 * Created by duqi on 15/8/19.
 */

@Controller
public class UsersController {

    @RequestMapping(value = "/showUser",method = RequestMethod.GET)
    public String showUser() {
        //1.调用BLL层的服务接口
        //2.设置模型数据
        //3.返回逻辑视图名称
        return "showUser";
    }
}

4. 创建模板文件

现在的模板文件非常简单,就是一句话:”hello velocity!!“,现在项目目录结构和模板文件如图16所示(注意路径与usersDemo-servlet.xml中配置的对应关系)。
Spring+Velocity+Mybatis整合笔记(step by step)_第15张图片
图16 模板文件示例

5. 测试视图解析器

启动tomat服务器,运行结果如图17所示。
Spring+Velocity+Mybatis整合笔记(step by step)_第16张图片
图17 测试Velocity解析器

6. 代码提交

通过SourceTree提交commit。

五、Mybatis支持

Mybatis 的着力点,则在于POJO 与SQL之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。 相对Hibernate“O/R”而言,Mybatis是一种“Sql Mapping”的ORM实现。

1. 增加Mybatis支持库以及Mysql库

在pom.xml文件中增加相应的支持库,包括mybatis、mybatis-spring、commons-dbcp2、mysql-connector-java等库。其中commons-dbcp2是用作管理数据库连接池。
增加的配置代码如下:


        
        
            org.mybatis
            mybatis
            ${mybatis.version}
        
        
            org.mybatis
            mybatis-spring
            ${mybatis-spring.version}
        
        
            org.apache.commons
            commons-dbcp2
            2.0
        
        
            mysql
            mysql-connector-java
            ${mysql.connector.version}
        
    

2. 在Mysql准备好数据库和表

(1)建立一个数据库mybatis用于测试;

(2)建立一张表users,各个字段的设置如图18所示。
Spring+Velocity+Mybatis整合笔记(step by step)_第17张图片
图18 users表结构

(3)为表中插入初始数据,如图19所示。
Spring+Velocity+Mybatis整合笔记(step by step)_第18张图片
图19 users表中的数据

3. 配置数据源dataSource

数据源的配置在applicationContext.xml中完成,具体的配置代码如下:

       
       
              
              
              
              
       

4. 增加数据模型POJO

在数据库中,id字段我们设置为自动增加。User.java的代码如下

package com.alibaba.yunos.usersDemo.model;

/**
 * Created by duqi on 15/8/19.
 */
public class User {
    private String NAME;
    private String age;

    public String getNAME() {
        return NAME;
    }

    public void setNAME(String NAME) {
        this.NAME = NAME;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

4. 增加DAO层

要和 Spring 一起使用 MyBatis,你需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。在 MyBatis-Spring 中,SqlSessionFactoryBean 是用于创建 SqlSessionFactory 的。


              
 

首先新建一个接口UserMapper,完成请求方法(getUser)到SQL语句的映射,代码下所示:

package com.alibaba.yunos.usersDemo.mapper;

import com.alibaba.yunos.usersDemo.model.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * Created by duqi on 15/8/19.
 */
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{userId}")
    User getUser(@Param("userId")String userId);
}

接着在applicationContext.xml文件中增加配置,将UserMapper接口加入到Spring容器中,配置代码如下所示:

       
              
              
       

至此,DAO和数据库层就已经配置好了。

5. 增加Service层

首先增加UserService接口,代码如下:

package com.alibaba.yunos.usersDemo.service;

import com.alibaba.yunos.usersDemo.model.User;
/**
 * Created by duqi on 15/8/19.
 */
public interface UserService {
    User getUser(String userId);
}

然后增加UserServiceImpl实现,代码如下:

package com.alibaba.yunos.usersDemo.service.impl;

import com.alibaba.yunos.usersDemo.mapper.UserMapper;
import com.alibaba.yunos.usersDemo.model.User;
import com.alibaba.yunos.usersDemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Created by duqi on 15/8/19.
 */
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    public User getUser(String userId) {
        return this.userMapper.getUser(userId);
    }
}

在applicationContext.xml中配置UserServieImpl的实例Bean,由于已经在代码中使用@Autiwired注解,因此不需要在配置文件中显式得规定属性以及提供setter函数。配置代码如下:

       
       

6. 修改控制器代码

控制器的逻辑依旧十分简单,就是三个步骤:

1. 调用BLL层的Service接口

2. 设置模型数据

3. 返回逻辑视图名称

修改后的控制器代码如下:

package com.alibaba.yunos.usersDemo.controller;

import com.alibaba.yunos.usersDemo.model.User;
import com.alibaba.yunos.usersDemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;


/**
 * Created by duqi on 15/8/19.
 */

@Controller
public class UsersController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/showUser",method = RequestMethod.GET)
    public String showUser(@RequestParam("id") String id, ModelMap modelMap) {
        //1.调用BLL层的服务接口
        User user = userService.getUser(id);
        //2.设置模型数据
        modelMap.put("user",user);
        //3.返回逻辑视图名称
        return "showUser";
    }
}

7. 修改模板代码

在模板中使用数据就像使用真正的java对象的数据一样,我们修改后的模板代码如下:

#if(${user})
    ${user.NAME}
#else
    您查找的用户不存在!
#end

8. 启动Web服务开始测试

(1). http://localhost:8080/usersDemo/showUser?id=8

这次请求的访问结果如图20所示:


Spring+Velocity+Mybatis整合笔记(step by step)_第19张图片
图2o 结果展示

(2). http://localhost:8080/usersDemo/showUser?id=1

该URL对应的结果如图21所示:


Spring+Velocity+Mybatis整合笔记(step by step)_第20张图片
图21 结果展示

9. 代码提交

至此,一个Spring+Mybatis+Velocity框架构成的简陋的Demo就完成一个查询功能了,通过SourceTree记录里程碑。

我们在这一步还做了一个调整,将applicationContext.xml调整到src/main/resources文件夹下。对此我的想法是将应用程序配置文件放在resources目录,至于是不是合理,还请各位看官讨论。

六、单元测试Junit

如上所示,一个接口从前端后数据库已经打通了,但是,每次都要等前端页面写好了才能开始测试?这样效率太低了,可不可以将前后端的工作分开,让后端人员能够专注于提供接口,并可以及时测试?可以,单元测试。

由于控制器层是非常薄的一层,负责将传入的URL请求传到BLL层对应的Service实例进行处理。我们可以假定控制器层的代码不需要测试,那么只要Service层保证自己的接口正确就ok。Java中最流行的单元测试框架是Junit,这里探讨如何在Junit的TestCase中自动注入Service实例。
首先在pom.xml中添加测试库支持,配置代码如下:

        
        
            junit
            junit
            4.12
        
        
            org.springframework
            spring-test
            ${spring.version}
        
    

第二,在src/test/java下新建包,与src/main下保持一致,在这里我要测试的类是UserServiceImpl,因此新建com.alibaba.yunos.usersDemo.service。
新建测试类UserServiceImplTest,该类的代码如下:

package com.alibaba.yunos.usersDemo.service;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.alibaba.yunos.usersDemo.model.User;

/**
 * Created by duqi on 15/8/19.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:/applicationContext.xml")
public class UserServiceImplTest {
    @Autowired
    private UserService userService;

    @Test
    public void getUserTest(){
        User user = userService.getUser("1");
        Assert.assertNotNull(user);
    }
}

@RunWith(SpringJUnit4ClassRunner.class)是为Spring 3接入Junit 4框架,从Spring 3开始提供;
@ContextConfiguration("classpath*:/applicationContext.xml")是加载该类中使用的Bean所在的配置文件

七、增加新的接口

根据上文第一到第六步,我们接着给这个Demo增加新的接口:addUser、allUsers和deleteUser。

1. 考虑到新增用户或者删除用户之后需要重定向到"/allUsers"(避免重复提交),我们首先实现allUsers接口。

具体的步骤如下:

(1)修改DAO层

在UserMapper中增加接口getAllUsers,代码如下:

@Select("SELECT * FROM users")
List getAllUsers();

(2)修改Service层

在UserService中增加新的接口getAllUsers,代码如下:

List getAllUsers();

在UserServiceImpl中实现该接口,代码如下:

    public List getAllUsers() {
        return this.userMapper.getAllUsers();
    }

(3)修改Controller

在UserController控制器中增加allUsers接口,代码如下:

@RequestMapping(value ="/allUsers", method = RequestMethod.GET)
public String allUsers(ModelMap modelMap){
    List users = userService.getAllUsers();
    modelMap.put("users",users);
    return "allUsers";
}

(4)增加新的模板文件

在/WEB-INF/templates下增加allUsers.vm文件,内容为:

#if(${users})
    #foreach(${user} in ${users})
        ${user.NAME}
#end #else 目前没有数据! #end

重新启动Web服务器,输入URL:http://localhost:8080/usersDemo/allUsers
结果如图22所示:

Spring+Velocity+Mybatis整合笔记(step by step)_第21张图片
图22 allUsers接口测试结果

2. 增加addUser接口的过程列举如下

(1)修改UserMapper

@Insert("INSERT into users(NAME,age) values(#{userName},#{userAge})")
void addUser(@Param("userName")String userName, @Param("userAge")String userAge);

(2)修改UserService

void addUser(User user);

(3)修改UserServiceImpl

public void addUser(User user) {                 
    this.userMapper.addUser(user.getNAME(),user.getAge());
}

(4)修改控制器Controller

@RequestMapping(value = "/addUser", method = RequestMethod.GET)    public String addUser(@RequestParam("name")String name, @RequestParam("age")String age,ModelMap modelMap){
    User user = new User();
    user.setNAME(name);
    user.setAge(age); 
    userService.addUser(user);
    return "redirect:/allUsers";
}

启动Web服务器运行,访问URL:http://localhost:8080/usersDemo/addUser?name=小红&age=15
发现有乱码如图23所示:

Spring+Velocity+Mybatis整合笔记(step by step)_第22张图片
图23 出现乱码错误

乱码错误是WEB开发中经常遇到的问题,我的经验是在每个数据传输的节点上都要保持一致,在这里我们用UTF-8。看一下数据从前端页面输入到存到后台数据库的流程可以看到,有几个关键点:页面字符、页面到Controller、DAO层到数据库;最终现在的问题我发现是在页面向Controller转换的时候没有强制处理,可能有问题。因此我在Contoller里的addUser方法一开始加了一行代码System.out.println(name);,再次运行发现终端输出乱码,因此确定错误位置。

解决错误的方法是:在web.xml里增加过滤器,即当页面向Controller映射之前要先经过该字符过滤器处理,过滤器设置的代码如下:

    
    
        encodingFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            utf-8
        
    
    
        encodingFilter
        /*
    

再次运行Web服务器测试,访问URL:localhost:8080/usersDemo/addUser?name=哈哈&age=18

发现运行结果如图24所示:
Spring+Velocity+Mybatis整合笔记(step by step)_第23张图片
图24 addUser接口运行成功

3. 增加deleteUser接口

要通过查询参数给定一个id,然后BLL层根据给定的id删除指定用户,这里没有考虑到数据库出错的处理方式。

(1)修改UserMapper

@Delete("DELETE FROM users WHERE id = #{userId}")
void deleteUser(@Param("userId")String userId);

(2)修改UserService

void deleteUser(String userId);

(3)修改UserServiceImpl

public void deleteUser(String userId){
    this.userMapper.deleteUser(userId);
}

(4)修改UserController

@RequestMapping(value = "/deleteUser",method = RequestMethod.GET)
public String deleteUser(@RequestParam("id")String id, ModelMap modelMap){
    userService.deleteUser(id);
    return "redirect:/allUsers";
}

为了便于验证,将用户的id也取出来,需要做下面两处修改

(1)修改User

    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

(2)修改allUsers.vm

#if(${users})
    #foreach(${user} in ${users})
        ${user.id},${user.NAME}
#end #else 目前没有数据! #end

代码修改完成,启动Web服务器,依次访下列的URL:
http://localhost:8080/usersDemo/deleteUser?id=8
http://localhost:8080/usersDemo/deleteUser?id=9
http://localhost:8080/usersDemo/deleteUser?id=100

然后再访问:http://localhost:8080/usersDemo/allUsers

结果截图如图25所示:
Spring+Velocity+Mybatis整合笔记(step by step)_第24张图片
图25 deleteUser接口验证

这里有一个疑问,删除id为100的时候,数据库中明显没有这个数据,但是后台也没报出异常,原因还有待我继续学习,有知道的朋友请留言给我,非常感谢。

八、总结

写这篇文字的最初目的是帮助自己回顾一遍前几天学习的东西,如果能碰巧帮助后来的同学就更好了。排版还有点乱,代码还很简陋,希望各位朋友指点一二。


参考文献

  1. 写给Java Web一年左右工作经验的人
  2. Spring+Mybatis+Velocity 工程示例
  3. Spring+Mybatis+Velocity配置
  4. Spring MVC + Mybatis + Velocity + Maven + Mysql整合实例
  5. Velocity介绍及语法
  6. Mybatis教程
  7. Mybatis与Hibernate的比较
  8. JUnit与Spring的整合——JUnit的TestCase如何自动注入Spring容器托管的对象
  9. 《Spring 实战》

本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。


Spring+Velocity+Mybatis整合笔记(step by step)_第25张图片
javaadu

你可能感兴趣的:(Spring+Velocity+Mybatis整合笔记(step by step))