(ps: 这篇文章简要的讲述 JavaWeb开发的 Spring MVC 框架基础,由于是初学Spring MVC,然后便上手做系统分析与设计的大作业,做的总结可能有些瑕疵,对一些概念的理解可能比较片面,欢迎大神纠错)
目录:
MVC框架
模型-视图-控制器(MVC)是一个众所周知的以设计界面应用程序为基础的设计模式。它主要通过分离模型、视图及控制器在应用程序中的角色将业务逻辑从界面中解耦。
- 模型负责封装应用程序数据在视图层展示。
- 视图仅仅只是展示这些数据,不包含任何业务逻辑。
- 控制器负责接收来自用户的请求,并调用后台服务(manager或者dao,即三层架构中的业务逻辑层和数据访问层)来处理业务逻辑。处理后,后台业务层可能会返回了一些数据在视图层展示。控制器收集这些数据及准备模型在视图层展示。
MVC模式的核心思想是将业务逻辑从界面中分离出来,允许它们单独改变而不会相互影响。
(图片来源于网络)
(图片来源于网络)
优点
(1)多个视图能共享一个模型。同一个模型可以被不同的视图重用,提高了代码的可重用性。
(2)由于MVC的三个模块相互独立,改变其中一个不会影响其他两个,所以依据这种设计思想能构造良好的松耦合的构件。
(3)此外,控制器提高了应用程序的灵活性和可配置性。控制器可以用来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。
缺点
(1)增加了系统结构和实现的复杂性。
对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。(2)视图与控制器间的过于紧密的连接。
视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。(3)视图对模型数据的低效访问。
依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
Spring的MVC框架
简单原理
(图片来源于网络)
在Spring MVC中前端的控制器就是DispatcherServlet这个Servlet来掌管着用户的请求及最后的系统回应。这个DispatcherServlet同具体的业务逻辑一点都不着边,而是把所有的事情委派给控制器去做(Controller);然后当控制器把事情都做完了后,这个时候轮到视图(View)上场了,它可以把数据以不同的展现形式交给客户,可以是jsp、xml、json等等。
第一步:pom.xml
pom.xml文件中包含spring mvc依赖及为编写jsp文件提供支持的各种相关依赖。因为这是一个maven项目,所有依赖(jar)都在pom.xml中进行配置,maven将自动帮我们下载所有的对应依赖(jar包)。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>hello</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>hello Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<springframework.version>4.0.6.RELEASE
</springframework.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- Below declared dependencies are included for the servers who may complain about servlet/jstl missing dependency -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>hello</finalName>
</build>
</project>
第二步:web.xml
即Servlet部分,上面说到前端控制器是DispatcherServlet这个Servlet,那很显然需要在web.xml中加入一个servlet,然后把用户的请求都让DispatcherServlet去处理。
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>hello Web Application </display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
这里有个地方就是contextConfigLocation,这个是个初始化参数(init-param),在servlet进行初始化的时候可以设置它的值,而这个值定义了spring应用上下文(context—上下文,指的是一种环境,主要是各种bean,可以理解为各种component)的配置文件(XML格式)的位置,这个上下文会被DispatcherServlet加载进来,这样Dispatcher工作起来时会依照这个上下文的内容进行分配任务。
关于上下文的配置,这里使用的是xml的配置方式,上面代码指定了配置文件是/WEB-INF/spring-servlet.xml,这个也可以使用java的配置方式
第三步:Controller类
package com.hello.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping("/")
public class HelloWorldController {
@RequestMapping(method = RequestMethod.GET)
public String sayHello(ModelMap model) {
model.addAttribute("greeting", "Hello World from Spring 4 MVC");
return "welcome";
}
@RequestMapping(value="/helloagain", method = RequestMethod.GET)
public String sayHelloAgain(ModelMap model) {
model.addAttribute("greeting", "Hello World Again, from Spring 4 MVC");
return "welcome";
}
}
1、使用了@Controller这个annotation(这个可以翻译成“注释”,表示下面的类的作用),来表示HomeController这个类是作为Spring MVC中的Controller(控制器),根据上面的那个图也就是表示这个类具有处理用户请求的能力。所以这里HomeController也是作为一个组件(Component),在Spring初始化扫描的时候它会被自动检测到并且加入到Spring container中(Spring容器或者叫Spring上下文,都是类似的概念),然后生成对应的类实例,最后像其他任何Spring组件一样允许注入到系统中。
2、home这个方法使用了@RequestMapping这个注释,表示home这个方法可以用来处理对应于路径“/”(根路径)的用户请求
3、这里home的处理就是在log中打印一个字符串,然后返回welcome,这个是交给View去处理的,这里就是个jsp文件. 默认情况下,如果我们没有设置特定的View的话,Spring会使用默认的View来处理WEB-INF/views/home.jsp这个Response(回应);对应View在后面将详细讲,这里只要知道View会把系统的home.jsp这个文件呈现给客户就好
第四步:spring-servlet.xml
在 WEB-INF 文件夹下创建一个名为spring-servlet.xml 的配置文件。
注意:此名字你可以随便起,但是要和web.xml里面声明的一致。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.hello.controller" />
<mvc:annotation-driven />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
spring 配置
<mvc:annotation-driven />
意思是我们可以不在xml中声明该bean,或者实现一个借口或者继承一个bean类或者其他类的情况下定义bean的依赖。例如仅仅在类上加上一个 @Controller注解(我们上面的控制器类就是这么用的),这样就不需要再在xml中配置bean,spring就会知道我们带了此注解的类包含响应http请求的处理器。
<context:component-scan base-package="com.websystique.springmvc" />
spring 自动扫描此包下面的组件
base-package [com.websystique.springmvc],
看看它们有没有带 [@Controller, @Service,@Repository, @Component, 等等]这些注解。如果有这些注解spring将自动的将它们在bean 工厂里面注册,和在xml中配置bean效果是一样的。
通过上面我们声明了一个view resolver,帮助控制器(controller)代理响应到正确的视图(view).
第五步:XXX.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!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=ISO-8859-1">
<title>HelloWorld page</title>
</head>
<body>
Greeting : ${greeting}
</body>
</html>
实战:大作业
1.目录结构
src/main/java:
com.filmgogo.Controller // 存放Controller
com.filmgogo.DAO // 存放业务层Repository
com.filmgogo.VO // 存放实体类Model
src/main/resources:
filmgogo-servlet.xml // 配置Spring上下文
jdbc.properties // 存放jdbc相关自定义的键值对
src/main/webapp: // 存放前端相关的资源文件
WEB-INF
index.jsp // 首页布局
target:
pom.xml //项目依赖包配置文件
2. Servlet 配置
放置于filmgogo-servlet.xml文件中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.filmgogo" />
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" lazy-init="false">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialSize" value="${jdbc.initialSize}" />
<property name="maxActive" value="${jdbc.maxActive}" />
<property name="maxIdle" value="${jdbc.maxIdle}" />
<property name="minIdle" value="${jdbc.minIdle}" />
<property name="maxWait" value="${jdbc.maxWait}" />
</bean>
<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
以及相应的键值映射:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/filmgogo?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true
jdbc.username=root
jdbc.password=XXXXXXXXX
jdbc.initialSize=10
jdbc.maxActive=20
jdbc.maxIdle=20
jdbc.minIdle=1
jdbc.maxWait=10000
3.VO层
例如:定义Reservation实体类:
4.DAO层
例如:MovieDAO, 用以完成对 Movie 实体的CRUD 操作:
@Repository
public class MovieDAO {
@Autowired
private JdbcTemplate jdb;
public String getMoviesViaCid(int cid)
{
String sql = "select distinct movie.id, movie.name, movie.type, movie.description, movie.image, movie.score, movie.star from movie, cinema " + "where cinema.id = ?;";
Object[] para = new Object[]{cid};
List<MovieVO> lm = jdb.query(sql, para, new RowMapper<MovieVO>(){
public MovieVO mapRow(ResultSet res, int arg1) throws SQLException
{
MovieVO m = new MovieVO();
m.setId(res.getInt("id"));
m.setName(res.getString("name"));
m.setType(res.getString("type"));
m.setDescription(res.getString("description"));
m.setImg(res.getString("image"));
m.setScore(res.getFloat("score"));
m.setStar(res.getString("star"));
return m;
}
});
return JSONArray.fromObject(lm).toString();
}
5.Controller层
例如:Customer的登录请求映射:
@Controller
@RequestMapping(value="/login", method=RequestMethod.POST)
public class CustomerLogin {
@Autowired
private CustomerDAO customerInfo;
@RequestMapping
void login(HttpServletRequest request, HttpServletResponse response) throws IOException
{
request.setCharacterEncoding("UTF-8");
StringBuffer requestData = new StringBuffer(); //requestData存放输入的用户名和密码等数据
String line = null;
try
{ //读取json数据
BufferedReader reader = request.getReader();
while ((line = reader.readLine()) != null)
{
requestData.append(line);
}
}
catch(Exception e)
{
e.printStackTrace();
}
JSONObject requestInfo = JSONObject.fromObject(requestData.toString());
JSONObject res = new JSONObject();
if (customerInfo.isNameExisted(requestInfo.getString("name")))
{ //用户名存在
if (customerInfo.isExisted(requestInfo.getString("name"), requestInfo.getString("password")))
{ //密码正确
res.put("exist", true);
res.put("loginAble", true);
}
else //密码不正确
{
res.put("exist", true);
res.put("loginAble", false);
}
}
else
{ //用户名不存在
res.put("exist", false);
res.put("loginAble", false);
}
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.print(res.toString());
}
}
客户端的 POST 请求中将附带 name 和 password 这两个参数,并返回 loginAble参数。
(完)
大作业项目源码:
https://github.com/cajet/FilmGoGo
(题外话:通过大作业还学到了很多关于设计模式方面的东西,具体的有时间再写博客进行记录。欢迎纠错~)