原理
controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
实现
在 springmvc 中配置视图解析器
<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>
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testString")
public String testString(Model model){
// 模拟从数据库中查询出 User 对象
User user = new User();
user.setName("张三");
user.setAge(18);
user.setGender("男");
model.addAttribute("user1",user);
// 指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp
return "success";
}
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
Title
访问成功
${user1}
原理
如果控制器的方法返回值编写成 void,执行程序报 404 的异常,因为默认查找 JSP 页面没有找到。(默认查找 @RequestMapping 中配置的地址.jsp)
可以使用请求转发或者重定向跳转到指定的页面
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 请求转发
// request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);
// 重定向
// response.sendRedirect(request.getContextPath()+"/index.jsp");
}
}
原理
ModelAndView 对象是 Spring 提供的一个对象,可以用来调整具体的 JSP 视图
实例
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
// 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
// 模拟从数据库中查询出 User 对象
User user = new User();
user.setName("张三");
user.setAge(18);
user.setGender("男");
// 把 user 对象存储到 mv 对象中,也会把 user 对象存入到 request 对象
mv.addObject("user1",user);
// 跳转到哪个页面
mv.setViewName("success");
return mv;
}
}
原理
使用关键字的方式进行转发或者重定向
实现
/**
* 使用关键字的方式进行转发或者重定向
* @return
*/
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect(){
// 请求的转发
// return "forward:/WEB-INF/pages/success.jsp";
// 重定向
return "redirect:/index.jsp";
}
@ResponseBody 注解实现将 controller 方法返回 JavaBean 转换为 JSON 响应给客户端。
使用 @RequestBody 注解获取请求体数据并封装为 JavaBean,再使用 @ResponseBody 注解实现将 controller 方法返回的 JavaBean 转换为 JSON 响应给客户端,从而实现客户端和服务端的交互。
因为 DispatcherServlet(前端控制器)会拦截所有的资源,所以静态资源(img、css、js)也会被拦截,从而静态资源不能被使用。所以要先配置静态资源不被拦截,在 springmvc.xml 配置文件添加如下配置
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>
导入 jackson 的依赖
Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 的包。
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-annotationsartifactId>
<version>2.9.0version>
dependency>
User
public class User implements Serializable {
private String name;
private Integer age;
private String gender;
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 getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
response.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
<script src="js/jquery-3.3.1.min.js">script>
head>
<script>
function fun() {
$.ajax({
// 编写json格式,设置属性和值
url:"user/testJSON",
contentType:"application/json;charset=UTF-8",
data:JSON.stringify({"name":"jack","age":19,"gender":"man"}),
dataType:"json",
type:"post",
success:function(data){
alert(data);
alert(data.name);
}
});
}
script>
<body>
<input type="button" onclick="fun()" value="发送ajax请求">
body>
html>
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testJSON")
public @ResponseBody User testJSON(@RequestBody User user){
System.out.println(user);
return user;
}
}
导入文件上传的依赖
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.4version>
dependency>
编写文件上传的 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
文件上传
编写文件上传的控制器
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload1")
public String fileuoload1(HttpServletRequest request) throws Exception {
// 使用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";
}
}
原理
实现
在 springmvc.xml 中配置文件解析器对象
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
bean>
编写文件上传的 jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
文件上传
编写文件上传的控制器
@RequestMapping("/fileupload2")
public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception {
// 使用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";
}
跨服务器文件上传的目的
在实际开发中,我们会有很多处理不同功能的服务器。 分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
实现
搭建两台服务器,一台作为应用服务器(端口号:8080),一台作为文件服务器(端口号:9090)
在 springmvc.xml 中配置文件解析器对象
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/>
bean>
在应用服务器导入开发所需的依赖
<dependency>
<groupId>com.sun.jerseygroupId>
<artifactId>jersey-coreartifactId>
<version>1.18.1version>
dependency>
<dependency>
<groupId>com.sun.jerseygroupId>
<artifactId>jersey-clientartifactId>
<version>1.18.1version>
dependency>
在应用服务器编写文件上传的 JSP 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
文件上传
在应用服务器编写文件上传的控制器
/**
* 跨服务器文件上传
* @return
*/
@RequestMapping("/fileupload3")
public String fileuoload3(MultipartFile upload) throws Exception {
// 定义上传文件服务器路径
String path = "http://localhost:9090/uploads/";
// 说明上传文件项
// 获取上传文件的名称
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
// 上传文件
webResource.put(upload.getBytes());
return "success";
}
几种报错问题的解决办法
以下三种错误我都遇到了:
错误 400
这是由于你上传的文件名是中文,解决办法就是改成英文
错误403
打开你本地的 apache-tomcat-x.x.x\conf 中的 web.xml,找到下面的内容
<servlet>
<servlet-name>defaultservlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
<init-param>
<param-name>debugparam-name>
<param-value>0param-value>
init-param>
<init-param>
<param-name>listingsparam-name>
<param-value>falseparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
添加 readonly:flase 标签,如下:
<servlet>
<servlet-name>defaultservlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
<init-param>
<param-name>debugparam-name>
<param-value>0param-value>
init-param>
<init-param>
<param-name>readonlyparam-name>
<param-value>falseparam-value>
init-param>
<init-param>
<param-name>listingsparam-name>
<param-value>falseparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
重新启动服务器
错误 409
在文件服务器中的 target/项目名下,新建一个 uploads 文件夹
系统的 dao、service、controller 出现异常都向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理
编写异常类
package com.zt.exception;
public class SysException extends Exception {
private String message;
public SysException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
编写错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
${errorMsg}
编写自定义异常处理类
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
// 获取到异常对象
SysException sysException = null;
if(e instanceof SysException){
sysException = (SysException)e;
}else{
e = new SysException("系统正在维护....");
}
// 创建ModelAndView对象
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg",e.getMessage());
mv.setViewName("error");
return mv;
}
}
在 springmvc.xml 中配置异常处理器
<bean id="sysExceptionResolver" class="com.zt.exception.SysExceptionResolver">bean>
编写控制器
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testException")
public String testException() throws SysException {
try {
int a = 1/0;
} catch (Exception e) {
e.printStackTrace();
throw new SysException("出错了");
}
return "success";
}
}
编写 JSP 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
testException
Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。
自定义拦截器
public class MyIntercepter implements HandlerInterceptor {
/**
* 预处理,controller方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行 controller 中的方法
* return false 不放行
* @param request
* @param response
* @param handler
* @return
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("preHandle...");
return true;
}
/**
* 后处理方法,controller方法执行后,success.jsp执行之前
* @param request
* @param response
* @param handler
* @param modelAndView
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("postHandle...");
}
/**
* success.jsp页面执行后,该方法会执行
* @param request
* @param response
* @param handler
* @param ex
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("afterCompletion...");
}
}
在 springmvc.xml 中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/*"/>
<bean class="com.zt.intercepter.MyIntercepter">bean>
mvc:interceptor>
mvc:interceptors>
编写控制器
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testInterceptor")
public String testInterceptor(){
System.out.println("testInterceptor...");
return "success";
}
}
打印结果
preHandle...
testInterceptor...
postHandle...
success.jsp
afterCompletion...
preHandle 方法是 controller 方法执行前拦截的方法
可以使用 request 或者 response 跳转到指定的页面
return true 放行,执行下一个拦截器,如果没有拦截器,执行 controller 中的方法。
return false 不放行,不会执行 controller 中的方法。
postHandle 是 controller 方法执行后执行的方法,在 JSP 视图执行前。
postHandle 方法是在 JSP 执行后执行
在配置一个自定义拦截器
在 springmvc.xml 中再配置一个拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/*"/>
<bean class="com.zt.intercepter.MyIntercepter">bean>
mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/user/*"/>
<bean class="com.zt.intercepter.MyIntercepter2">bean>
mvc:interceptor>
mvc:interceptors>