Spring MVC第一天
一、三层架构和MVC
三层架构:
表现层:
也就是我们常说的web层。它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web 层,web 需要接收 http 请求,完成 http 响应。
表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。
表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。
表现层的设计一般都使用 MVC 模型。(MVC 是表现层的设计模型,和其他层没有关系)
业务层:
也就是我们常说的 service 层。它负责业务逻辑处理,和我们开发项目的需求息息相关。web 层依赖业务层,但是业务层不依赖 web 层。
业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性。(也就是我们说的,事务应该放到业务层来控制)
持久层:
也就是我们是常说的 dao 层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。通俗的讲,持久层就是和数据库交互,对数据库表进行曾删改查的。
MVC 模型
MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式。MVC 中每个部分各司其职:
Model(模型):
通常指的就是我们的数据模型。作用一般情况下用于封装数据。
View(视图):
通常指的就是我们的 jsp 或者 html。作用一般就是展示数据的。通常视图是依据模型数据创建的。
Controller(控制器):
是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。
它相对于前两个不是很好理解,这里举个例子:
例如:
我们要保存一个用户的信息,该用户信息中包含了姓名,性别,年龄等等。
这时候表单输入要求年龄必须是 1~100 之间的整数。姓名和性别不能为空。并且把数据填充到模型之中。
此时除了 js 的校验之外,服务器端也应该有数据准确性的校验,那么校验就是控制器的该做的。
当校验失败后,由控制器负责把错误页面展示给使用者。
如果校验成功,也是控制器负责把数据填充到模型,并且调用业务层实现完整的业务需求。
spring mvc框架介绍:
二、入门案例
需求分析:
前期配置:
1、pom.xml导坐标
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>springmvc_day01_01_start</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>springmvc_day01_01_start Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- 版本锁定 -->
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2、完善项目结构
3、在web.xml中配置前端控制器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--前端的控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4、部署Tomcat
入门案例代码
1、先看一下整个项目结构
index.jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门程序</h3>
<a href="hello">哈哈哈</a>
</body>
</html>
控制器代码:
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//控制器
@Controller
public class HelloController {
@RequestMapping(path = "/hello")
public String sayHello(){
System.out.println("Hello Spring MVC");
return "success";
}
}
success.jsp代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>.....入门程序success</h3>
</body>
</html>
即index.jsp跳到控制器,再根据控制器里面的视图解析跳到success.jsp
springmvc.xml配置文件代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注解和mvc的空间约束-->
<!--要先在web.xml中配置加载springmvc.xml,不然不加载,扫描无效,注解都没有用-->
<!--告诉spring要扫描的包-->
<context:component-scan base-package="cn.itcast"></context:component-scan>
<!--配置视图解析对象-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--告诉文件目录-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!--告诉文件的后缀名-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--开启springmvc框架注解的支持-->
<mvc:annotation-driven/>
</beans>
web.xml文件配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--前端的控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载springmvc配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
@RequestMapping注解
作用:
用于建立请求 URL 和处理请求方法之间的对应关系。
出现位置:
类上:
请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。
它出现的目的是为了使我们的 URL 可以按照模块化管理
方法上:
请求 URL 的第二级访问目录。
属性:
value:用于指定请求的 URL。它和 path 属性的作用是一样的。
method:用于指定请求的方式。
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和
配置的一模一样。
例如:
params = {"accountName"},表示请求参数必须有 accountName
params = {"moeny!100"},表示请求参数中 money 不能是 100。
headers:用于指定限制请求消息头的条件。
注意:
以上四个属性只要出现 2 个或以上时,他们的关系是与的关系。
使用示例:
控制器代码:
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
//控制器
@Controller
@RequestMapping(path = "/user")
public class HelloController {
@RequestMapping(path = "/hello")
public String sayHello(){
System.out.println("Hello Spring MVC");
return "success";
}
@RequestMapping(path = "/test")
public String testRequestMapping(){
System.out.println("Test RequestMapping");
return "success";
}
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门程序</h3>
<%--<a href="hello">哈哈哈</a>--%>
<a href="user/test" >入门程序入口</a>
</body>
</html>
属性:value和path是一样的。
method 属性的示例:
@RequestMapping(value = "/test",method = {RequestMethod.POST})//指定路径和请求方式为post
public String testRequestMapping(){
System.out.println("Test RequestMapping");
return "success";
}
params 属性的示例:
控制器
@RequestMapping(value = "/test",params = {"user"})
public String testRequestMapping(){
System.out.println("Test RequestMapping");
return "success";
}
index.jsp
<a href="user/test?user=xixi" >入门程序入口</a>
index.jsp
<a href="user/test?user=xixi" >入门程序入口</a>
控制器
@RequestMapping(value = "/test",params = {"user=haha"})
public String testRequestMapping(){
System.out.println("Test RequestMapping");
return "success";
}
入门程序的流程总结:
设置成1,是每次启动服务器都会创建DispatcherServlet对象
入门案例使用的组件介绍:
1、DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。
2、 HandlerMapping:处理器映射器
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3 、Handler:处理器
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理
4 、HandlAdapter:处理器适配器
通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
5、 View Resolver:视图解析器
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
< mvc:annotation-driven>说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 <mvc:annotation-driven> 自动加载 RequestMappingHandlerMapping (处理映射器) 和
RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代注解处理器和适配器的配置。
三、请求参数的绑定
零散参数的传递
param.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="param/testParam?user=浩浩&pwd=root">请求绑定参数</a>
</body>
</html>
控制器
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*请求参数绑定*/
@Controller
@RequestMapping("/param")
public class ParamController {
/*请求绑定参数入门*/
@RequestMapping("/testParam")
public String testParam(String user,String pwd){
System.out.println("zhixing.........");
System.out.println("用户:"+ user);
System.out.println("密码:"+ pwd);
return "success";
}
}
请求参数绑定实体类
<form action="param/testJavaBean" method="post">
<%--name里面的值要和实体类的属性名保持一致--%>
姓名:<input type="text" name="name"><br>
密码:<input type="text" name="password"><br>
金额:<input type="text" name="money"><br>
<input type="submit" value="提交"><br>
</form>
请求参数绑定实体类里面有引用类型
<form action="param/testJavaBean" method="post">
姓名:<input type="text" name="name"><br>
密码:<input type="text" name="password"><br>
金额:<input type="text" name="money"><br>
用户名称:<input type="text" name="user.uname"><br>
用户年龄:<input type="text" name="user.age"><br>
<input type="submit" value="提交"><br>
</form>
实体类
在Account中引用User,生成它的get/set方法,并且重新生成Account的tostring方法。在表单name属性里面写user.它的属性名称
post请求乱码,但是get不会乱码,那怎么解决post中文乱码?
在web.xml中配置过滤器
<!--配置解决中文乱码的过滤器-->
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这样就解决了
请求绑定集合类型
Account实体类
package cn.itcast.domain;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class Account implements Serializable {
private String name;
private String password;
private Double money;
private List<User> list;
private Map<String,User> map;
public List<User> getList() {
return list;
}
public void setList(List<User> list) {
this.list = list;
}
public Map<String, User> getMap() {
return map;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
/* private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
", money=" + money +
", list=" + list +
", map=" + map +
'}';
}
}
param.jsp
<%--把数据封装Account中,类中存在list和Map集合--%>
<form action="param/testJavaBean" method="post">
姓名:<input type="text" name="name"><br>
密码:<input type="text" name="password"><br>
金额:<input type="text" name="money"><br>
用户名称:<input type="text" name="list[0].uname"><br>
用户年龄:<input type="text" name="list[0].age"><br>
用户名称:<input type="text" name="map['one'].uname"><br>
用户年龄:<input type="text" name="map['one'].age"><br>
<input type="submit" value="提交"><br>
</form>
控制器类
package cn.itcast.controller;
import cn.itcast.domain.Account;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/*请求参数绑定*/
@Controller
@RequestMapping("/param")
public class ParamController {
/*请求绑定参数入门*/
@RequestMapping("/testParam")
public String testParam(String user,String pwd){
System.out.println("zhixing.........");
System.out.println("用户:"+ user);
System.out.println("密码:"+ pwd);
return "success";
}
/*请求绑定javabean类*/
@RequestMapping("/testJavaBean")
public String testJavaBean(Account account){
System.out.println("zhixing.........");
System.out.println(account);
return "success";
}
}
自定义类型转换器
只要是页面提交的数据都是字符串,
日期格式的2020/11/11,框架会自动帮你转换。但是2020-11-11这样的日期,框架就没法帮你转换了
1、创建一个类,让其实现类型转换功能
package cn.itcast.utils;
import org.springframework.core.convert.converter.Converter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* 将字符串转换为日期
* */
public class StringToDateConverter implements Converter<String, Date> {
//String s 代表传入进来的字符串
@Override
public Date convert(String s) {
if (s == null) {
throw new RuntimeException("请传入数据");
}
try {
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
//将字符串解析为日期
return df.parse(s);
} catch (Exception e) {
throw new RuntimeException("数据格式有误");
}
}
}
2、在springmvc.xml中配置自定义的转换器
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="cn.itcast.utils.StringToDateConverter"></bean>
</set>
</property>
</bean>
<!--开启springmvc框架注解的支持-->
<mvc:annotation-driven/> <!--适配器,处理器,映射器生效-->
<!--让类型转换器生效-->
<mvc:annotation-driven conversion-service="conversionService"/>
想拿到servlet原生的API
param.jsp
<a href="param/testServlet">获取servlet原生的API</a>
控制器代码
/*获得原生的API*/
@RequestMapping("/testServlet")
public String testServlet(HttpServletRequest request, HttpServletResponse response){
System.out.println("zhixing.........");
System.out.println(request);
HttpSession session = request.getSession();
System.out.println(session);
ServletContext servletContext = session.getServletContext();
System.out.println(servletContext);
System.out.println(response);
return "success";
}
输出的都是一些地址值
四、常用注解
@RequestParam
先写jsp发送请求,再写控制器,之后再去配jsp中的路径
anno.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="anno/testRequestParam?uname=哈哈哈">RequestParam</a>
</body>
</html>
控制器代码
package cn.itcast.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/anno")
public class AnnoController {
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam(name = "uname") String name){
System.out.println("执行了。。。");
System.out.println(name);
return "success";
}
}
注意:jsp中的参数名称为uname,而控制器设置的参数名称为name,两者不匹配,这个时候会报错,所以我们可以在参数前面使用@RequestParam注解,将里面的name属性定义为jsp中的参数名称,这样就ok了
@RequestBody
get请求没有请求体,他把参数都封装到了地址栏上
超链接是属于get请求
表单jsp
<form action="anno/testRequestBody" method="post">
用户名称:<input type="text" name="uname"><br>
用户年龄:<input type="text" name="age"><br>
<input type="submit" value="提交"><br>
</form>
控制器
/*testRequestBody*/
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body){
System.out.println("执行了。。。");
System.out.println(body);
return "success";
}
运行结果:
uname=gg&age=2
@PathVariable
restful编程风格:
注意占位符的使用,是斜杠后面加上数值
anno.jsp
<a href="anno/testPathVariable/99">testPathVariable</a>
控制器
@RequestMapping("/testPathVariable/{sid}")
public String testPathVariable(@PathVariable(name = "sid") Integer id){
System.out.println("执行了。。。");
System.out.println(id);
return "success";
}
@ModelAttribute
带返回值的控制器写法
@RequestMapping(value = "/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("执行了。。。");
System.out.println(user);
return "success";
}
/*
该方法先执行,带返回值的
* */
@ModelAttribute
public User test(String uname){
System.out.println("test执行了。。。");
User user=new User();
//通过用户查询数据库(模拟)
user.setUname(uname);
user.setAge(1);
user.setBirthday(new Date());
return user;
}
运行结果:
不带返回值的控制器写法
@RequestMapping(value = "/testModelAttribute")
public String testModelAttribute(@ModelAttribute("aaa") User user){
System.out.println("执行了。。。");
System.out.println(user);
return "success";
}
/*
该方法先执行,不带返回值的
* */
@ModelAttribute
public void test(String uname, Map<String,User> map){
System.out.println("test执行了。。。");
User user=new User();
//通过用户查询数据库(模拟)
user.setUname(uname);
user.setAge(1);
user.setBirthday(new Date());
map.put("aaa",user);
}
运行结果:
jsp写法
<form action="anno/testModelAttribute" method="post">
用户名称:<input type="text" name="uname"><br>
用户年龄:<input type="text" name="age"><br>
<input type="submit" value="提交"><br>
</form>
@SessionAttributes
存到requestion域中
@RequestMapping(value = "/testSessionAttributes")
public String testSessionAttributes(Model model){
System.out.println("testSessionAttributes");
//底层会存储到request域对象中
model.addAttribute("msg","哈哈");
return "success";
}
存到requestion域和session域中
从session域中获取值
控制器
/*从session中获取值*/
@RequestMapping(value = "/getSessionAttributes")
public String getSessionAttributes(ModelMap modelMap){
System.out.println("getSessionAttributes");
//从session域中取值
String msg =(String) modelMap.get("msg");
System.out.println(msg);
return "success";
}
从session域中清除值
控制器
/*从session中清除值*/
@RequestMapping(value = "/deleteSessionAttributes")
public String deleteSessionAttributes(SessionStatus status){
System.out.println("deleteSessionAttributes");
//从session域中清除值
status.setComplete();
return "success";
}
jsp
<a href="anno/testSessionAttributes">testSessionAttributes</a>
<br>
<a href="anno/getSessionAttributes">getSessionAttributes</a>
<br>
<a href="anno/deleteSessionAttributes">deleteSessionAttributes</a>
<br>
点击先存值,再获取,再清除的运行结果:发现清除后再点击获取值就位null