模仿网站 素材哪里来/搜索引擎营销策略有哪些
Spring MVC
课程内容的介绍
一、Spring MVC 入门案例
1. 什么是MVC?
2. Spring MVC
3.第一个Spring MVC 案例
3.1 创建一个Web项目
3.2 添加对应的依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.2.RELEASE</version>
</dependency>
<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope>
</dependency>
3.3 添加一个Spring MVC的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"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"></beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"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"><!-- 注入一个处理器映射器 --><bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /><!-- 注入一个处理器适配器 --><bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /></beans>
3.4 配置前端控制器
<!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><!--配置一个Spring MVC 的前端控制器目的是所有的客户端的请求都会被 DispatcherServlet 处理--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联自定义的Spring MVC的配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping>
</web-app>
3.5 创建自定义的控制器
package com.bobo.controller;import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class UserController implements Controller {/*** 具体处理请求的方法* @param httpServletRequest* @param httpServletResponse* @return* @throws Exception*/@Overridepublic ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception {System.out.println("请求进来了...");ModelAndView mm = new ModelAndView();mm.setViewName("/index.jsp");return mm;}
}
3.6 自定义控制器的注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"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"><!-- 注入一个处理器映射器 --><bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /><!-- 注入一个处理器适配器 --><bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /><!-- 将自定义的控制器注入到IoC容器中 name="/user" 用户访问的请求地址 --><bean class="com.bobo.controller.UserController" name="/user" /></beans>
3.7 Tomcat插件
<plugins><!-- tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><!-- 端口号 --><port>8082</port><!-- /表示访问路径 省略项目名 --><path>/</path><!-- 设置编码方式 --><uriEncoding>utf-8</uriEncoding></configuration></plugin>
</plugins>
3.8 测试
4. 基于注解的实现
4.1 修改Spring MVC 的配置文件
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 添加对应的扫描路径 --><context:component-scan base-package="com.bobo.controller"/><!-- 开启Spring MVC 注解的使用方式 --><mvc:annotation-driven />
</beans>
4.2 创建控制器
package com.bobo.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;import javax.jws.WebParam;@Controller // 将当前类的对象交给容器管理
@RequestMapping("/user") // 配置的而是请求的路径
public class UserController {/*** 查询方法* 请求地址* http://localhost:8082/user/query* @return*/@RequestMapping("/query")public ModelAndView query(){System.out.println("query");ModelAndView mm = new ModelAndView();mm.setViewName("/index.jsp");return mm;}/*** 添加方法* 请求地址* http://localhost:8082/user/addUser* @return*/@RequestMapping("/addUser")public ModelAndView addUser(){System.out.println("add User ...");ModelAndView mm = new ModelAndView();mm.setViewName("/index.jsp");return mm;}
}
4.3测试
5.Spring MVC 的工作原理
5.1 Spring MVC 原理图
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServletcontext.
# Not meant to be customized by application developers.org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
5.2 工作原理文字说明
5.3 相关核心组件的说明
二、Spring MVC的核心操作
1.响应请求
1.1 ModelAndView
/*** 查询方法* 请求地址* http://localhost:8082/user/query* @return*/@RequestMapping("/query")public ModelAndView query(){System.out.println("query");ModelAndView mm = new ModelAndView();mm.setViewName("/index.jsp");return mm;}
1.2 返回void
@RequestMapping("/update")
public void updateUser(){System.out.println("update .... ");
}
/*** 返回结果信息为空* void + @ResponseBody*/@RequestMapping("/update")@ResponseBodypublic void updateUser(){System.out.println("update .... ");}
1.3 返回一个字符串
*** 要跳转到哪个页面我们直接返回该页面对应的字符串即可** @return*/@RequestMapping("/deleteUser")public String deleteUser(){System.out.println("delete .....");// "/index.jsp"中的 "/" 表示的是绝对路径return "redirect:/index.jsp";}
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 添加对应的扫描路径 --><context:component-scan base-package="com.bobo.controller"/><!-- 开启Spring MVC 注解的使用方式 --><mvc:annotation-driven /><!-- 配置视图解析器 --><bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver" ><!-- 配置视图解析器的前后缀--><property name="prefix" value="/" /><property name="suffix" value=".jsp" /></bean>
</beans>
/*** 要跳转到哪个页面我们直接返回该页面对应的字符串即可** @return*/@RequestMapping("/deleteUser1")public String deleteUser1(){System.out.println("delete1 .....");// "/index.jsp"中的 "/" 表示的是绝对路径return "index";}
@RequestMapping("/deleteUser")
public String deleteUser(){System.out.println("delete .....");// "/index.jsp"中的 "/" 表示的是绝对路径return "redirect:/index.jsp";
}
1.4 通过Servlet处理响应
@RequestMapping("/queryAll")
public void queryAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("query All ... ");request.getRequestDispatcher("/index.jsp").forward(request,response);//response.sendRedirect("/index.jsp");
}
2.接收请求数据
2.1 简单数据类型
/*** 接收的参数为简单数据类型* @param userName* @param age* @return*/@RequestMapping("/query1")public String query1(String userName, Integer age){System.out.println("query1 ....."+ userName + " " + age);return "/index.jsp";}
/*** 接收的参数为简单数据类型* @param userName* @param age* @return*/@RequestMapping("/query1")public String query1(@RequestParam(value = "name",defaultValue = "lisi") String userName,@RequestParam(value = "userAge",defaultValue = "18") Integer age){System.out.println("query1 ....."+ userName + " " + age);return "/index.jsp";}
2.2 简单对象
package com.bobo.bean;import org.springframework.web.bind.annotation.RequestParam;import java.util.List;public class StudentBean {private Integer id;private String userName;private Integer age;private String address;private List<String> favrites;public List<String> getFavrites() {return favrites;}public void setFavrites(List<String> favrites) {this.favrites = favrites;}// 包装类private Book book;public Book getBook() {return book;}public void setBook(Book book) {this.book = book;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "StudentBean{" +"id=" + id +", userName='" + userName + '\'' +", age=" + age +", address='" + address + '\'' +", book=" + book +'}';}
}
/*** 通过自定义对象来接收参数* @param stu* @return*/@RequestMapping("/addStudent")public String addStudent(StudentBean stu){System.out.println(stu);return "/index.jsp";}
package com.bobo.bean;public class Book {private Integer bookId;private String bookName;private String author;public Integer getBookId() {return bookId;}public void setBookId(Integer bookId) {this.bookId = bookId;}public String getBookName() {return bookName;}public void setBookName(String bookName) {this.bookName = bookName;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}@Overridepublic String toString() {return "Book{" +"bookId=" + bookId +", bookName='" + bookName + '\'' +", author='" + author + '\'' +'}';}
}
2.3 包装对象
2.4 数组和集合类型
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/29Time: 16:36To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><h1>学生管理</h1><form action="/stu/addStudentNew" method="post" ><label>学生姓名:</label><input type="text" name="userName"><br><label>兴趣爱好:</label><input type="checkbox" name="favrites" value="footerball">足球<input type="checkbox" name="favrites" value="basketball">篮球<br><input type="submit" value="提交"></form>
</body>
</html>
@RequestMapping("/addStudentNew")
public String addStudentNew(String userName,String[] favrites){System.out.println(userName + ":" + Arrays.toString(favrites) );return "/index.jsp";
}
@RequestMapping("/addStudentList")
public String addStudentList(String userName, List<String> favrites){System.out.println(userName + ":" + favrites );return "/index.jsp";
}
2.5 Date类型
package com.bobo.convert;import org.springframework.core.convert.converter.Converter;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;/*** 自定义一个转换器* 实现 String到Date类型的转换*/
public class DateConvert implements Converter<String, Date> {@Overridepublic Date convert(String s) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date date = null;try {date = sdf.parse(s);} catch (ParseException e) {e.printStackTrace();}return date;}
}
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 添加对应的扫描路径 --><context:component-scan base-package="com.bobo.controller"/><!-- 开启Spring MVC 注解的使用方式 --><mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/><!-- 配置视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" ><!-- 配置视图解析器的前后缀<property name="prefix" value="/" /><property name="suffix" value=".jsp" />--></bean><!-- 注册自定义的转换器 --><bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean"id="conversionServiceFactoryBean"><property name="converters"><set><bean class="com.bobo.convert.DateConvert"/></set></property></bean>
</beans>
3.响应用户数据
<!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 version="2.5"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><display-name>Archetype Created Web Application</display-name><!--配置一个Spring MVC 的前端控制器目的是所有的客户端的请求都会被 DispatcherServlet 处理--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联自定义的Spring MVC的配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 配置设置编码的过滤器 --><filter><filter-name>encodingFilter</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><init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value></init-param><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
</web-app>
3.1 通过ModelAndView方式
@RequestMapping("/fun1")public ModelAndView fun1(){System.out.println("fun1...");ModelAndView mm = new ModelAndView();mm.setViewName("/person1.jsp");// 绑定一个回传信息mm.addObject("msg","Hello World");return mm;}
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/29Time: 17:20To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h1>Person管理</h1><label>msg:</label> ${ msg } <br>
</body>
</html>
3.2 Map集合
/*** 响应数据我们可以在形参中声明一个Map集合* 来保存要响应的数据* @return*/@RequestMapping("/fun2")public String fun2(Map<String,Object> map){map.put("msg","map类型数据");map.put("username","张三");System.out.println("fun2....");return "/person2.jsp";}
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/29Time: 17:20To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h1>Person管理</h1><label>msg:</label> ${ requestScope.msg } <br><label>msg:</label> ${ sessionScope.msg } <br><label>msg:</label> ${ applicationScope.msg } <br><label>userName:</label> ${ username } <br>
</body>
</html>
3.3 Model对象
@RequestMapping("/fun3")public String fun3(Model model){model.addAttribute("msg","msg-->model");model.addAttribute("username","bobo");System.out.println("fun3....");return "/person2.jsp";}
3.4 ModelMap对象
@RequestMapping("/fun4")public String fun4(ModelMap model){model.addAttribute("msg","msg-->modelMap你好啊");model.addAttribute("username","bobo");System.out.println("fun4....");return "/person2.jsp";}
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/29Time: 17:20To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<h1>Person管理</h1><label>msg:</label> ${ requestScope.msg } <br><label>msg:</label> ${ sessionScope.msg } <br><label>msg:</label> ${ applicationScope.msg } <br><label>userName:</label> ${ username } <br>
</body>
</html>
4.中文乱码问题
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String encoding = this.getEncoding();if (encoding != null) {if (this.isForceRequestEncoding() || request.getCharacterEncoding() == null) {request.setCharacterEncoding(encoding);}if (this.isForceResponseEncoding()) {response.setCharacterEncoding(encoding);}}filterChain.doFilter(request, response);
}
<!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 version="2.5"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><display-name>Archetype Created Web Application</display-name><!--配置一个Spring MVC 的前端控制器目的是所有的客户端的请求都会被 DispatcherServlet 处理--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联自定义的Spring MVC的配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 配置设置编码的过滤器 --><filter><filter-name>encodingFilter</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><init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value></init-param><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
</web-app>
三、常见应用
1.文件上传操作
1.1 添加fileUpload的依赖
<!-- 添加fileUpload的依赖 -->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version>
</dependency>
1.2 创建表单
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/29Time: 20:03To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><h1>文件上传:</h1><form action="/user/fileUpload" method="post" enctype="multipart/form-data"><label>用户名:</label><input type="text" name="username"><br><label>头像:</label><input type="file" name="headFile"><br><input type="submit" value="上传"></form>
</body>
</html>
1.3 修改配置文件
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 添加对应的扫描路径 --><context:component-scan base-package="com.bobo.controller"/><!-- 开启Spring MVC 注解的使用方式 --><mvc:annotation-driven /><!-- 配置文件上传的解析器 --><bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"id="multipartResolver"><!--通过设值注入的方式设置上传的相关参数 --><property name="maxUploadSize" value="5232880"/></bean><!-- 防止资源文件被Spring MVC拦截。我们在Spring MVC设置资源的映射关系 --><!-- 防止资源文件被spring MVC拦截<mvc:resources mapping="/img/**" location="/img/" cache-period="31556926"/><mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/><mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>--></beans>
1.4 控制器中处理文件
package com.bobo.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;@Controller
@RequestMapping("/user")
public class UserController {/*** 文件上传操作* @return*/@RequestMapping("/fileUpload")public String fileUpload(String username, MultipartFile headFile) throws IOException {System.out.println(username+" : " + headFile.getOriginalFilename() );headFile.transferTo(new File("d:/tools/","123.jpg"));return "/index.jsp";}
}
2.文件下载操作
/*** 文件下载*/@RequestMapping("/download")public void fileDownload(HttpServletRequest request, HttpServletResponse response) throws Exception {File file = new File("d:/tools/","123.jpg");// 设置响应头信息response.setCharacterEncoding("utf-8");response.setContentType("multipart/form-data");response.setHeader("Content-Disposition", "attachment;fileName=" + file.getName());// 打开需要下载的文件InputStream in = new FileInputStream(file);// 激活下载的操作ServletOutputStream outputStream = response.getOutputStream();// 实现下载操作--> 本质就是一个文件的复制操作byte[] b = new byte[1024*1024];int length= 0;while((length = in.read(b)) > 0){outputStream.write(b,0,length);}// 关闭连接outputStream.close();in.close();}
/*** 使用SpringMVC中提供的下载方式* @param request* @return* @throws Exception*/@RequestMapping("/download2")public ResponseEntity<byte[]> download2(HttpServletRequest request) throws Exception{File file = new File("d:/tools/","123.jpg");byte[] body = null;InputStream in = new FileInputStream(file);body = new byte[in.available()];in.read(body);HttpHeaders headers = new HttpHeaders();headers.add("Content-Disposition","attachment;fileName=" + file.getName());HttpStatus status = HttpStatus.OK;ResponseEntity<byte[]> entity = new ResponseEntity<>(body,headers,status);return entity;}
3.静态资源文件处理
<!-- 防止资源文件被Spring MVC的前端控制器拦截 --><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.css</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.js</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.jpg</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.png</url-pattern></servlet-mapping>
<!-- 防止资源文件被Spring MVC拦截。我们在Spring MVC设置资源的映射关系 -->
<!-- 防止资源文件被spring MVC拦截 -->
<mvc:resources mapping="/img/**" location="/img/" cache-period="31556926"/>
<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>
<mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>
4.服务端数据校验
4.1 为什么需要服务端校验?
4.2 普通校验
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.3.0.Alpha1</version>
</dependency>
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 添加对应的扫描路径 --><context:component-scan base-package="com.bobo.controller"/><!-- 开启Spring MVC 注解的使用方式 --><mvc:annotation-driven validator="validatorFactoryBean"/><!-- 配置Hibernate-validator验证框架 --><bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" id="validatorFactoryBean"><property name="providerClass" value="org.hibernate.validator.HibernateValidator"/><property name="validationMessageSource" ref="bundleMessageSource"/></bean><!-- 添加Hibernate验证框架的相关属性信息 --><bean class="org.springframework.context.support.ReloadableResourceBundleMessageSource" id="bundleMessageSource"><property name="fileEncodings" value="utf-8"/><property name="cacheSeconds" value="120" /></bean></beans>
package com.bobo.controller;import com.bobo.bean.UserBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller
@RequestMapping("/user")
public class UserController {/**** @param user @Validated 修饰表示user会被校验* @param br 校验的结果* @return*/@RequestMapping("/addUser")public String addUser(@Validated UserBean user, BindingResult br, Model m) {// 对客户端提交的用户数据检验/*if(user != null){if(user.getUserName().length() > 3 ){}}*/// 获取校验结果List<ObjectError> allErrors = br.getAllErrors();for (ObjectError allError : allErrors) {System.out.println(allError.getDefaultMessage());}m.addAttribute("errors",allErrors);return "/index.jsp";}
}
4.3 分组校验
package com.bobo.controller;import com.bobo.bean.UserBean;
import com.bobo.group.GroupInterface1;
import com.bobo.group.GroupInterface2;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller
@RequestMapping("/user")
public class UserController {/**** @param user @Validated 修饰表示user会被校验* @param br 校验的结果* @return*/@RequestMapping("/addUser")public String addUser(@Validated(value = GroupInterface2.class) UserBean user, BindingResult br, Model m) {// 对客户端提交的用户数据检验/*if(user != null){if(user.getUserName().length() > 3 ){}}*/// 获取校验结果List<ObjectError> allErrors = br.getAllErrors();for (ObjectError allError : allErrors) {System.out.println(allError.getDefaultMessage());}m.addAttribute("errors",allErrors);return "/index.jsp";}@RequestMapping("/udpateUser")public String udpate(@Validated(value = GroupInterface1.class) UserBean user, BindingResult br, Model m){return "/index.jsp";}}
5.数据回写
5.1 普通实现方式
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/doLogin")public String doLogin(String userName, String password, Model model){model.addAttribute("userName",userName);model.addAttribute("password",password);return "forward:/login.jsp";}
}
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/30Time: 14:14To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><h1>登录页面</h1>获取全局配置信息:${as} <br><form action="/user/doLogin" method="post"><label>用户名:</label><input type="text" name="userName" value="${userName}"><br><label>密码:</label><input type="text" name="password" value="${password}"><br><input type="submit" value="提交"></form>
</body>
</html>
5.2 通过Model方式实现
/*** 如果我们用对象去接收请求传递的参数。* 那么该对象默认会被保存到Model对象中* model.addAttribute("userBean",user)* @param user* @return*/@RequestMapping("/addUser")public String addUser(UserBean user) {System.out.println(user);return "/user.jsp";}
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/29Time: 20:03To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
获取全局配置信息:${as} <br><form action="/user/addUser" method="post" ><label>编号:</label><input type="text" name="id" value="${user.id}"><br><label>用户名:</label><input type="text" name="userName" value="${user.userName}"><br><input type="submit" value="添加"></form>
</body>
</html>
5.3 @ModelAttribute注解的实现
/*** 配置全局的信息* 该类中的其他方法在处理请求后都会绑定本方法的返回信息* @return*/@ModelAttribute("as")public List<String> getAllAddress(){return Arrays.asList("深圳","长沙","北京");}
6.异常处理
package com.bobo.resolver;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 全局异常处理器*/
@Component
public class MyExceptionResolver implements HandlerExceptionResolver {/*** 处理异常* @param httpServletRequest* @param httpServletResponse* @param o* @param e* @return*/@Overridepublic ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {System.out.println(e.getMessage());ModelAndView mm = new ModelAndView();mm.setViewName("/500.jsp");return mm;}
}
7.JSON数据操作
7.1 响应JSON数据
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.5.2</version>
</dependency>
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.5.2</version>
</dependency>
@Controller
@RequestMapping("/person")
public class PersonController {@RequestMapping("/getUser")@ResponseBodypublic UserBean getUser(){UserBean user = new UserBean();user.setId(666);user.setUserName("波波");user.setGender("男");user.setAddress("湖南长沙");return user;}
}
@RequestMapping("/getAllUser")@ResponseBodypublic List<UserBean> getAllUser(){UserBean user = new UserBean();user.setId(666);user.setUserName("波波");user.setGender("男");user.setAddress("湖南长沙");UserBean user2 = new UserBean();user2.setId(123);user2.setUserName("波波1");user2.setGender("男1");user2.setAddress("湖南长沙1");List<UserBean> list = new ArrayList<>();list.add(user);list.add(user2);return list;}
通过以上案例可知,我们返回的任意的Java对象数据Jackson都会将其转换为JSON数据。
<mvc:annotation-driven validator="validatorFactoryBean"><mvc:message-converters><beanclass="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"></bean></mvc:message-converters></mvc:annotation-driven>
7.2 接收JSON数据
/*** 客户端发送的是JSON格式的字符串* @param user*/@RequestMapping("/addUser2")@ResponseBodypublic void addUser2(@RequestBody UserBean user){System.out.println(user);}
<%--Created by IntelliJ IDEA.User: dpbDate: 2021/1/30Time: 16:23To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title><script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<input type="button" value="提交JSON数据" onclick="fun1();">
<script type="text/javascript">function fun1(){$.ajax({type: 'POST',url: "person/addUser2",contentType: "application/json",//如果想以json格式把数据提交到后台的话,这个必须有,否则只会当做表单提交data: JSON.stringify({"userName":"bobo","password":"12345"}),//JSON.stringify()必须有,否则只会当做表单的格式提交dataType: "json",//期待返回的数据类型success: function(data){alert("success:"+data);},error:function(data){alert("error"+data);}});}
</script>
</body>
</html>
8、Restful风格
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {String value() default "";
}
package com.bobo.controller;import com.bobo.bean.UserBean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import java.util.ArrayList;
import java.util.List;/*** Restful风格编程介绍*/
/*@ResponseBody
@Controller*/
@RestController
public class StudentController {/*** 查询用户的方法* 基于Restf风格的规范,该请求只能接受GET方式提交的请求* @return*///@RequestMapping("/getAll")@GetMapping("/stus/{id}")//@RequestMapping(value = "/stus",method = RequestMethod.POST)public List<UserBean> getAllUser(@PathVariable Integer id){System.out.println("查询数据--->"+id);List<UserBean> list = new ArrayList<>();list.add(new UserBean(1,"root","123456"));list.add(new UserBean(2,"admin","123456"));return list;}/*** 添加用户数据* 接受POST方式提交* @param user* @return*/@PostMapping("/stus")public String addUser(@RequestBody UserBean user){System.out.println("添加数据"+user);return "数据添加成功...";}@DeleteMapping("/stus/{id}")public String deleteUser(@PathVariable Integer id){System.out.println("删除的编号:" + id);return "删除数据成功...";}@PutMapping("/stus")public String updateUser(@RequestBody UserBean user){System.out.println("更新数据:" + user);return "更新数据成功...";}
}
9、拦截器
9.1 简介
9.2 使用
package com.bobo.interceptor;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 自定义的拦截器*/
public class MyInterceptor implements HandlerInterceptor {/*** 自定义处理器处理请求之前执行的方法* @param httpServletRequest* @param httpServletResponse* @param o* @return* true 表示放过请求* false 表示拦截请求* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {System.out.println("preHandle .... ");return true;}/*** 在目标方法执行完成后执行的方法* postHandle 在afterCompletion之前执行* 返回ModelAndView之前执行* 我们可以修改ModelAndView中的信息* @param httpServletRequest* @param httpServletResponse* @param o* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,ModelAndView modelAndView) throws Exception {System.out.println("ModelAndView执行之前操作...");modelAndView.setViewName("/index.jsp");System.out.println("postHandle ....");}/*** 在目标方法执行完成后执行的方法* 返回ModelAndView之后执行* 改变不了ModelAndView中的信息* 只能做一些资源回收相关的工作* @param httpServletRequest* @param httpServletResponse* @param o* @param e* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {System.out.println("afterCompletion ... ");}
}
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 添加对应的扫描路径 --><context:component-scan base-package="com.bobo.controller,com.bobo.resolver"/><!-- 开启Spring MVC 注解的使用方式 --><mvc:annotation-driven /><!-- 配置自定义的拦截器 --><mvc:interceptors><mvc:interceptor><!-- ;拦截的映射地址 /** 表示拦截根目录及其子目录下的所有的请求 --><mvc:mapping path="/user/**"/><bean class="com.bobo.interceptor.MyInterceptor" /></mvc:interceptor></mvc:interceptors></beans>
四、Spring MVC 整合Spring框架
1.整合实现步骤
1.1 创建Web项目
1.2 添加相关的依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.3.4.RELEASE</version>
</dependency>
<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope>
</dependency>
1.3 添加Spring的配置文件
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd
"><!-- 配置扫描路径 --><context:component-scan base-package="com.bobo.service.impl,com.bobo.dao.impl"use-default-filters="true"><!-- 排除掉Controller注解的使用 --><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan></beans>
1.4 添加Spring MVC的配置文件
<?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:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd
"><!-- 配置扫描路径 --><context:component-scan base-package="com.bobo.controller" use-default-filters="false" ><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!-- 开启注解 --><mvc:annotation-driven ></mvc:annotation-driven>
</beans>
1.5 添加Tomcat的插件
<plugins><!-- tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><!-- 端口号 --><port>8082</port><!-- /表示访问路径 省略项目名 --><path>/</path><!-- 设置编码方式 --><uriEncoding>utf-8</uriEncoding></configuration></plugin></plugins>
1.6 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 version="2.5"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><display-name>Archetype Created Web Application</display-name><!-- 配置Spring --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- 配置Servlet的前端控制器 --><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 关联自定义的Spring MVC的配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- 支持Restful风格编程 --><url-pattern>/</url-pattern></servlet-mapping><!-- 配置字符编码的过滤器 --><!-- 配置设置编码的过滤器 --><filter><filter-name>encodingFilter</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><init-param><param-name>forceRequestEncoding</param-name><param-value>true</param-value></init-param><init-param><param-name>forceResponseEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- default 防止静态资源拦截 --><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.css</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.js</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.jpg</url-pattern></servlet-mapping><servlet-mapping><servlet-name>default</servlet-name><url-pattern>*.png</url-pattern></servlet-mapping>
</web-app>
1.7创建对应的逻辑代码
package com.bobo.bean;public class UserBean {private Integer id;private String userName;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}@Overridepublic String toString() {return "UserBean{" +"id=" + id +", userName='" + userName + '\'' +'}';}public UserBean(Integer id, String userName) {this.id = id;this.userName = userName;}public UserBean() {}
}
package com.bobo.dao;import com.bobo.bean.UserBean;public interface IUserDao {UserBean getUserBean();
}
package com.bobo.dao.impl;import com.bobo.bean.UserBean;
import com.bobo.dao.IUserDao;
import org.springframework.stereotype.Repository;@Repository
public class UserDaoImpl implements IUserDao {@Overridepublic UserBean getUserBean() {return new UserBean(666,"bobo");}
}
package com.bobo.service;import com.bobo.bean.UserBean;public interface IUserService {UserBean getUserBean();
}
package com.bobo.service.impl;import com.bobo.bean.UserBean;
import com.bobo.dao.IUserDao;
import com.bobo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements IUserService {@Autowiredprivate IUserDao dao;@Overridepublic UserBean getUserBean() {return dao.getUserBean();}
}
package com.bobo.controller;import com.bobo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@Autowiredprivate IUserService service;@GetMapping("/user/query")public String query(){return service.getUserBean().toString();}
}
1.8 测试效果
2.Spring IoC 源码浅析
package com.bobo.test;import com.bobo.bean.UserBean;
import com.bobo.service.IUserService;
import com.bobo.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test01 {@Testpublic void fun1(){// 进入源码 Ctrl+鼠标点击 进入ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println(ac.getBean(IUserService.class));}
}
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {this(new String[]{configLocation}, true, (ApplicationContext)null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh,ApplicationContext parent) throws BeansException {// 初始化父类super(parent);// 设置本地配置信息this.setConfigLocations(configLocations);// 完成Spring容器的初始化操作if (refresh) {this.refresh();}
}
public void refresh() throws BeansException, IllegalStateException {synchronized(this.startupShutdownMonitor) {// 准备工作this.prepareRefresh();// 获取BeanFactory工厂对象,并且完成 配置文件的而加重解析操作ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try {this.postProcessBeanFactory(beanFactory);this.invokeBeanFactoryPostProcessors(beanFactory);this.registerBeanPostProcessors(beanFactory);this.initMessageSource();this.initApplicationEventMulticaster();this.onRefresh();this.registerListeners();this.finishBeanFactoryInitialization(beanFactory);this.finishRefresh();} catch (BeansException var9) {if (this.logger.isWarnEnabled()) {this.logger.warn("Exception encountered during contextinitialization - cancelling refresh attempt: " + var9);}this.destroyBeans();this.cancelRefresh(var9);throw var9;} finally {this.resetCommonCaches();}}
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 刷新BeanFactory对象this.refreshBeanFactory();// 获取BeanFactory对象 说明已经完成了 BeanFactory的创建和 配置文件的加载解析操作ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();if (this.logger.isDebugEnabled()) {this.logger.debug("Bean factory for " + this.getDisplayName() + ": " +beanFactory);}return beanFactory;
}
protected final void refreshBeanFactory() throws BeansException {// 判断BeanFactory是否已经存在if (this.hasBeanFactory()) {// 存在 就销毁和关闭this.destroyBeans();this.closeBeanFactory();}try {// 创建BeanFactory对象DefaultListableBeanFactory beanFactory = this.createBeanFactory();beanFactory.setSerializationId(this.getId());this.customizeBeanFactory(beanFactory);// 加载解析配置文件this.loadBeanDefinitions(beanFactory);synchronized(this.beanFactoryMonitor) {this.beanFactory = beanFactory;}} catch (IOException var5) {throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);}
}
protected DefaultListableBeanFactory createBeanFactory() {return new DefaultListableBeanFactory(this.getInternalParentBeanFactory());
}
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException {XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));this.initBeanDefinitionReader(beanDefinitionReader);// 核心代码 直接进入this.loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = this.getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}String[] configLocations = this.getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}
}
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int counter = 0;Resource[] var3 = resources;int var4 = resources.length;for(int var5 = 0; var5 < var4; ++var5) {Resource resource = var3[var5];counter += this.loadBeanDefinitions((Resource)resource);}// 统计解析的配置文件的个数return counter;
}
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return this.loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (this.logger.isInfoEnabled()) {this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());}Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!((Set)currentResources).add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");} else {int var5;try {// 获取配置文件对应的字节输入流InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);// 设置对应的编码方式if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}// 核心代码 执行解析操作var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());} finally {inputStream.close();}} catch (IOException var15) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);} finally {((Set)currentResources).remove(encodedResource);if (((Set)currentResources).isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}return var5;}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 将xml中的内容加载到Document对象中Document doc = this.doLoadDocument(inputSource, resource);// 完成配置文件的注册操作 将配置文件中的信息载入到BeanDefinition对象中return this.registerBeanDefinitions(doc, resource);} catch (BeanDefinitionStoreException var4) {throw var4;} catch (SAXParseException var5) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + var5.getLineNumber() + " in XML document from " + resource + " isinvalid", var5);} catch (SAXException var6) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", var6);} catch (ParserConfigurationException var7) {throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, var7);} catch (IOException var8) {throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, var8);} catch (Throwable var9) {throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, var9);}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();int countBefore = this.getRegistry().getBeanDefinitionCount();// 具体注册的方法documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;this.logger.debug("Loading bean definitions");// 获取Document对象的root标签Element root = doc.getDocumentElement();// 具体操作的方法this.doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent = this.delegate;this.delegate = this.createDelegate(this.getReaderContext(), root, parent);// 对profile标签处理if (this.delegate.isDefaultNamespace(root)) {String profileSpec = root.getAttribute("profile");if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (this.logger.isInfoEnabled()) {this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());}return;}}}// 解析配置文件之前的操作this.preProcessXml(root);// 解析配置文件this.parseBeanDefinitions(root, this.delegate);// 解析配置文件之后的操作this.postProcessXml(root);this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root)) {// 获取根节点下的所有的直接子标签NodeList nl = root.getChildNodes();// 循环获取每一个子标签for(int i = 0; i < nl.getLength(); ++i) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element)node;if (delegate.isDefaultNamespace(ele)) {// 解析默认的标签this.parseDefaultElement(ele, delegate);} else {// 解析自定义的标签delegate.parseCustomElement(ele);}}}} else {delegate.parseCustomElement(root);}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// 配置文件的解析BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// 注册我们获取的 BeanDefinitionHolder 对象BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,this.getReaderContext().getRegistry());} catch (BeanDefinitionStoreException var5) {this.getReaderContext().error("Failed to register bean definitionwith name '" + bdHolder.getBeanName() + "'", ele, var5);}this.getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));}
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele,BeanDefinition containingBean) {// 获取bean标签的id属性String id = ele.getAttribute("id");// 获取bean标签的name属性String nameAttr = ele.getAttribute("name");List<String> aliases = new ArrayList();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",;");aliases.addAll(Arrays.asList(nameArr));}String beanName = id;if (!StringUtils.hasText(id) && !aliases.isEmpty()) {beanName = (String)aliases.remove(0);if (this.logger.isDebugEnabled()) {this.logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {// 检查name是否唯一this.checkNameUniqueness(beanName, aliases, ele);}AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition,this.readerContext.getRegistry(), true);} else {beanName = this.readerContext.generateBeanName(beanDefinition);String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {aliases.add(beanClassName);}}if (this.logger.isDebugEnabled()) {this.logger.debug("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]");}} catch (Exception var9) {this.error(var9.getMessage(), ele);return null;}}String[] aliasesArray = StringUtils.toStringArray(aliases);return new BeanDefinitionHolder(beanDefinition, beanName,aliasesArray);} else {return null;}
}
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {String beanName = definitionHolder.getBeanName();// 关键代码registry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());String[] aliases = definitionHolder.getAliases();if (aliases != null) {String[] var4 = aliases;int var5 = aliases.length;for(int var6 = 0; var6 < var5; ++var6) {String alias = var4[var6];registry.registerAlias(beanName, alias);}}
}
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition)beanDefinition).validate();} catch (BeanDefinitionValidationException var9) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);}}BeanDefinition oldBeanDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {if (!this.isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound.");}if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {if (this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");}} else if (!beanDefinition.equals(oldBeanDefinition)) {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");}} else if (this.logger.isDebugEnabled()) {this.logger.debug("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");}this.beanDefinitionMap.put(beanName, beanDefinition);} else {if (this.hasBeanCreationStarted()) {synchronized(this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;if (this.manualSingletonNames.contains(beanName)) {Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);updatedSingletons.remove(beanName);this.manualSingletonNames = updatedSingletons;}}} else {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);this.manualSingletonNames.remove(beanName);}this.frozenBeanDefinitionNames = null;}if (oldBeanDefinition != null || this.containsSingleton(beanName)) {this.resetBeanDefinition(beanName);}}
}
@Override
public void refresh() throws BeansException, IllegalStateException {//startupShutdownMonitor对象在spring环境刷新和销毁的时候都会用到,确保刷新和销毁不会同时执行synchronized (this.startupShutdownMonitor) {// 准备工作,例如记录事件,设置标志,检查环境变量等,并有留给子类扩展的位置,用来将属性加入到applicationContext中prepareRefresh();// 创建beanFactory,这个对象作为applicationContext的成员变量,可以被applicationContext拿来用,// 并且解析资源(例如xml文件),取得bean的定义,放在beanFactory中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 对beanFactory做一些设置,例如类加载器、SPEL解析器、指定bean的某些类型的成员变量对应某些对象.prepareBeanFactory(beanFactory);try {// 子类扩展用,可以设置bean的后置处理器(bean在实例化之后这些后置处理器会执行)postProcessBeanFactory(beanFactory);// 执行beanFactory后置处理器(有别于bean后置处理器处理bean实例,beanFactory 后置处理器处理bean定义)invokeBeanFactoryPostProcessors(beanFactory);// 将所有的bean的后置处理器排好序,但不会马上用,bean实例化之后会用到registerBeanPostProcessors(beanFactory);// 初始化国际化服务initMessageSource();// 创建事件广播器initApplicationEventMulticaster();// 空方法,留给子类自己实现的,在实例化bean之前做一些ApplicationContext相关的操作onRefresh();// 注册一部分特殊的事件监听器,剩下的只是准备好名字,留待bean实例化完成后再注册registerListeners();// 单例模式的bean的实例化、成员变量注入、初始化等工作都在此完成finishBeanFactoryInitialization(beanFactory);// applicationContext刷新完成后的处理,例如生命周期监听器的回调,广播通知等finishRefresh();}catch (BeansException ex) {logger.warn("Exception encountered during context initialization -cancelling refresh attempt", ex);// 刷新失败后的处理,主要是将一些保存环境信息的集合做清理destroyBeans();// applicationContext是否已经激活的标志,设置为falsecancelRefresh(ex);// Propagate exception to caller.throw ex;}}
}
3. Spring MVC 源码浅析
3.1 分析init方法
public final void init() throws ServletException {if (this.logger.isDebugEnabled()) {this.logger.debug("Initializing servlet '" + this.getServletName() +"'");}try {PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(),this.requiredProperties);BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader,this.getEnvironment()));this.initBeanWrapper(bw);bw.setPropertyValues(pvs, true);} catch (BeansException var4) {this.logger.error("Failed to set bean properties on servlet '" +this.getServletName() + "'", var4);throw var4;}// 关键代码this.initServletBean();if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");}
}
protected final void initServletBean() throws ServletException {this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");if (this.logger.isInfoEnabled()) {this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");}long startTime = System.currentTimeMillis();try {// 初始化 Web容器this.webApplicationContext = this.initWebApplicationContext();// 预留给我们扩展的方法this.initFrameworkServlet();} catch (ServletException var5) {this.logger.error("Context initialization failed", var5);throw var5;} catch (RuntimeException var6) {this.logger.error("Context initialization failed", var6);throw var6;}if (this.logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");}
}
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());WebApplicationContext wac = null;// 如果找到了 rootContextif (this.webApplicationContext != null) {wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac =(ConfigurableWebApplicationContext)wac;if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);}this.configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {// 查找容器wac = this.findWebApplicationContext();}if (wac == null) {// 创建容器wac = this.createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// 刷新容器this.onRefresh(wac);}if (this.publishContext) {String attrName = this.getServletContextAttributeName();this.getServletContext().setAttribute(attrName, wac);if (this.logger.isDebugEnabled()) {this.logger.debug("Published WebApplicationContext of servlet '"+ this.getServletName() + "' as ServletContext attribute with name [" + attrName+ "]");}}
return wac;
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {Class<?> contextClass = this.getContextClass();if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet with name '" + this.getServletName() + "'will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "', using parent context [" + parent + "]");}if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");} else {ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);wac.setEnvironment(this.getEnvironment());wac.setParent(parent);wac.setConfigLocation(this.getContextConfigLocation());// 核心代码this.configureAndRefreshWebApplicationContext(wac);return wac;}
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac){if (ObjectUtils.identityToString(wac).equals(wac.getId())) {if (this.contextId != null) {wac.setId(this.contextId);} else {wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());}}wac.setServletContext(this.getServletContext());wac.setServletConfig(this.getServletConfig());wac.setNamespace(this.getNamespace());wac.addApplicationListener(new SourceFilteringListener(wac, newFrameworkServlet.ContextRefreshListener()));ConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());}this.postProcessWebApplicationContext(wac);this.applyInitializers(wac);// 关键代码 完成SpringMVC配置文件的加载解析操作 Spring容器初始化操作wac.refresh();
}
3.2 分析service方法
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());if (HttpMethod.PATCH != httpMethod && httpMethod != null) {super.service(request, response);} else {// 具体处理请求的方法this.processRequest(request, response);}
}
protected final void processRequest(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = this.buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());this.initContextHolders(request, localeContext, requestAttributes);try {this.doService(request, response);} catch (ServletException var17) {failureCause = var17;throw var17;} catch (IOException var18) {failureCause = var18;throw var18;} catch (Throwable var19) {failureCause = var19;throw new NestedServletException("Request processing failed",var19);} finally {this.resetContextHolders(request, previousLocaleContext,previousAttributes);if (requestAttributes != null) {requestAttributes.requestCompleted();}if (this.logger.isDebugEnabled()) {if (failureCause != null) {this.logger.debug("Could not complete request",(Throwable)failureCause);} else if (asyncManager.isConcurrentHandlingStarted()) {this.logger.debug("Leaving response open for concurrent processing");} else {this.logger.debug("Successfully completed request");}}this.publishRequestHandledEvent(request, response, startTime,(Throwable)failureCause);}
}
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {if (this.logger.isDebugEnabled()) {String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");}Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap();Enumeration attrNames = request.getAttributeNames();label108:while(true) {String attrName;do {if (!attrNames.hasMoreElements()) {break label108;}attrName = (String)attrNames.nextElement();} while(!this.cleanupAfterInclude &&!attrName.startsWith("org.springframework.web.servlet"));attributesSnapshot.put(attrName,request.getAttribute(attrName));}}// 将Web容器保存到了 Request请求中request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) {request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);try {// 处理请求分发this.doDispatch(request, response);} finally {if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() &&
attributesSnapshot != null) {this.restoreAttributesAfterInclude(request, attributesSnapshot);}}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Object dispatchException = null;try {// 检查是否有文件上传processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {this.noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request,mappedHandler.getHandler());if (this.logger.isDebugEnabled()) {this.logger.debug("Last-Modified value for [" +getRequestUri(request) + "] is: " + lastModified);}if ((new ServletWebRequest(request,response)).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest,response)) {return;}mv = ha.handle(processedRequest, response,mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response,mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handler dispatch failed", var21);}this.processDispatchResult(processedRequest, response,mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response,mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response,mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}
}
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {this.logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, this typically results from an additional MultipartFilter in web.xml");} else {if (!(request.getAttribute("javax.servlet.error.exception")instanceof MultipartException)) {// 文件上传操作的具体执行return this.multipartResolver.resolveMultipart(request);}this.logger.debug("Multipart resolution failed for current request before - skipping re-resolution for undisturbed error rendering");}}return request;
}
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {Assert.notNull(request, "Request must not be null");// 是否需要延迟 处理if (this.resolveLazily) {return new DefaultMultipartHttpServletRequest(request) {protected void initializeMultipart() {MultipartParsingResult parsingResult = CommonsMultipartResolver.this.parseRequest(request);this.setMultipartFiles(parsingResult.getMultipartFiles());this.setMultipartParameters(parsingResult.getMultipartParameters());this.setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());}};} else {// 解析请求MultipartParsingResult parsingResult = this.parseRequest(request);return new DefaultMultipartHttpServletRequest(request,parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters(),parsingResult.getMultipartParameterContentTypes());}
}
protected MultipartParsingResult parseRequest(HttpServletRequest request)throws MultipartException {String encoding = this.determineEncoding(request);FileUpload fileUpload = this.prepareFileUpload(encoding);try {// 获取提交的表单中的所有的表单域List<FileItem> fileItems = ((ServletFileUpload)fileUpload).parseRequest(request);// 解析每一个表单域return this.parseFileItems(fileItems, encoding);} catch (SizeLimitExceededException var5) {throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(),var5);} catch (FileSizeLimitExceededException var6) {throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), var6);} catch (FileUploadException var7) {throw new MultipartException("Failed to parse multipart servlet request", var7);}
}
protected CommonsFileUploadSupport.MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap();Map<String, String[]> multipartParameters = new HashMap();Map<String, String> multipartParameterContentTypes = new HashMap();Iterator var6 = fileItems.iterator();while(true) {while(var6.hasNext()) {FileItem fileItem = (FileItem)var6.next();// 普通表单域处理if (fileItem.isFormField()) {String partEncoding = this.determineEncoding(fileItem.getContentType(), encoding);String value;if (partEncoding != null) {try {// 获取对应的编码方式value = fileItem.getString(partEncoding);} catch (UnsupportedEncodingException var12) {if (this.logger.isWarnEnabled()) {this.logger.warn("Could not decode multipartitem '" + fileItem.getFieldName() + "' with encoding '" + partEncoding + "': using platform default");}value = fileItem.getString();}} else {// 得到提交的值value = fileItem.getString();}String[] curParam = (String[])multipartParameters.get(fileItem.getFieldName());if (curParam == null) {// 将提交的数据保存起来multipartParameters.put(fileItem.getFieldName(), new String[]{value});} else {String[] newParam = StringUtils.addStringToArray(curParam, value);multipartParameters.put(fileItem.getFieldName(),newParam);}multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());} else {// 表单提交的文件信息CommonsMultipartFile file = new CommonsMultipartFile(fileItem);// 将表单提交的文件信息 封装到了 CommonsMultipartFile 对象中multipartFiles.add(file.getName(), file);if (this.logger.isDebugEnabled()) {this.logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() + " bytes with original filename [" + file.getOriginalFilename() + "], stored " + file.getStorageDescription());}}}return new CommonsFileUploadSupport.MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Object dispatchException = null;try {// 检查是否有文件上传processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;// 处理器映射器mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {this.noHandlerFound(processedRequest, response);return;}// 获取处理器适配器HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request,mappedHandler.getHandler());if (this.logger.isDebugEnabled()) {this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest,response)) {return;}// 处理适配器处理请求mv = ha.handle(processedRequest, response,mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response,mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handlerdispatch failed", var21);}this.processDispatchResult(processedRequest, response,mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return ((Controller)handler).handleRequest(request, response);
}
4. Spring MVC和Spring IoC的关联关系
1. 为什么不全部都在Spring中扫描
2. 为什么不全部在SpringMVC中扫描
<!-- 配置Spring -->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener><listenerclass>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
public void contextInitialized(ServletContextEvent event) {// 初始化web容器this.initWebApplicationContext(event.getServletContext());
}
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");} else {Log logger = LogFactory.getLog(ContextLoader.class);servletContext.log("Initializing Spring root WebApplicationContext");if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");}long startTime = System.currentTimeMillis();try {if (this.context == null) {// 创建容器对象this.context = this.createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;if (!cwac.isActive()) {if (cwac.getParent() == null) {ApplicationContext parent = this.loadParentContext(servletContext);cwac.setParent(parent);}// 完全Spring IoC容器的初始化操作this.configureAndRefreshWebApplicationContext(cwac, servletContext);}}// 将Spring的IoC容器对象保存在了Servlet容器中 key是ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);ClassLoader ccl = Thread.currentThread().getContextClassLoader();if (ccl == ContextLoader.class.getClassLoader()) {currentContext = this.context;} else if (ccl != null) {currentContextPerThread.put(ccl, this.context);}if (logger.isDebugEnabled()) {logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");}if (logger.isInfoEnabled()) {long elapsedTime = System.currentTimeMillis() - startTime;logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");}return this.context;} catch (RuntimeException var8) {logger.error("Context initialization failed", var8);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);throw var8;} catch (Error var9) {logger.error("Context initialization failed", var9);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);throw var9;}
}
protected WebApplicationContext initWebApplicationContext() {// 获取Spring IoC容器对象WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);}this.configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {wac = this.findWebApplicationContext();}if (wac == null) {wac = this.createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {this.onRefresh(wac);}if (this.publishContext) {String attrName = this.getServletContextAttributeName();this.getServletContext().setAttribute(attrName, wac);if (this.logger.isDebugEnabled()) {this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");}}return wac;
}
public static WebApplicationContext getWebApplicationContext(ServletContext sc){return getWebApplicationContext(sc,WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {Class<?> contextClass = this.getContextClass();if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet with name '" + this.getServletName() + "'will try to create custom WebApplicationContext context of class '" +contextClass.getName() + "', using parent context [" + parent + "]");}if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");} else {ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);wac.setEnvironment(this.getEnvironment());// Spring MVC的IoC容器对象设置Spring 的IoC容器对象为父容器wac.setParent(parent);wac.setConfigLocation(this.getContextConfigLocation());this.configureAndRefreshWebApplicationContext(wac);return wac;}
}
五、Spring和SpringMVC总结
相关文章:

Java经典框架之Spring MVC
Spring MVC Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. Spring MVC 入门案例 2. 基…...

Golang make vs new
文章目录 1.简介2.区别3.new 可以初始化 slice,map 和 channel 吗?4.make 可以初始化其他类型吗?5.小结参考文献 1.简介 在 Go 语言中,make 和 new 是两个用于创建对象的内建函数,但它们有着不同的用途和适用范围。 …...

Arthas
概述 Arthas(阿尔萨斯) 能为你做什么? Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar 包加载的?为什么会…...

IP代理科普| 共享IP还是独享IP?两者的区别与优势
通俗地讲,共享IP就像乘坐公共汽车一样,您可以到达目的地,但将与其他乘客共享旅程,座位很可能是没有的。独享IP就像坐出租车一样,您可以更快到达目的地,由于车上只有您一个人,座位是您一个人专用…...

龙芯loongarch64服务器编译安装tensorflow-io-gcs-filesystem
前言 安装TensorFlow的时候,会出现有些包找不到的情况,直接使用pip命令也无法安装,比如tensorflow-io-gcs-filesystem,安装的时候就会报错: 这个包需要自行编译,官方介绍有限,这里我讲解下 编译 准备 拉取源码:https://github.com/tensorflow/io.git 文章中…...

开源持续测试平台Linux MeterSphere本地部署与远程访问
文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…...

Kubernetes(K8S)快速入门
概述 在本门课程中,我们将会学习K8S一些非常重要和核心概念,已经操作这些核心概念对应组件的相关命令和方式。比如Deploy部署,Pod容器,调度器,Service服务,Node集群节点,Helm包管理器等等。 在…...

将遗留系统分解为微服务:第 2 部分
在当今不断发展的技术环境中,从整体架构向微服务的转变对于许多企业来说都是一项战略举措。这在报销计算系统领域尤其重要。正如我在上一篇文章第 1 部分应用 Strangler 模式将遗留系统分解为微服务-CSDN博客中提到的,让我们探讨如何有效管理这种转变。 …...

RK3588平台开发系列讲解(AI 篇)RKNN-Toolkit2 模型的加载转换
文章目录 一、Caffe 模型加载接口二、TensorFlow 模型加载接口三、TensorFlowLite 模型加载接口四、ONNX 模型加载五、DarkNet 模型加载接口六、PyTorch 模型加载接口沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 RKNN-Toolkit2 目前支持 Caffe、TensorFlow、Tensor…...

CNVD原创漏洞审核和处理流程
一、CNVD原创漏洞审核归档和发布主流程 (一)审核和归档流程 审核流程分为一级、二级、三级审核,其中一级审核主要对提交的漏洞信息完整性进行审核,漏洞符合可验证(通用型漏洞有验证代码信息或多个互联网实例、事件型…...

【java爬虫】基于springboot+jdbcTemplate+sqlite+OkHttp获取个股的详细数据
注:本文所用技术栈为:springbootjdbcTemplatesqliteOkHttp 前面的文章我们获取过沪深300指数的成分股所属行业以及权重数据,本文我们来获取个股的详细数据。 我们的数据源是某狐财经,接口的详细信息在下面的文章中,本…...

智能优化算法应用:基于人工兔算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于人工兔算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于人工兔算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工兔算法4.实验参数设定5.算法结果6.参考文…...

【ubuntu 22.04】安装vscode并配置正常访问应用商店
注意:要去vscode官网下载deb安装包,在软件商店下载的版本不支持输入中文 在ubuntu下用火狐浏览器无法访问vscode官网,此时可以手动进行DNS解析,打开DNS在线查询工具,解析以下主机地址(复制最后一个IP地址&a…...

K8s出现问题时,如何排查解决!
K8s问题的排查 1. POD启动异常、部分节点无法启动pod2. 审视集群状态3. 追踪事件日志4. 聚焦Pod状态5. 检查网络连通性6. 审视存储配置7. 研究容器日志8. K8S集群网络通信9. 问题:Service 是否通过 DNS 工作?10. 总结1、POD启动异常、部分节点无法启动p…...

2015年第四届数学建模国际赛小美赛B题南极洲的平均温度解题全过程文档及程序
2015年第四届数学建模国际赛小美赛 B题 南极洲的平均温度 原题再现: 地表平均温度是反映气候变化和全球变暖的重要指标。然而,在以前的估计中,在如何界定土地平均数方面存在一些方法上的差异。为简单起见,我们只考虑南极洲。请建…...

npm常见错误
三个方面 1. npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! phantomjs-prebuilt2.1.15 install: node install.js npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the phantomjs-prebuilt2.1.15 install script. np…...

JVM入门到入土-Java虚拟机寄存器指令集与栈指令集
JVM入门到入土-Java虚拟机寄存器指令集与栈指令集 HotSpot虚拟机中的任何操作都需要入栈和出栈的步骤。 由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小&#x…...

MS2244模拟开关可Pin to Pin兼容NJM2244
MS2244 是一款集成的视频开关,实现三输入视频或音频信号的三选一。可Pin to Pin兼容NJM2244。 芯片集成了 75Ω驱动电路,可以直接驱动电视监控器。芯片工作电压 5V~12V,带宽 10MHz,抗串扰 70dB (4.43MHz)。另外芯片还集…...

PostgreSQL 可观测性最佳实践
简介 软件简述 PostgreSQL 是一种开源的关系型数据库管理系统 (RDBMS),它提供了许多可观测性选项,以确保数据库的稳定性和可靠性。 可观测性 可观测性(Observability)是指对数据库状态和操作进行监控和记录,以便在…...

51单片机相关寄存器
前言 单片机复习的时候对应寄存器的记忆感觉很混乱,这里进行一下整理,后面的单词是我用来辅助记忆的,可能并不是表示原本的含义。 P3口的第二功能 0RXD 串行数据输入口 1TXD串行数据输出口2INT0外部中断0输入3INT1外部中断1输入4T0定时器0外部计数输入…...

二叉树进阶题目(超详解)
文章目录 前言根据二叉树创建字符串题目分析写代码 二叉树的层序遍历题目分析 写代码二叉树的层序遍历II题目分析写代码 二叉树的最近公共祖先题目分析写代码时间复杂度 优化思路优化的代码 二叉搜索树与双向链表题目分析写代码 从前序与中序遍历序列构造二叉树题目分析写代码从…...

W6100-EVB-Pico评估版介绍
文章目录 1 简介2 硬件资源2.1 硬件规格2.2 引脚定义2.3 工作条件 3 参考资料3.1 Datasheet3.2 原理图3.3 尺寸图(尺寸:mm)3.4 参考例程 4 硬件协议栈优势 1 简介 W6100-EVB-Pico是一款基于树莓派RP2040和全硬件TCP/IP协议栈以太网芯片W6100的…...

嵌入式面试准备
题目都摘于网上 嵌入式系统中经常要用到无限循环,如何用C编写死循环 while(1){}或者for(;😉 内存分区 代码区,全局区(全局变量,静态变量,以及常量),栈区,堆区 const关键…...

在Linux Docker中部署RStudio Server,实现高效远程访问
🌈个人主页:聆风吟 🔥系列专栏:网络奇遇记、Cpolar杂谈 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 📋前言一. 安装RStudio Server二. 本地访问三. Linux 安装cpolar四. 配置RStudio serv…...

EternalBlue【永恒之蓝】漏洞详解(复现、演示、远程、后门、入侵、防御)内容丰富-深入剖析漏洞原理-漏洞成因-以及报错解决方法-值得收藏!
漏洞背景: 1.何为永恒之蓝? 永恒之蓝(Eternal Blue)爆发于2017年4月14日晚,是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限,以此来控制被入侵的计算机。甚至于2017年5月12日, 不法分子…...

长链接与在线文件
什么是在线文件 常见的聊天工具,比如。。。微信,你可以发送一个文件给对端,即使对端不在线,这个文件也可以暂存在服务器上面,直到接收端上线消费或者超时,这个叫离线文件。与之对应的,在线文件要…...

Python内置数据类型等入门语(句)法
内置数据类型 数字(Number)关键字: int 、float、complex字符串(String)关键字:单引号,双引号 三引号都可以表示,8 种内置类型都可转为字符串类型列表(List) 关键符号 […...

ElasticSearch之RestClient笔记
1. ElasticSearch 1.1 倒排索引 1.2 ElasticSearch和Mysql对比 1.3 RestClient操作 导入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.15.…...

饥荒Mod 开发(二二):显示物品信息
饥荒Mod 开发(二一):超大便携背包,超大物品栏,永久保鲜 饥荒Mod 开发(二三):显示物品栏详细信息 饥荒中的物品没有详细信息,基本上只有一个名字,所以很多物品的功能都不知道,比如浆果吃了也不知…...

Microsoft Edge使用方法和心得
Microsoft Edge使用方法和心得 大家好,我是豪哥,一名来自杭州的Java程序员,今天我想分享一下我对Microsoft Edge的使用方法和心得。作为一名热爱编程的程序员,我发现一个高效的浏览器对于我们的工作和学习至关重要。而Microsoft …...