ping MVC是基于模型-视图-控制器(Model-View-Controller,MVC)模式实现的Web层框架。
一个案例教你使用Spring MVC 构建Web应用程序。
我们还是以一张图来了解Spring MVC如何做到和前端页面交互的:
搭建Spring MVC
首先我们就需要导入jar,这在前面的文章中已经介绍了,不在赘述。(需要jar可以去Maven仓库进行下载)
- DispatcherServlet
根据图示,页面请求首先走的就是Spring MVC的前端控制器DispatcherServlet
,我们需要在web.xml
中配置前端控制器Servlet:1 2 3 4 5
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.DispatcherServlet</servlet-class> <load-on>1</load-on> </servlet>
默认情况下,DispatcherServlet
加载时会从一个基于这个Servlet名字的XML文件中加载Spring上下文。
接下来我们必须声明DispatcherServlet
处理哪些URL:
1 2 3 4 | <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</pattern> </servlet-mapping> |
注意: 我们常见的配置此资源请求路径为*.do
,这样,在浏览器的请求路径通常是xxx.do
这样,但是存在暴露URL实现细节的问题。
所以我们通过将DispatcherServlet
映射到/
,声明了它作为默认的Servlet会处理所有请求,包括静态资源的请求。
- 命名空间
同时,Spring提供了<mvc:resources>
元素,让你更灵活的配置如何处理静态资源的请求。下面我们创建springmvc.xml
1 2 3 4 5 6 7 8 9 10
<?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: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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:resources mapping="/resources/**" location="/resources/"> </beans>
如上所示这个spring.xml
并没有太大区别,仅仅是多了一个命名空间。当我们想对静态资源请求做处理,<mvc:resources>
就正好满足了我们的需求:
属性mapping
设置为/resources/**
表明路径必须以/resources
开始,而且包含其任意子路径;属性location
定义了这里静态资源的存储位置。
-
处理器映射器和控制器
配置前端控制器Servlet和Spring MVC的命名空间,下面我们应该配置Spring MVC的控制器(DispatcherServlet
需要咨询一个或多个处理器映射来明确的将请求分发给哪个控制器)。Spring自带多个处理器映射:BeanNameUrlHandlerMapping
:根据Bean的名字将控制器映射到URLControllerBeanNameHandlerMapping
:根据控制器Bean的名字将控制器映射到URL。(Bean的名字不需要遵循URL的约定)ControllerClassNameHandlerMapping
:通过使用控制器的类名作为URL基础将控制器映射到URLDefaultAnnotationHandlerMapping
:将请求映射给使用@RequestMapping注解的控制器和控制方法SimpleUrlHandlerMapping
:使用定义在Spring应用上下文的属性集合将控制器映射到URL
如上几种控制器,因为我们使用@RequestMapping
,所以以上控制器我们并不需要直接去使用,取而代之的是<mvc:annotation-driven>
标签将自动为我们分配所需要的控制器。
-
视图解析器
当前台request
请求发送过来,先经过Spring MVC的DispatcherServlet
前端控制器,然后经过Spring的处理器映射器和处理器控制器(Controller)对接收到URL信息进行处理;控制器会将处理后的信息(Model)返回给用户;之后再讲这些数据进行格式化,发送给一个视图(View);
控制器最后再将这些模型视图数据打包,将渲染输出的模型和视图名称发送给DispatcherServlet
;最后DispatcherServlet
将使用视图解析器把逻辑视图名匹配为一定特定的视图实现。
所以,接下来我们要配置视图解析器:1 2 3 4 5 6
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀 --> <property name="prefix" value="/WEB-INF/"/> <!-- 后缀 --> <property name="suffix" value=".jsp"/> </bean>
Spring MVC中常用InternalResourceViewResolver
视图解析器根据我们定义的路径将视图解析到浏览器上。prefix
表示模板视图(JSP)所在路径的前缀名,suffix
表示模板视图的后缀名。
注意:
如果你的JSP放到了WEB-INF
下,那么我们在浏览器上是无法直接访问的,必须通过控制层进行跳转,但同时这也保证了一定的安全性。所以我们习惯将JSP文件放到WEB-INF/
下,将静态资源放到webapp
下。
- 编写控制层
1 2 3 4 5 6 7 8 9 10
@Controller @RequestMapping(value="/user") public class UserController{ @RequestMapping(value="/save", method = RquestMethod.POST) public String save(@RequestParam String username, @RequestParam String password, Model model){ //逻辑处理 return "save"; } }
如上,这就是一个简单的控制器,我们接下来分析一下这个Controller:
@Controller
(@Component
注解的一种)表明了该类是一个控制器类(我们需要现在配置文件中配置扫描这个控制器类<context:component-scan>
),<xontext:component-scan>
会查找被@Component
标注的类,将其注册为Bean。@RequestMapping
注解有两个作用:1.表名此方法(save()
)是一个请求处理方法;2.指明了该方法要处理请求/save
路径下的请求。所以我们看到@RequestMapping
注解可以标注在类上和方法上,其实分别表示两种不同的作用范围,因为类包裹了方法。如我们现在的配置,就表示save()
方法的在浏览器上的请求路径应该是:localhost:8080/(项目名)/user/save
(如果我们指定了前端控制器DispatcherServlet
的映射路径,如:*,do
,那么save()
方法的请求路径就应该是localhost:8080/(项目名)/user/save.do
)。method=RequestMethod.POST
指定了当前请求是POST请求return
表明了该映射方法需要返回到哪里。首先我们观察该save()
方法的返回值数据类型,那么设置为String
在Spring MVC的映射方法中就表示要返回一个视图,我们只需要定义返回视图的名字,Spring MVC会根据视图解析器查找此名字对应的视图。如我们现在的配置返回的视图路径其实就是:/WEB-INF/save.jsp
(在视图解析器中我们已经配置了前缀和后缀)。@RequestParam
:该注解并不是必须的,它提供了一些属性如可以设置默认值等,在参数不匹配的时候有用,且对于当前我们这种请求包含多个参数时尽量使用@RequestParam
,避免一些未知的问题。Model
对象内部类似Map<String, Obeject>
,且提供了一些方式用来设置值。通过Model
对象的addAttribute()
方法设置的值可以在JSP中用EL表达式取出来,类似放到了域对象中
入门案例
首先我们看一下项目目录结构(注意 :我这里使用的是IDEA+Maven),可能和大家的有所区别,但大家目前不要过分关注这些。
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?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/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Archetype Created Web Application</display-name> <welcome-file-list> <welcome-file>/WEB-INF/index.jsp</welcome-file> </welcome-file-list> <!-- 配置Spring的字符编码过滤器 --> <filter> <filter-name>CharacterEncoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ContextLoaderListener监听器监听servlet的创建 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 告诉监听器需要加载那些配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring.xml</param-value> </context-param> <!-- Spring MVC的前端控制器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> |
注意之前没有介绍CharacterEncodingFilter
,此过滤器是Spring提供的为避免request
请求乱码问题而设定的。
配置Spring MVC的前端控制器时,包含了<init-param>
标签旨在加载前端控制器时同时去加载Spring MVC的上下文。
spring.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <?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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解扫描 --> <context:component-scan base-package="service"/> <!-- 加载数据库资源配置文件 --> <context:property-placeholder location="classpath:resource/jdbc.properties"/> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 注入Bean --> <bean id="userService" class="service.UserServiceImp"/> <bean id="userDao" class="dao.UserDaoImp"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans> |
注意这里配置连接池我们使用的是加载外置的属性文件:jdbc.properties
,使用<context:property-placeholder>
加载。
springmvc.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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"> <!-- 扫描Controller层 --> <context:component-scan base-package="controller"/> <!-- 处理静态资源 --> <mvc:resources mapping="/resources/**" location="/resources/"/> <!-- 一个注解实现配置:BeanNameUrlHandlerMapping(处理器映射器)和DefaultAnnotationHandlerMapping(处理器控制器) --> <mvc:annotation-driven/> <!-- 配置Spring MVC视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀 --> <property name="prefix" value="/WEB-INF/"/> <!-- 后缀 --> <property name="suffix" value=".jsp"/> </bean> </beans> |
Controller层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Controller @RequestMapping(value = "/user") public class UserController { //注入 @Autowired private UserService userService; //跳转到添加用户信息页 @RequestMapping(value="/savePage") public String savePage(){ return "view/save"; } //保存用户 @RequestMapping(value = "/save", method = RequestMethod.POST) public String save(@RequestParam String username,@RequestParam String password, Model model){ userService.save(username,password); model.addAttribute("message","保存数据成功"); return "view/success"; } } |
如果我们想要访问保存信息的JSP页面,应在浏览器中输入:localhost:8080/user/savePage
;而保存表单信息方法的路径是:localhost:8080/user/save
Service层
-
UserService
1 2 3
public interface UserService { void save(String username, String password); }
-
UserServiceImp
1 2 3 4 5 6 7 8 9 10 11
@Transactional public class UserServiceImp implements UserService { @Autowired private UserDao userDao; //保存 public void save(String username, String password) { userDao.save(username,password); } }
用@Transaction
标识该类,Spring会为该类设置事务性性通知(配合<tx:annotation-driven>
)
Dao层
-
UserDao
1 2 3
public interface UserDao { void save(String username, String password); }
-
UserDaoImp
1 2 3 4 5 6 7 8
public class UserDaoImp extends JdbcDaoSupport implements UserDao { //保存数据 public void save(String username, String password) { //使用Spring提供的JDBC模板可以直接执行SQL语句 this.getJdbcTemplate().update("insert into user(id,username,password) values(?,?,?)",null,username,password); } }
这里我们仍使用的是Spring提供的JDBC模板类,只需要dao继承这个模板类即可。使用JdbcDaoSupport
的getJdbcTemple()
方法执行SQL语句。(this
表示当前对象)
前端页面
save.jsp
1 2 3 4 5 6 7 | <h2>表单</h2> <hr/> <form action="<%=basePath%>/user/save" method="post"> username: <input type="text" name="username"/><br/> password: <input type="password" name="password"/><br/> <input type="submit" value="提交"/> </form> |
success.jsp
1 2 3 4 5 6 7 8 9 | <head> <title>Title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>${message}</h1> </body> </html> |
运行
首先我们需要把项目部署到Tomcat服务器上,然后启动Tomcat:
没有报错,接下来我们来浏览器中输入我们项目中添加页面的访问路径:localhost:8080/user/savePage
我们输入中文信息,并点击提交,通过断点查看:
后台映射方法save()
接收到了JSP表单传递进来的参数,并且中文没有乱码(如果没有配置CharacterEncodingFilter
就可能乱码)。继续执行:
当我们的request请求经控制层处理完毕后,DispatcherServlet
将视图数据返回到页面上,我们return
已经设置了返回页面/WEB-INF/view/success.jsp
,那么请求成功就会返回到该页面上。并且我们在success.jsp
中用EL表达式取出来了message
这个属性的值,当我们在Controller
控制层设置了message
值为数据保存成功
,就相当于保存进了request域中一个名为message
的值。
数据库: