Html是“名词”,CSS是“形容词”,JavaScript是“动词”,这三个兄弟凑在一块儿,就构成了 “静态” 页面,那么如何让他 “动态” 起来呢?这就须要后端相关技术的支持,这也是咱们今天想要说的。php
那么又怎么去理解 “静态” 和 “动态” 这两个词呢?css
这两个词最大的不一样就是在于其交互性,静态页面不是指页面不能进行变化,而是指不能与后端进行交互,实现数据的传输与处理,也就是说,静态页面一旦作好后,基本就是这个样子了,更像一个单纯的展现,而动态页面却能够实现根据用户的要求和选择而动态的去改变和响应,浏览器客户端,成为了先后端动态交互的一个桥梁。html
而随着如今用户需求的增长,以及数据量的增长,在Web开发中,可以及时、正确地响应用户的请求几乎已经能够说是必须的了前端
今天重点要学习的就是也就是——如何在获取请求后对其解析,而后执行相关的逻辑处理,最终跳转到页面,将数据回馈java
上面我提到了,在先后端动态交互中,浏览器客户端,成为了先后端沟通的桥梁,这也就是常见的 B/S 架构方式,也就是 浏览器/服务器,在其中最为经常使用的就是三层架构的开发模式jquery
你们在 JavaWeb 的学习过程当中,基本上已经在用三层架构的模式来进行编程,哪三层呢?web
注:以JavaWeb中为例ajax
① 表现层(Web层)spring
② 业务层(Service层)数据库
③ 持久层 (Dao)
有两点须要强调一下:
什么是,某某层依赖于某某层?
例如表现层依赖业务层,在 JavaWeb 阶段实际上就是在 Servlet 中 new 了一个 Service ,固然,在Spring的 IOC 下咱们只须要在控制层中添加Service的引用就能够了,并不须要再new了,耦合大大下降,咱们上面说的依赖主要指两个层之间存在必定的关系
什么是业务逻辑?
针对,一些简单的操做,例如单表数据的增删,实际上几乎没有任何业务,最多例如参数不合法一类的,能加个返回的错误码,但若是面对一些比较复杂的项目,就存在一些业务逻辑须要编写
例如:查询时须要的结果,并非简单的一张表中,而查询条件也比较复杂,咱们就能够经过对查询条件进行拆分,再组合,就能够查询到不一样需求的数据。
再例如:之前文章中我常说的转帐案例,为了不在转帐的整个过程当中发生异常,致使资金发生问题,就须要保证事务的一致性,而这些事务咱们就能够放在业务层来作,固然 Spring 的AOP 能够帮助咱们更好的处理事务问题
MVC 也就是 model-view-controller,咱们来看看它的每一部分
Model(模型)
View(视图)
Controller(控制)
作了一张 MVC 模式下的工程结构图,方便你们理解
实际上,若是是初次接触 Spring MVC 实际上,看个基本概念也就好了,好比下面我提到的,Spring MVC 的优势,Spring MVC 与 Struts 的区别,若是在没有进行过一些基本的使用,以及简单一些流程的简单分析,实际上没啥卵用,这些东西简单看看就好了,通过必定的学习之后,回过头来再看,会有感受的多
Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as “Spring MVC”.
—— Spring官网
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,能够选择使用Spring的Spring MVC框架或集成其余MVC开发框架,如Struts1(如今通常不用),Struts 2(通常老项目使用)等。
—— 百度百科
MVC 在上面咱们已经进行了基本的介绍,而Spring MVC 就是一款基于 MVC架构模式的轻量级Web框架,咱们所说的 Spring MVC 与 Spring Web MVC 是等价的,只不过人们更习惯前者的叫法,这一款框架,本质上也是基于 Servlet 的,若是你有 Servlet 以及 Spring 的基础,简单的上手这个Web框架是很是快的
① Spring MVC 具备 Spring 的优势,例如依赖注入 (IOC) 和切面编程 (AOP)
② 清晰的模块化职能划分,各模块各司其职,清晰明了
③ 能够很是方便的与其余视图技术 (FreeMarker) 整合,因为Spring MVC 的模型数据每每放在 Map 数据结构中,所以能够很方便的被其余框架引用
④ 能够灵活的实现绑定 (binding) 、验证 (validation)
⑤ 简介的异常处理机制
⑥ 比较强大的 JSP 标签库,简化了JSP的开发
⑦ 支持 RESTful 风格
⑧ 提供了强大的约定大于配置的契约式编程支持,也就是提供一种软件设计范式,减小软件开发人员作决定的次数,开发人员仅须要规定应用中不符合约定的部分
Struts 也是一款基于 MVC 这种在开发模式的 JavaEE框架,近些年来,实际上开发者更多的选择使用 SpringMVC 这个框架,那么二者的区别是什么呢?Spring MVC 的过人之处又在哪里呢?
① Spring MVC 基于方法开发,Struts 基于类开发
② Spring MVC 支持单例开发模式,而 Struts 不支持
③ Spring MVC 的速度比 Struts 的速度稍微快一些
④ Spring MVC 使用更加简洁,同时还支持 JSR303,可以比较方便的处理 ajax
⑤ Struts2 的 OGNL 表达式使页面的开发效率相比 Spring MVC 更高一点,可是执行效率对于 JSTL 也没有很明显的提高
① 建立Maven项目 --> ② 选择JDK版本 --> ③ 勾选 create from archetype 即便用骨架建立项目 --> ④ 选择 maven-archetype-webapp 建立出一个web项目
而后指定基本信息,点击下一步
可是,因为建立 maven archetype 的缘由,在建立时,会执行 mvn archetype:generate这个命令,这样就须要指定一个 archetype-catalog.xml 文件,命令中参数 -DarchetypeCatalog 的值有三种
咱们须要作的就是添加这样一组键值对,就能够加快建立项目的速度
这里没什么好说的,基本不须要更改,继续下一步
将版本从1.7改成1.8,接着又在 dependencies 中引入咱们须要的一些 jar 包
定义 <spring.version>5.0.2.RELEASE</spring.version>
这样一个标签对,在下面就能够引用,这样相比于直接将版本信息写到每个 dependencie 中,更利于后期的维护,方便更换版本,这种方式叫作锁定版本
<?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.ideal</groupId>
<artifactId>spring_mvc_01_basic</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring_mvc_01_basic 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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>spring_mvc_01_basic</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
复制代码
刚建立好的项目中,main文件夹下是空的,咱们须要建立出 java 以及 resources 两个文件夹,而且分别设置为,源代码根目录 以及 资源根目录,设置方式以下图
在之前 JavaWeb 阶段中,咱们都很清楚,前端发出的请求,都会被映射到 Web.xml 中,而后匹配到对应的 Servlet 中,而后调用对应的 Servlet 类 来处理这个请求
因为如今咱们使用了 Spring MVC,因此这些请求,咱们就交给 Spring MVC 进行管理,因此须要在工程 webapp-WEB-INF 中找到 web.xml 进,在其中配置核心控制器,也就是 DispatcherServelt
<servlet ></servlet >
标签中指定了一个实现类为 DispatcherServelt ,名称为 dispatcherServlet 的 servlet 配置
<servlet-mapping></servlet-mapping>
标签中则指定了 dispatcherServlet 拦截请求的范围,使用 /
即表明全部请求都须要通过这里
<init-param></init-param>
标签对中放置 DispatcherServelt 所须要的初始化参数,配置的是 contextConfigLocation 上下文参数变量,其加载的配置文件为编译目录下的 springmvc.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初始化参数,读取springmvc的配置文件,建立spring容器-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置servlet启动时加载对象-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
复制代码
在这里,一个是开启扫描,以及开启注解,还有就是配置视图解析器,它的做用就是执行方法后,根据返回的信息,来加载相应的界面,而且绑定反馈数据
<?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">
<!-- 配置spring建立容器时要扫描的包-->
<context:component-scan base-package="cn.ideal"></context:component-scan>
<!-- 配置视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置spring开启注解mvc的支持 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
复制代码
特别说明:通常开发咱们都须要写上这个标签,即便或许如今还没怎么体现出来
package cn.ideal.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ControllerDemo {
@RequestMapping(path = "/test")
public String methodTest(){
System.out.println("这是Controller测试方法");
return "testSuccess";
}
}
复制代码
index.jsp
写一个超连接,去请求test这个路径,也就是指向到了 Controller 下的 methodTest() 方法
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>这是主页面</h3>
<a href="test">访问test试试</a>
</body>
</html>
复制代码
WEB-INF -> pages
testSuccess.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>跳转成功哈</h3>
</body>
</html>
复制代码
我这里,配置了本地的tomcat,以及项目名称
前端控制器(DispatcherServlet)
处理器映射器(HandlerMapping)
处理器适配器(HandlerAdapter)
处理器(Hander)
视图解析器(View resolver)
视图(View)
注:咱们开发人员真正须要进行开发的是处理器(Handler)和视图(View)
也就是,处理用户请求的具体逻辑代码,以及展现给用户的界面
@RequestMaspping 注解是指定控制器能够处理哪些URL请求,这个注解能够放在类或者方法上。
属性:
而通常不在 @RequestMaspping 中配置其余属性的时候,能够省去 value 参数名,直接写一个表明 URL 映射信息的字符串就能够了
例如:@RequestMaspping(/test)
在用户在页面中出发请求的时候,提交表单的数据通常都是 key/value 格式的数据
在传统JavaWeb 中咱们所使用的通常是 request.getParameter() 等方法将请求参数获取到
而Spring MVC中能够经过参数绑定,将客户端请求的这个 key/value 格式的数据绑定到 Controller 处理器方法的形参上,支持的数据类型咱们能够分为三类
index.jsp
注:只截取了部分
<h3>这是主页面</h3>
<a href="user/testA?username=admin&password=admin888">测试一下</a>
复制代码
pages --> testSuccess.jsp
<h3>跳转成功哈</h3>
复制代码
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testA")
public String testA(String username, String password) {
System.out.println("获取到的username: " + username);
System.out.println("获取到的password: " + password);
return "testSuccess";
}
}
复制代码
经过构建一个超连接的方式传递参数,例如 ?username=admin
而在后端中若是方法形参与这个username是一致的,这个提交的数据就会被绑定到参数username中
参数中使用 JavaBean 类型接收时,在提交表单的时候,就须要将其中的 name 属性中的值与实体类中的成员变量的值是同样的
若是一个JavaBean类中包含其余的引用类型,那么表单的name属性须要编写成:对象.属性例如:account.username
index.jsp
<form action="user/testB" method="post">
昵称: <input type="text" name="nickname"><br/>
年龄: <input type="text" name="age"><br/>
住址: <input type="text" name="address"><br/>
用户名: <input type="text" name="account.username"><br/>
密码: <input type="text" name="account.password"><br/>
<input type="submit" value="提交">
</form>
复制代码
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testB")
public String testB(User user) {
System.out.println(user);
return "testSuccess";
}
}
复制代码
实体类 User 和 Account
public class User implements Serializable {
private String nickname;
private int age;
private String address;
private Account account;
......省略 get set toString方法
}
复制代码
public class Account implements Serializable {
private String username;
private String password;
......省略 get set toString方法
}
复制代码
对于集合类型,仍然使用一个表达式的写法
index.jsp
<form action="user/testB" method="post">
昵称: <input type="text" name="nickname"><br/>
年龄: <input type="text" name="age"><br/>
住址: <input type="text" name="address"><br/>
用户名1: <input type="text" name="list[0].username"><br/>
密码1: <input type="text" name="list[0].password"><br/>
用户名2: <input type="text" name="map['First'].username"><br/>
密码2: <input type="text" name="map['First'].password"><br/>
<input type="submit" value="提交">
</form>
复制代码
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testB")
public String testB(User user) {
System.out.println(user);
return "testSuccess";
}
}
复制代码
实体类 User 和 Account
public class User implements Serializable {
private String nickname;
private int age;
private String address;
private List<Account> list;
private Map<String, Account> map;
......省略 get set toString方法
}
复制代码
public class Account implements Serializable {
private String username;
private String password;
......省略 get set toString方法
}
复制代码
在 web.xml 中的 <web-app></web-app>
标签内配置过滤器类,达到解决请求参数中文乱码的问题
<!--配置解决中文乱码的过滤器-->
<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>
复制代码
做用:把请求中的指定名称的参数传递给控制器中的形参
属性
value:请求参数中的名称
name:name是value的别名,一个帐户
required:是否必需,默认为 true,即 请求中必须包含该参数,若是没有包含,将会抛出异常(可选)
@RequestMapping(path="/hello")
public String sayHello(@RequestParam(value="nick",required=false)String nickname) {
System.out.println(nickname);
return "success";
}
复制代码
@RequestMapping("/testC")
public String testC(@RequestBody String body) {
System.out.println(body);
return "testSuccess";
}
复制代码
例如接收到这样的语句
nickname=BWH_Steven&age=666&address=beijing
复制代码
UserController
@RequestMapping(path="/test/{uid}")
public String testD(@PathVariable(value="uid") String id) {
System.out.println(id);
return "testSuccess";
}
复制代码
index.jsp
<a href="user/test/66">访问test试试</a>
复制代码
UserController
@RequestMapping("/testD")
public String testD(@RequestHeader(value="Accept") String header) {
System.out.println(header);
return "testSuccess";
}
复制代码
index.jsp
<a href="user/testD">访问test试试</a>
复制代码
打印结果:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3
复制代码
属性:
UserController
@RequestMapping("/testF")
public String testF(@CookieValue(value="JSESSIONID") String cookieValue) {
System.out.println(cookieValue);
return "testSuccess";
}
复制代码
index.jsp
<a href="user/testF">访问test试试</a>
复制代码
打印结果:
FCFDD389AC473F837266FC890E9E6F36
复制代码
做用:
在方法上:表示当前方法会在控制器方法执行前执行
在参数上:获取指定的数据给参数赋值
应用场景:
index.jsp
只提供修改年龄和地址的表单,同时传一个隐藏域中的id,方便去数据库查询(固然咱们这里是模拟的)
<form action="user/testG" method="post">
<input type="hidden" name="uid" value="1">
年龄: <input type="text" name="age"><br/>
住址: <input type="text" name="address"><br/>
<input type="submit" value="提交">
</form>
复制代码
UserController
实体类就不给出了,就是三个成员,nickname age address
若是没有下面这个增长了 @ModelAttribute 注解的 findUserByUid方法,当执行 testG 方法后,会获取到一个 nickname = null 的值
而咱们下面的作法,在执行 testG 以前会先执行 findUserByUid,而后能够去数据库中根据uid查询,固然咱们这里是模拟的,而后将这个user返回
接着执行 testG 方法的时候,就能将用户提交的 age 和 address 获取到,同时将用户没有提交的 nickname 使用数据库中的值
@RequestMapping("/testG")
public String testG(User user) {
System.out.println("这是testG方法");
System.out.println("用户修改后的: " + user);
return "testSuccess";
}
@ModelAttribute
public User findUserByUid(Integer uid) {
System.out.println("这是findUserByUid方法");
//模拟查询数据库
User user = new User();
user.setNickname("BWH_Steven");
user.setAge(66);
user.setAddress("北京");
System.out.println("数据库中查询到: " + user);
return user;
}
复制代码
另外一种方式
若是没有返回值的方式就能够这样改写,增长一个 map ,而后存进去
@RequestMapping("/testG")
public String testG(@ModelAttribute("first") User user) {
System.out.println("这是testG方法");
System.out.println("用户修改后的: " + user);
return "testSuccess";
}
@ModelAttribute
public void findUserByUid(Integer uid, Map<String,User> map) {
System.out.println("这是findUserByUid方法");
//模拟查询数据库
User user = new User();
user.setNickname("BWH_Steven");
user.setAge(66);
user.setAddress("北京");
System.out.println("数据库中查询到: " + user);
map.put("first",user);
}
复制代码
属性
UserController
在存入方法跳转以前,会将数据保存到 nickname age address 中,由于注解@SessionAttribute中有这几个参数
@Controller
@RequestMapping("/user")
@SessionAttributes(value = {"nickname","age","address"})
public class UserController {
/** * 向session中存入值 * @param model * @return */
@RequestMapping("/addUser")
public String addUser(Model model) {
model.addAttribute("nickname", "BWH_Steven");
model.addAttribute("age", 20);
model.addAttribute("address", "北京");
return "testSuccess";
}
/** * 从session中获取值 * @param modelMap * @return */
@RequestMapping("/findUser")
public String findUser(ModelMap modelMap) {
String nickname = (String) modelMap.get("nickname");
Integer age = (Integer)modelMap.get("age");
String address= (String) modelMap.get("address");
System.out.println(nickname + " " + age + " " + address);
return "testSuccess";
}
/** * 清除值 * @return */
@RequestMapping("/delete")
public String delete(SessionStatus status) {
status.setComplete();
return "testSuccess";
}
}
复制代码
index.jsp
<a href="user/addUser">访问addUser试试</a>
<a href="user/findUser">访问findUser试试</a>
<a href="user/delete">访问delete试试</a>
复制代码
testSuccess.jsp
注意设置 isELIgnored="false"
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>跳转成功哈</h3>
${nickname}
${age}
${address}
${sessionScope}
</body>
</html>
复制代码
讲完了请求与参数绑定,以及一些经常使用的注解,接着就能够说一下响应的一些知识,也就是咱们接受到用户的请求,而且进行必定的处理之后,如何进行正确的响应
其实在前面的讲解中,咱们一直用的就是返回字符串的形式,而结果也是很直观的,也就是,进行了同名页面的跳转,例如返回 success 则跳转到 success.jsp 的页面中
这也就是说,Controller 方法返回字符串能够指定逻辑视图的名称,视图解析器会将其解析成物理视图的地址
演示一种常见的使用场景
index.jsp
<a href="user/testString">修改用户信息页面</a>
复制代码
UserController
注:实体类就不谈了,只有 username 和 password 两个成员
模拟一个数据库查询到数据信息,而后将数据存到 request 域中
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testString")
public String testString(Model model) {
//模拟从数据库中查询
User user = new User();
user.setUsername("张三");
user.setPassword("888666");
model.addAttribute("user", user);
return "success";
}
}
复制代码
success.jsp
注意配置:isELIgnored="false"
当用户点击主页面中进行页面修改,就会跳转到这个用户名以及密码的修改界面,同时将数据进行回显,优化体验
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>修改</h3>
<form action="" method="post">
用户名:<input type="text" name="username" value="${ user.username }"><br>
密码:<input type="text" name="password" value="${ user.password }"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
复制代码
<a href="user/testForward">测试一下</a>
复制代码
@RequestMapping("/testForward")
public String testForward() throws Exception{
System.out.println("testForward 被执行了");
//转发
// return "forward:/WEB-INF/pages/success.jsp";
//重定向
return "redirect:/index.jsp";
}
复制代码
若是说直接去掉返回值,以及修改返回类型为void,会报出一个404异常,能够看到地址栏中,去指向了一个 http://localhost:8080/springmvc-response/user/testVoid.jsp
的地址,也就是说它默认去查找了一个jsp页面(也就是 @RequestMapping("/testVoid") 值同名的 jsp),不过没有找到
若是想要在这种状况下,跳转页面可使用请求转发,或者重定向跳转
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request,HttpServletResponse response) throws Exception {
System.out.println("请求转发或者重定向被执行了");
// 1. 请求转发
// request.getRequestDispatcher("/WEB-INF/pages/test1.jsp").forward(request,
response);
// 2. 重定向
// response.sendRedirect(request.getContextPath()+"/test2.jsp");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 3. 直接响应数据
response.getWriter().print("测试被执行了哈");
return;
}
复制代码
这种方式其实和String达到的效果基本是一致的
index.jsp
<a href="user/findAll">测试一下</a>
复制代码
UserController
@RequestMapping("/findUser")
public ModelAndView findUser() throws Exception{
ModelAndView modelAndView = new ModelAndView();
//跳转到jsp
modelAndView.setViewName("success");
//模拟从数据库中查询用户信息
User user = new User();
user.setUsername("李四");
user.setPassword("888888");
modelAndView.addObject("user",user);
return modelAndView;
}
复制代码
success.jsp
${user.username}
${user.password}
复制代码
在 web.xml 中配置的 DispatcherServle(前端控制器),会拦截到全部的资源,在之后的开发中,一个特别显著的问题就是,静态资源 (img、css、js)这样的文件也被拦截了,也就没法使用,咱们首先须要了解的就是如何不对静态资源进行拦截
很是简单,在springmvc.xml中配置就能够了
mvc:resources 标签就能够配置不过滤
<!--前端控制器-->
<mvc:resources mapping="/css/**/" location="/css/"/>
<mvc:resources mapping="/images/**/" location="/images/"/>
<mvc:resources mapping="/js/**/" location="/js/"/>
复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
alert("Just for test");
});
});
</script>
</head>
<body>
<%--<a href="user/testString">修改用户信息页面</a>--%>
<%--<a href="user/testForward">测试一下</a>--%>
<button id="btn">发送ajax请求</button>
</body>
</html>
复制代码
index.jsp
在 Javaweb 阶段,你们基本都是有了解过 ajax 的,因此我就直接用了,若是有不熟悉的,能够去查一下api或者找一下教程,格式仍是很是好理解的
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
data:'{"username":"zhangsan","password":"888888"}',
dataType:"json",
type:"post",
success:function (data) {
//解析响应数据
}
})
});
});
</script>
</head>
<body>
<button id="btn">发送ajax请求</button>
</body>
</html>
复制代码
参数中使用 @RequestBody 这个注解,就能够接收到请求体,而后变成这样一个串的形式
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testAjax")
public void testAjax(@RequestBody String body){
System.out.println("testAjax 被执行了");
System.out.println(body);
}
}
复制代码
打印结果就是这样的
testAjax 被执行了
{"username":"zhangsan","password":"888888"}
复制代码
UserControllr
@RequestMapping("/testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
System.out.println("testAjax 被执行了");
// 模拟数据库查询
System.out.println(user);
user.setUsername("admin");
user.setPassword("admin888");
return user;
}
复制代码
使用 @RequestBody String body 接收到的是一个串,而想要直接将 Json 字符串和 JavaBean 对象相互转换,须要 jackson 的jar包,咱们能够增长这样的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
复制代码
index.jsp
<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
//发送ajax请求
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
data:'{"username":"zhangsan","password":"888888"}',
dataType:"json",
type:"post",
success:function (data) {
//解析响应数据
alert(data);
alert(data.username);
alert(data.password);
}
})
});
});
</script>
复制代码
index.jsp
<h3>文件上传</h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form>
复制代码
form表单的enctype的默认值是:application/x-www-form-urlencoded
若是想要进行文件上传,就必需要改成 multipart/form-data(),同时method属性取值必须是Post
注意:当form表单的enctype取值不是application/x-www-form-urlencoded后,request.getParameter()方法就不能再使用
注意:想要实现文件上传,能够借助一些组件,须要导入该组件相应的支撑jar 包:Commons-fileupload 和commons-io
commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它使用须要commons-io包的支持
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload")
public String fileupload(HttpServletRequest request) throws Exception {
System.out.println("文件上传...");
// 使用fileupload组件完成文件上传
// 上传位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 不存在则建立文件夹
file.mkdirs();
}
// 解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析request
List<FileItem> items = upload.parseRequest(request);
// 遍历
for (FileItem item : items) {
// 进行判断,当前item对象是不是上传文件项
if (item.isFormField()) {
// 普通表单项
} else {
// 上传文件项
// 上传文件的名称
String filename = item.getName();
// 把文件的名称设置惟一值,UUID 防止重复覆盖问题
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
item.write(new File(path, filename));
// 删除临时文件
item.delete();
}
}
return "success";
}
}
复制代码
说明:
request.getSession().getServletContext() 获取的是Servlet容器对象,就比如tomcat容器
getRealPath("/") 表明获取实际路径,“/”指代项目根目录
因此代码返回的是项目在容器中的实际发布运行的根路径
index.jsp
<h3>文件上传</h3>
<form action="user/fileupload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form>
复制代码
springmvc.xml
<!--配置文件解析器对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" />
</bean>
复制代码
注意:10485760 = 10x1024x1024
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("文件上传...");
// 使用fileupload组件完成文件上传
// 上传位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断,该路径是否存在
File file = new File(path);
if (!file.exists()) {
// 不存在则建立文件夹
file.mkdirs();
}
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置惟一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 完成文件上传
upload.transferTo(new File(path,filename));
return "success";
}
}
复制代码
不少时候会将整个工程部署到不一样的服务器,例如:
应用服务器,数据库服务器,缓存和消息服务器,文件服务器等等,不过入门来讲了解一下就能够了
想要测试下面的代码,能够配置两个 Tomcat 给不一样端口等配置,模拟一下
index.jsp
<h3>文件上传</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传文件"/>
</form>
复制代码
增长依赖
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
复制代码
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("SpringMVC跨服务器方式的文件上传...");
// 定义图片服务器的请求路径
String path = "http://localhost:9090//springmvc-fileupload/uploads/";
// 获取到上传文件的名称
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 把文件的名称惟一化
filename = uuid + "_" + filename;
// 向图片服务器上传文件
// 建立客户端对象
Client client = Client.create();
// 链接图片服务器
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}
}
复制代码
异常处理也算一个老生常谈的问题,在上线项目或者运行项目的时候,总可能会出现一些没法预料的异常信息,对于开发者而言,天然须要看到具体的异常信息,而后进行排除,而对于用户,天然尽量的出现一些简单,易于理解的语言或者提示
在 Spring MVC 中,提供了一个全局异常处理器,能够对异常进行统一处理
Dao、Service、Controller出现都经过 throws Exception 向上抛出,最后由Spring MVC前端 控制器交由全局异常处理器进行异常处理
对于预期的异常,一般定义一个自定义异常类,用来存储异常的信息
首先这个类继承了 Exception 类,用来描述程序能获取的异常,设置了一个成员message,就是用来存放异常信息的
package cn.ideal.exception;
public class SysException extends Exception {
private String message;
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}
复制代码
全局异常处理器实现的是 Spring MVC 的 HandlerExceptionResolver 接口
这是接口的源码
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}
复制代码
public class SysExceptionResolver implements HandlerExceptionResolver {
/** * 跳转到具体的错误页面的方法 */
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {
ex.printStackTrace();
//解析出异常类型
SysException sysException = null;
// 获取到异常对象
if (ex instanceof SysException) {
//若是异常类型是系统自定义异常,直接取出异常信息,在错误页面展现
sysException = (SysException) ex;
} else {
//若是异常类型不是系统自定义异常,则构造一个自定义异常类型
sysException = new SysException("未知错误");
}
ModelAndView mv = new ModelAndView();
// 存入错误的提示信息
mv.addObject("errorMsg", sysException.getMessage());
// 跳转的Jsp页面
mv.setViewName("error");
return mv;
}
}
复制代码
springmvc.xml 中配置
<bean id="sysExceptionResolver" class="cn.ideal.exception.SysExceptionResolver"/>
复制代码
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("testException")
public String testException() throws SysException {
System.out.println("testException被执行了");
try {
int a = 100 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new SysException("测试这个方法出错了");
}
return "success";
}
}
复制代码
index.jsp
<h3>主页面</h3>
<a href="user/testException">测试一下</a>
复制代码
error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${errorMsg}
</body>
</html>
复制代码
拦截器,用来干吗呢,就好比你须要检测用户的权限,或者把请求信息记录到日志等等,也就是说须要在用户请求先后执行的一些行为
首先在 Spring MVC 中是有两种机制,第一种就是实现 HandlerInterceptor接口,还有一种就是实现 Spring 的 WebRequestInterceptor 接口,不过咱们就简单说一下第一种
自定义一个类简单看一下,实现三个方法
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle方法执行了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法执行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion方法执行了");
}
}
复制代码
① preHandle方法:controller 方法执行前拦截的方法
可使用 request 或者 response 跳转到指定的页面
return true放行,执行下一个拦截器,若是没有拦截器,执行 controller 中的方法
return false不放行,不会执行 controller 中的方法
② postHandle:controller 方法执行后执行的方法,在 JSP 视图执行前
可使用 request 或者 response 跳转到指定的页面
若是指定了跳转的页面,那么 controller 方法跳转的页面将不会显示。
③ postHandle方法:在JSP执行后执行
配置拦截器
注:不要拦截用这个标签<mvc:exclude-mapping path=""/>
注:/user/*
表明全部访问带有 /user/的路径都会被拦截,例如 /user/test
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/user/*"/>
<!--配置拦截器对象-->
<bean class="cn.ideal.interceptor.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
复制代码
随便写一个方法测试一下
@RequestMapping("/testInterceptor")
public String testInterceptor() {
System.out.println("testInterceptor被执行了");
return "success";
}
复制代码
执行结果
preHandle方法执行了
testInterceptor被执行了
postHandle方法执行了
afterCompletion方法执行了
复制代码
写着写着又是1w字了,以前就想着写这篇文章,也一直没什么空,对于这一篇文章,我认为对于入门来讲仍是比较有好的,前面给了几个大点的基本知识讲解,而后从开发环境以及一个入门程序开始,再到请求以及如何响应,以及一些经常使用的注解,再到其余的,文件上传,异常处理,拦截器等知识,基原本说,达到了一个 工具书 + 入门讲解的效果,不过要说的点太多了,即便1w字的文章,实际上也只够简单说起,再加个小案例,就例如拦截器,或者文件上传的讲解,只能说讲了最基本的,对于已经有必定基础的朋友,天然没什么进阶的帮助,不过个人初心,也是想巩固一下本身的知识,而后能将文章带给刚接触 Spring MVC 的朋友,我也不是什么大牛,不过但愿能给你们一点帮助,咱们能够一块儿交流,一块儿进步哈!
感谢你们的支持!!!
若是文章中有什么不足,欢迎你们留言交流,感谢朋友们的支持!
若是能帮到你的话,那就来关注我吧!若是您更喜欢微信文章的阅读方式,能够关注个人公众号
在这里的咱们素不相识,却都在为了本身的梦而努力 ❤
一个坚持推送原创开发技术文章的公众号:理想二旬不止