一. 为什么要用SpringMVC
上次我们用Gradle创建了一个基于servlet的表单提交应用,今天我们使用SpringMVC框架来完成同样的工作。
之前不是挺好的,我们为什么非要用一个框架啊?我们来看看上次应用中数据库处理部分的代码:
看了之后你肯定会想,存数据不就只是一个insert吗,怎么搞出这么一堆东西出来?好吧,做准备工作,那后面那一堆try,catch是个啥,这个多异常处理,看着好烦啊,能不能自己写一个处理类,把这些工作做完。
好,我们自己创建了一个DBprocess类,在这里我们只需创建一个DB的类,用完之后销毁就行了。这个不错啊,这就是oop的基本准则。但是要是有第三个类要调用我们现在这个类呢?要是我们的类多了,相互调用,相互创建,是不是很乱,反正我以前是被这种事搞崩溃过,但凡中间有一个类没有创建或是资源因为类没析构掉而一直被占用,然后就是可怕的调试,简直就是噩梦。
这时候我们想,要是有这么一种东西,它知道工程中类和类之间的关系,在每个类有需求的时候自动的满足,把该给的东西给它,用完之后再拿回来,而我们只要关注每个类内部是怎么工作,那就太好了!没错,这个就是框架!
这里有一个很专业的名词叫做:控制反转,或是依赖注入。
其实我们生活中早就有这样的概念了,比如我们的电脑和外接硬盘,U盘之间的关系。我们本身就是中间那个框架,当我们需要拷贝文件到其他地方,电脑就需要一个U盘来存放需要向外输出的数据,但是电脑不会亲自来做,它只是告诉你我需要,然后你就把外部存储设备接到USB插口上,其实电脑也不关心你用的是U盘还是硬盘,只要你的存储设备遵循一定的规定,比如是USB标准等,电脑就向外输出,等到文件拷贝进度条走完,电脑告诉你我用完了,你就把U盘拔出来,用在到其他电脑上。整个过程中,我们就担任了框架的作用,控制权从电脑反转到了框架,而电脑依赖的U盘是我们帮助插入USB接口的,所以叫"依赖注入"。
二. 这个应用发生了什么变化
现在你可以畅想一下你新的数据库类的工作方式了,没错,你告诉框架什么时候要数据库操作,并把操作定义好(比如一个模板),需要的数据库操作的时候,框架会自动喂给你一个定义好的数据库模板,你昨晚该做的操作之后框架再回收这个模板(类)。好,现在我们就来看看相对于上次的servlet,用SpringMVC之后,我们的表单提交应用从逻辑到代码细节有什么变化。
1. 逻辑上变了什么
对了,为什么叫MVC呢?对应为Model,View, Controller,什么意思呢,相信看完这个例子你自己就有体会了。
我们自上而下,先从逻辑层面开始,再来看代码是怎么根据逻辑一步步跑起来的。
如图2所示,和以前一样,用户请求一个URL,URL被已经在web.xml文件中注册的servlet截获,不同的是,这次是SpringMVC中的特定的servlet接获,然后查询目标控制器看看是谁来管这个请求,接着根据控制器的要求,参数被封装成相应的命令对象传给目标控制器,控制器在昨晚自己的工作之后,通过调用spring 容器把自己的成果用视图的形式反馈个用户,这样就完成了一次请求操作。
2. 让我们来扣扣代码细节吧
怎么样,逻辑很简单吧,我们先来看看web.xml文件:
我们发现servlet-class发生的变化,对应的url-pattern也发生了变化。因为SpringMVC提供了一个DispatcherServlet的servlet类,并且这个类可以处理所有的请求,其实是用它截获请求。
上一篇我们讲到web.xml文件是注册servlet用的,他是一个list,告诉servlet容器在注册的servlet有多少,叫什么,干什么用,怎么用。上面又说到框架要给我们服务,需要知道我们类之间的关系,以及要注入什么样的"依赖",这样的"说明文件"都是由xml文件完成的,spring也会预先扫描xml文件,得到你工程的信息之后才能工作的。所以我们再来看一个springmvc_form-servlet.xml文件。
开始的一行告诉spring容器要扫描的基础包名是什么,也就是管辖范围。之后有一个bean注册,这个bean是spring提供一个class,用来处理视图的,一起就是说我在需要视图的时候,这个class就会起作用,具体是怎么起作用的,大概就是给一个参数,spring就会把接下来的工作交给一个视图来完成,也就是注入一个视图。那么后面的两个参数是什么,一个是前缀,一个是后缀,后面会详细的解释,这里可以猜到肯定是和参数拼接之类的。
然后我们再看看Beans.xml文件,这个文件注册了带注入的bean的详细信息
这里注册了两个bean,都有id和对应的class,还有一些参数值,上面一个是数据库连接的,可以看到里面有连接数据库的一些基本参数。下面的是一个我们自己定义的类的bean,我们来看下这个类:
这个类其实就是增加用户的数据库操作类,那么图4第二个bean的意思就是把这个类变成了一个bean,再适当的时候注入到需要他的地方,而图4第一个bean被第一个引用,两个一起合作完成注入工作。
我们自己定义了一个student类,里面有一些待填写的属性。
接着我们到最应用最开始的地方,注册页面,也就是student.jsp
这里截取了最重要的一部分,之后都是些表单的内容。上面一行也有解释,与上一篇文章相似,这里也有一个action,只是这个action要提交给控制器。我们来看看控制器是怎么获取到数据的,也就是参数是怎么按照控制器的要求打包的。
在springMVC中很重要的一个概念就是注解类,RequestMapping就是一个注解类,在这个类之前还有一个Controller注解,如图8所示,表示这个类是一个控制器,控制器中的方法是具体的控制器实现。
RequestMapping注解填写了两个参数:value表示请求的路径,method表示这个控制方法处理的请求类型。一旦jsp页面请求了value中的值,请求的method值能匹配,那么请求就由这个方法来完成。而数据是怎么打包的呢,有什么数据进行了传递,是怎么传递的呢?图7中有详细的解释:
//ModelMap是一个和模板文件进行沟通的参数,可以理解成是一个封装好的数据包
ModelMap是通信的媒介,它把数据封装好,从控制器传给视图。
//输入:转换成command对象(command对象是从视图传过来的)的student对象,带着被spring打包的网页数据
可以看到jsp页面用了form:form标签,这个标签会自动绑定一个command属性,这是一个student对象,student.jsp页面把数据封装成student类然后发送过来,这里其实就是注入了一个student对象,也就是spring中的构造器注入,你不用自行创建,只需按照原来类的使用方法使用就行,如这里的get方法得到私有成员的值。
而返回的值是什么呢?是一个字符串?what?还记得之前的那个视图bean吗,它有前缀和后缀,对了,这里是返回的字符串和前缀后缀拼接后,就组成了一个带路径的jsp页面,也就是反悔了视图,而视图只是模板啊,数据呢,注意看我们的addStudent类还有一个参数,我之前说了,这个参数是一个对象,你向其中加入一些键值对,如:
model.addAttribute("username",student.getUsername());
那么在返回的视图中就可以通过键的到响应的值了。
//输出,将这属性值处理之后加入到模型和视图之间传递ModelMap,让它返回给视图,这里的视图就是最后返回的reslut.jsp,前缀和后缀都已经配置好
我们来看看返回的视图result.jsp
在视图中我们用${model键},就能的到controller处理后的值了。
最后我我们来看看数据库是怎么注入的
你看,我们在这里先读取必要的Beans.xml的配置文件,然后获取bean,这个bean就被注入到这个控制器类里了,你调用类中的创建方法,完成了创建空户的操作,其他就什么都不用管了,springMVC框架都会帮你完成的。
怎么样?SpringMVC是不是很方便,很科学呢!
最后的结果如图:
提交界面:
反馈界面:
数据库查询:
本文的详细代码可以在我的github上获取:
https://github.com/intwzt/TW_CODE/tree/master/12-9