MVC设计模式
什么是设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。
设计模式使代码编写真正工程化;
设计模式是软件工程的基石脉络,如同大厦的结构一样。
设计模式就是一种模子,经过多年实践锤炼形成一套行之有效的完成某个特定任务的步骤和方式。
例如:西凤酒的酿造过程,酿造工序,前后不能变,温差不能变,这样做就是好喝,稍微改动就变味道了。
再如,北京烤鸭,就是那样做,就是那些调料腌制,变量配比,味道口感就是不如人家。
MVC设计模式
MVC设计模式是一种通用的软件编程思想
在MVC设计模式中认为, 任何软件都可以分为三部分组成:
(1)控制程序流转的控制器(Controller)
(2)封装数据处理数据的模型(Model)
(3)负责展示数据的视图(view)
并且在MVC设计思想中要求一个符合MVC设计思想的软件应该保证上面这三部分相互独立,互不干扰,每一个部分只负责自己擅长的部分。
如果某一个模块发生变化,应该尽量做到不影响其他两个模块。这样做的好处是,软件的结构会变得更加的清晰,可读性强。有利于后期的扩展和维护,并且代码可以实现复用。
初识SpringMVC
Servlet的缺点
1、通常情况下,一个Servlet类只负责处理一个请求,若项目中有成百上千个请求需要处理,就需要有成百上千个Servlet类,这样会使得项目中Servlet类的个数暴增;
2、在Servlet3.0版本之前,每一个Servlet都需要在web.xml文件中至少做八行配置信息,配置内容多且繁琐。当Servlet特别多时,web.xml配置量太多,不利于团队开发;
3、当通过客户端提交参数到服务器,通过Servlet进行接收时,无论数据本身是什么格式,在Servlet中一律按照字符串进行接收,后期需要进行类型转换,复杂类型还需要特殊处理,特别麻烦!
String value = request.getParameter(String name);
4、servlet具有容器依赖性,必须放在服务器中运行,不利于单元测试;
...
SpringMVC简介
Springmvc是spring框架的一个模块,spring和springmvc无需中间整合层整合
Springmvc是一个基于mvc的web框架
spring执行原理
(1)
.用户发送请求 至 前端控制器(DispatcherServlet);
提示:DispatcherServlet的作用:接收请求,调用其它组件处理请求,响应结果,相当于转发器、中央处理器,是整个流程控制的中心
(2)
.前端控制器(DispatcherServlet)收到请求后调用处理器映射器(HandlerMapping)
处理器映射器(HandlerMapping)找到具体的Controller(可以根据xml配置、注解进行查找),并将Controller返回给DispatcherServlet;
(3)
.前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)。处理器适配器经过适配调用具体的Controller;(Controller--> service --> Dao --> 数据库)
Controller执行完成后返回ModelAndView,
提示:Model(模型数据,即Controller处理的结果,Map) View(逻辑视图名,即负责展示结果的JSP页面的名字)
处理器适配器(HandlerAdapter)将controller执行的结果(ModelAndView)返回给前端控制器(DispatcherServlet);
(4)
.前端控制器(DispatcherServlet)将执行的结果(ModelAndView)传给视图解析器(ViewReslover)
视图解析器(ViewReslover)根据View(逻辑视图名)解析后返回具体JSP页面
(5)
.前端控制器(DispatcherServlet)根据Model对View进行渲染(即将模型数据填充至视图中);
前端控制器(DispatcherServlet)将填充了数据的网页响应给用户。
其中整个过程中需要开发人员编写的部分有Controller、Service、Dao、View;
springmvc入门案例
需求:
(1)通过浏览器访问 http://localhost/项目名称/hello
地址,在控制台输出 "hello springmvc"
(2)将请求转向(跳转到) /WEB-INF/pages/home.jsp
页面
创建Maven—web工程
1、通过Maven创建web工程
2、在pom.xml中引入springmvc所需jar包:将下面的配置直接拷贝到pom.xml中的根标签内
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<!-- SpringMVC的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<!-- servlet 和 jsp的jar包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- java对象转换json的工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
在web.xml中配置前端控制器
<?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">
<!-- 配置springmvc前端控制器, 将所有请求交给springmvc来处理 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springmvc核心配置文件的位置,默认Springmvc的配置文件是在WEB-INF目录下,默认的名字为springmvc-servlet.xml,如果要放在其他目录,则需要指定如下配置:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-config.xml</param-value>
</init-param>
</servlet>
<!-- 其中的斜杠(/)表示拦截所有请求(除JSP以外), 所有请求都要经过springmvc前端控制器 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
创建并配置springmvc-config.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
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">
<!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) -->
<mvc:default-servlet-handler/>
<!-- 2.配置注解驱动,用于识别注解(比如@Controller) -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 3.配置需要扫描的包:spring自动去扫描 base-package 下的类,
如果扫描到的类上有 @Controller、@Service、@Component等注解,
将会自动将类注册为bean
-->
<context:component-scan base-package="com.tedu.controller">
</context:component-scan>
<!-- 4.配置内部资源视图解析器
prefix:配置路径前缀
suffix:配置文件后缀
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
创建并实现HelloController类
1、创建com.tedu.controller.HelloController类
2、实现HelloController类
package com.tedu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/* @Controller作用: 表示当前类属于controller层
* 同时标识将当前类的对象的创建交给spring容器负责
* http://localhost/day16-springmvc/hello
*/
@Controller
public class HelloController {
/* @RequestMapping("/hello") 用于配置当前方法的访问路径,不能省略,且不能重复!
* @RequestMapping注解在当前方法上声明的访问路径, 在当前controller类中不能重复!
* 如果controller类上没有访问路径,当前方法上的访问路径在所有controller类中都不能重复!
*/
@RequestMapping("/hello")
public String testHello() {
System.out.println( "hello springmvc...." );
//跳转到哪一个路径 /WEB-INF/pages/home.jsp
return "home";
}
}
创建并实现home.jsp
在WEB-INF/pages/目录下,创建home.jsp页面。
WEB-INF/pages/home.jsp
<%@ page pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>day16-springmvc...home.jsp....</h1>
</body>
</html>
访问测试
打开浏览器,输入url地址:http://localhost/day16-springmv/hello 地址,访问结果如下:
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tedu</groupId>
<artifactId>day20-springmvc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!-- SpringMVC的jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<!-- servlet 和 jsp的jar包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<!-- java对象转换json的工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
</project>
<?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"
version="2.5">
<display-name>day20-springmvc</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置springmvc的前端控制器 ,(DispatcherServlet) 配置拦截除了jsp以外的所有请求(ctrl+shift+t) -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置springmvc-config文件的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name><!-- 这里配置的是固定值 -->
<param-value>classpath:springmvc-config.xml</param-value><!-- 这里是根据文件名和位置决定 -->
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- /* :拦截静态web资源(html/css/js/图片)以及JSP / :拦截静态web资源(html/css/js/图片)但是不包括JSP -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置过滤器,解决POST提交的中文参数乱码问题 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.Springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>day20-springmvc....home.jsp....</h1>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>day20-springmvc....test.jsp....</h1>
${name }
<hr/>
${user.getName() }
${user.getAge() }
${user.getAddr() }
<hr/>
${user.name() }
${user.age() }
${user.addr() }
</body>
</html>
package com.tedu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Controller 如果当前类所在的包配置了spring容器包扫描,具有该注解的类,
* 就会作为bean注册到spring容器中,由spring容器创建实例
* @author huawei
*
*/
@Controller
public class HelloController {
/**
* @RequestMapping 为当前方法配置访问路径
* 当前浏览器访问http://localhost:8080/day20-springmvc/{controller路径}/hello
* @return
*/
@RequestMapping("/hello")
public String testHello() {
System.out.println("Hello springmvc.....");
//跳转到/WEB-INF/pages/home.jsp
return "home";
}
}
package com.tedu.controller;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tedu.pojo.User;
@Controller
public class ParamController {
/**
* 1.springmvc的参数绑定--简单类型的参数绑定
* http://localhost:8080/day20-springmvc
* ../testParam01?name=张飞&age=35&addr=河北
* 可以在方法上声明和请求参数名同名的形参
* 例如,在testParam01方法上声明name,age,addr三个形参
* 用于接收请求参数中name,age,和addr的值
* @return
*/
@RequestMapping("/testParam01")
public String testParam01(String name,Integer age,String addr) {
System.out.println("ParamController.testParam01()");
System.out.println("name="+name);
System.out.println("age="+age);
System.out.println("addr="+addr);
return "home";
}
/**
* 练习:使用springmvc的参数绑定机制获取请求中的参数的值
*../testParam02?user=刘备&psw=abc123&like=足球&like=篮球
*request.getParameterValues("like")
* @return
*/
@RequestMapping("/testParam02")
public String testParam02(String user,String psw,String[] like) {
System.out.println("user"+user);
System.out.println("psw"+psw);
System.out.println("like"+Arrays.toString(like));
return "home";
}
/**
* 2.包装类型的参数绑定(将请求中的参数直接封装到对象中)
* ../testParam03?name=赵云&age=28&addr=北京昌平
* 可以直接将请求中的参数封装到POJO对象中
* 根据请求参数的名字(name,age,addr)到POJO对象中寻找
* 对应的setXXX方法(setName/setAge/setAddr)将请求中的参数值封装到POJO对象中
*/
@RequestMapping("/testParam03")
public String testParam03(User user) {
System.out.println(user);
return "home";
}
/**
* 3.日期类型的参数绑定(可能会出现400错误:参数类型不匹配)
* ../testParam04?date=2020/08/24 9:39:50
* ../testParam04?date=2020-08-24 9:39:50
* 由于SpringMVC在接收日期类型的参数时,默认使用斜杠(/)分隔
* 如果在传递日期类型的参数时,日期参数不是/分隔,就会出现400错误
* 解决方法:
* 1.在传递日期类型的参数时,用斜杠(/)分隔
* 2.如果使用横杠(-)分隔日期类型的参数,需要通知Springmvc
* 将/分隔日期改为使用横杠(-)分隔
*/
@RequestMapping("/testParam04")
public String testParam04(Date date) {
System.out.println(date.toLocaleString());
return "home";
}
//添加springmvc自定义日期格式转换器
/* 自定义日期格式转换器
* 将springmvc默认以斜杠(/)分隔日期改为以横杠分隔(-)
* ctrl+shift+o导包
*/
@InitBinder
public void InitBinder (ServletRequestDataBinder binder){
binder.registerCustomEditor(java.util.Date.class,
new CustomDateEditor(new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"), true)
);
}
/**
* 4.乱码处理(POST提交的中文乱码问题)
* 接收请求中的user和like两个参数的值
* 使用GET提交 提交表单中的User和Like:没有乱码,所以不用处理
* 使用POST提交 提交表单中的User和Like:有乱码,需要处理
* SpringMVC针对POST提交中文参数有乱码提供解决方案
* 我们只需要启用SpringMVC提供的乱码处理过滤器即可解决POST提交乱码问题
*
*/
@RequestMapping("/testParam05")
public String testParam05(String user,String[] like) {
System.out.println("user"+user);
System.out.println("like="+Arrays.toString(like));
return "home";
}
/**
* 5.实现跳转:请求转发,转发的特点为:
* 1.转发是一次请求,一次响应
* 2.转发前后地址栏地址不会发生变化
* 3.转发只能是同一个应用内部的资源跳转
* 4.转发可以和域对象配合使用,带数据到目的地
* 转发实例:
* 1.从Controller中的方法直接转发到test.jsp(return "jsp的名字")
* 2.从Controller中的方法转发到另外一个方法
*
*/
@RequestMapping("/testForward")
public String testForward(Model model) {
System.out.println("ParamController.testForward()");
//往request域中存数据(model底层就是request域)
model.addAttribute("name","刘德华");
//返回值是jsp的名字,默认就表示转发到这个JSP
//return "test";
//返回值是以forward开头,就表示要转发到别的controller方法
return "forward:/test01";
}
@RequestMapping("/test01")
public String test01() {
System.out.println("test01()执行了.....");
//转发到test.jsp
return "test";
}
/**
* 6.实现跳转: 重定向
* 1.重定向是两次请求,两次响应
* 2.重定向前后地址栏地址会发生变化
* 3.重定向没有限制,可以是一个web应用或者不同web应用之间的资源
* 4.重定向不能配合request域带数据
* 重定向实例:
* 1.从controller方法重定向到其他的controller方法
* 2.从当前应用重定向到别的web应用程序
* 3.重定向到别的服务器,比如百度
*/
@RequestMapping("/testRedirect")
public String testRedirect() {
System.out.println("testRedirect()执行了.....");
//重定向到/test02对应的方法
//return "redirect:/test02";
//重定向到/day14-jsp/index.jsp
//return "redirect:http://localhost:8080/day14-jsp/index.jsp";
//重定向到http://www.baidu.com
return "redirect:http://www.baidu.com";
}
@RequestMapping("/test02")
public String test02() {
System.out.println("test02()执行了....");
//转发到test.jsp
return "test";
}
/**
* 7.通过Model响应数据(Model+转发带数据到JSP)
*/
@RequestMapping("/testModel")
public String testModel(Model model) {
//可以将数据存入Model中(底层就是request域)
User u1=new User();
u1.setName("韩信");
u1.setAge(18);
u1.setAddr("北京");
model.addAttribute("user",u1);
//转发到test.jsp
return "test";
}
}
package com.tedu.pojo;
/**
* 用于封装用户信息
* @author huawei
*
*/
public class User {
private String name;
private Integer age;
private String addr;
//提供get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
//提供toString方法
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", addr=" + addr + "]";
}
}
<?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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
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">
<!-- 1.配置前端控制器放行静态资源(html/css/js等,否则静态资源将无法访问) -->
<mvc:default-servlet-handler/>
<!-- 2.配置注解驱动,用于识别注解(比如@Controller) -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 3.配置需要扫描的包:spring自动去扫描 base-package 下的类,
如果扫描到的类上有 @Controller、@Service、@Component等注解,
将会自动将类注册为bean
-->
<context:component-scan base-package="com.tedu.controller">
</context:component-scan>
<!-- 4.配置内部资源视图解析器
prefix:配置路径前缀
suffix:配置文件后缀
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>