前言
我们最初的javaSE部分学习后,基本算是入门了,也熟悉了Java的语法和一些常用API,然后再深入到数据库操作、WEB程序开发,渐渐会接触到JDBC、Servlet/Jsp之类的知识,期间可能会接触一两个关系型数据库,例如MySQL/Oracle等等。像前面的MyBatis部分,主要是针对JDBC的进一步封装,使得更适用于实际项目开发过程,但是JDBC、MyBatis或者Hibernate都是针对持久层数据库操作,例如查询、更新记录等等,我们开发程序最终的展现对象是用户,而用户操作程序往往用的是什么?各种各样的浏览器,而不是通过我们后台的这些测试代码,用户看不懂代码,也不需要懂。用户只需要点击、需要输入,比如点击一个链接,跳转到一个新页面,页面中的部分数据都是从数据库中查询获取的;又或者是输入一段文本,点击提交按钮,更新的是数据库中的记录。我们要理清的是,这些页面的操作指令是怎样传递到后台服务器,然后访问到数据库的;还有数据库的数据是怎么从后台又传回前端,怎样显示到页面上的?
如果有学习过Servlet/Jsp相关内容,应该会对上面的问题多少有些理解。首先回顾下什么是Servlet,Servlet是运行在我们服务器上的Java程序,作用于中间层,即客户端请求和服务器之间,我们发送请求,控制数据库、业务逻辑、做出响应,返回前端页面数据等等都可以通过其实现。Servlet的生命周期:1.初始化调用init()方法,2.处理客户端请求调用service()方法,3.结束程序调用destroy方法,4.最后垃圾回收。JSP本质上也是Servlet,项目启动,Web容器将JSP的代码编译成JVM能够识别的java类即Servlet。Jsp重点偏向于页面的展示,应用html、CSS、JavaScript等前端技术在这里都不成问题,而Servlet更倾向于后台的业务逻辑、控制转发等等。
SpringMVC是在Servlet基础上进一步封装开发的一套WEB层框架,所以如果能深入理清Servlet的相关内容,那么很容易就可以上手SpringMVC,下面我们先简单回顾一下之前Servlet的实现过程。
Servlet篇
新建一个普通web工程或者maven web工程,通过继承HttpServlet,创建我们自定义的Servlet,如下所示
package com.mmm.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServletA extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //请求转发,相当于一次请求 req.getRequestDispatcher("/a.jsp").forward(req, resp); //响应重定向,相当于二次请求,前一次请求设置的属性值在页面中将无法获取到 //resp.sendRedirect("./a.jsp"); //这里往往还要通过持久层对象访问数据库,获取或者更新数据 //获取到的数据通过设置属性,然后在前端Jsp页面可以通过el表达式之类的方法拿到然后渲染样式展示出来 } }
这里的/a.jsp,代表Jsp页面路径,为web文件根目录下例如webapp下,所以我们在webapp下简单创建a.jsp如下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>a.jsp</title> </head> <body> 这是a.jsp </body> </html>
然后在web文件夹下WEB-INF文件夹下web.xml中<web-app></web-app>节点内添加如下内容
<servlet> <servlet-name>MyServletA</servlet-name> <servlet-class>com.mmm.servlet.MyServletA</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServletA</servlet-name> <url-pattern>/a.do</url-pattern> </servlet-mapping>
这里的<servlet-name>的值在<servlet>和<servlet-mapping>中要保持一致,<servlet-class>即为我们前面自定义的Servlet全名(含包名),<url-pattern>用于匹配我们在地址栏输入的url地址。例如这里通过localhost:8080/servlet-web/a.do就可以匹配到该Servlet,从而运行到该Servlet中的相关方法,这里会请求转发到a.jsp,如下图所示。
上面为一个最基本的从前端http请求到后台服务器,运行Servlet后,返回前端视图的过程。但是实际情况下,前端的请求不会是这样简单拿回一个静态无数据的页面,也不会只有一个,举个例子,如果上面a.do对应一个增加一条商品数据的操作,即在我们自定义的MyServlet中doPost()方法里执行添加操作,那么如果还有删改查操作,按照这里的路子,我们需要再定义MyServletB、MyServletC、MyServletD,然后web.xml添加3段对应的Servlet节点定义,再继续拓展,我们不止对商品进行增删改查,还有员工、销售记录等等进行操作,这样下去,我们得定义和配置多少个Servlet,有的人想到办法,例如商品的操作路径匹配都是a.do,然后加个参数判断,及路径后加上?action=add或者?action=del等等,这样同一类对象的操作我们将其统一匹配到同一个Servlet,在Servlet的方法内部再去根据具体的action操作类型去判断到底要执行哪种操作(运行哪段业务逻辑)。
SpirngMVC更是将这种思想进化到了极致,我们在web,xml只定义有且只有一个Servlet,这个Servlet能匹配到所有的正常请求,然后可以根据路径精确解析到具体的执行某个类的某个方法,这里的类即控制层Controller,例如一个商品的相关操作,我们定义一个商品Controller类,类中定义各种操作方法,需要访问数据库的话,往往是通过Service层对象去调用持久层的代码实现数据库操作。下面就实例一段单独通过SpringMVC来实现中间控制层效果的小案例。
SpringMVC篇
为了方便管理jar包,这里我们可以新建一个maven web项目,pom依赖可参考前面的环境搭建篇,这里同Servlet对比,我们仅需要一个Java类也就是上面提到的controller,然后是一个指定路径下的jsp文件,最后稍微修改一下web.xml,这三步即可简单过一遍SpringMVC的基本运行过程。下面分别详细写出这三步的内容。
DemoController.java
package com.mmm.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("demo") public class DemoController { @RequestMapping(value="/toPage") public String toPage() { return "page"; } }
从上往下,首先@Controller注解属于Spring系列组件bean注解之一,对应web表现层的组件,类似的还有代表业务逻辑层的@Service,代表数据库访问层的@Repositoty,通用组件@Component等等,实际项目中,层与层之间类对象还会相互调用,基于Spring容器的IOC及依赖注入,我们又还会接触到@Autowired,@Autowired等等注解。在这里我们可以简单理解为通过@Controller注解,Spring会识别并实例化这个bean,这个类对象的创建和管理将交由Spring容器去控制。
然后@RequestMapping("demo"),看英文单词意思应该不难理解,Request请求,Mapping映射,请求映射,通过我们客户端例如浏览器那边过来的http请求,前面我们用过的Servlet会通过配置url匹配到访问路径从而匹配到相应的Servlet中,而在这里类似,SpringMVC会根据我们的访问路径匹配到相应的Contoller中的相应方法,是的,这里还会进一步精确匹配到方法。下面@RequestMapping(value="/toPage")即方法名的映射,例如这里把类和方法的映射名组合起来即为demo/toPage,在本例中,我们通过localhost:8080/spring-mvc/demo/toPage,即可匹配到这个DemoController的toPage方法中执行。
可以看到方法的返回值是String字符串类型,这里可以理解为用于获取视图页面路径。下面结合Spring MVC配置文件讲解。
application-mvc.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:p="http://www.springframework.org/schema/p" 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-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!-- 自动扫描开启 --> <context:component-scan base-package="com.mmm.web" /> <mvc:annotation-driven/> <!-- Spring MVC视图解析配置--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
这里的<context:component-scan base-package="com.mmm.web" />即用于Spring扫描识别bean注解,例如刚刚讲到的@Controller,而<mvc:annotation-driven/>则用于扫描SpringMVC相关特定注解,例如这里@RequestMapping等等。
下面的视图解析配置即结合刚刚上面讲到的视图页面路径,prefix和suffix分别为前缀和后缀的意思,结合前面的controller中方法,即页面请求转发路径的统一前后缀,前面controller中方法的返回值为"page",那么组合前后缀,即为/WEB-INF/views/page.jsp,而这个路径对应哪里,如下图所示。
所以我们要在这里创建一个jsp文件,views文件夹也是。
最后在web.xml中加入如下内容
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/application-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
我们会发现这里也是配置的Servlet,即org.springframework.web.servlet.DispatcherServlet,只不过只配置了这单独一个,映射路径匹配通用请求,同时init-param设置初始参数即我们SpringMVC配置文件路径。这样一来,我们请求都会被SpringMVC系列组件接受、解析路径、分配到相应的Controller的相应方法。
上面步骤结束后,我们发布项目并启动Tomcat服务器,在浏览器地址栏输入http://localhost/spring-mvc/demo/toPage,看到如下页面,即为成功
小结
通过上面基础的实例,我们能一步步理解从Servlet到web框架SpringMVC的使用,实际开发中同样遵循这些基本规则,只不过事更多的业务逻辑、视图文件等等,比如这里讲到页面跳转中的请求转发,那么是否类似Servlet还有响应重定向,还有,Controller中方法我们不一定是跳转到页面,也可以直接传递JSON数据返回前端,以及我们前面学习到的MyBatis封装持久层,那么我们在这里怎么与持久层连接起来去访问数据库查询或者更新数据,还有到底什么是业务逻辑,等等这些都是我们需要考虑的问题,后面准备分别以具体实例展开说明,欢迎一起探讨。