1、MVC常用
web.xml:
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!--1.注册DispatcherServlet--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc-servlet.xml</param-value></init-param><!--启动级别-1--><load-on-startup>1</load-on-startup></servlet><!--/ 匹配所有的请求;(不包括.jsp)--><!--/* 匹配所有的请求;(包括.jsp)--><servlet-mapping><servlet-name>springmvc</servlet-name><url-pattern>/</url-pattern></servlet-mapping><filter><filter-name>encoding</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><!--解决乱码--><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
/ 和 / 的区别:< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。< url-pattern > / </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错
springmvc-servlet.xml:
在resources中配置,SpringMVC的核心配置文件
<?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" xmlns:mv="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/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvchttps://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 --><context:component-scan base-package="com.qing.controller"/><!-- 让Spring MVC不处理静态资源 .css .js .html等--><mvc:default-servlet-handler/><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/"/><!-- 后缀 --><property name="suffix" value=".jsp"/></bean><!--Json乱码问题--><mvc:annotation-driven><mvc:message-converters register-defaults="true"><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"><property name="failOnEmptyBeans" value="false"/></bean></property></bean></mvc:message-converters></mvc:annotation-driven><!-- 拦截器配置--><mv:interceptors><mvc:interceptor><mvc:mapping path="/**"/><bean class="com.qing.interceptor.MtInterceptor"/></mvc:interceptor></mv:interceptors></beans>
第23行的路径一定要对应WEB-INF下的jsp
常用的jar包
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.0</version></dependency><dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.6</version></dependency>
2、回顾servlet
1.1 创建web项目
1.2 导包
<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.0</version><scope>provided</scope></dependency>
1.3 配置servlet
public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1 获取前端参数String method=req.getParameter("method");if(method.equals("add")){req.getSession().setAttribute("msg","执行了add方法");}if (method.equals("delete")){req.getSession().setAttribute("msg","执行了delete方法");}//2 调用业务层//3 视图转发或重定向req.getRequestDispatcher("/jsp/test.jsp").forward(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}}
1.4 配置web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><servlet><servlet-name>hello</servlet-name><servlet-class>com.qing.servlet.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>hello</servlet-name><url-pattern>/hello</url-pattern></servlet-mapping><welcome-file-list><welcome-file>form.jsp</welcome-file></welcome-file-list></web-app>
3、第一个SpringMVC程序
创建项目,添加web框架支持
编写web.xml配置文件
编写springmvc-servlet.xml配置文件 ```xml <?xml version=”1.0” encoding=”UTF-8”?>
<!--Handler--><bean id="/hello" class="com.qing.controller.HelloController"/>
-编写一个hello.jsp页面-编写HelloController控制```javaimport org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.mvc.Controller;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;//注意:这里我们先导入Controller接口public class HelloController implements Controller {public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {//ModelAndView 模型和视图ModelAndView mv = new ModelAndView();//业务代码String result = "HelloSpringMVC";//封装对象,放在ModelAndView中 ,Modelmv.addObject("msg", result);//封装要跳转的视图,放在ModelAndView中mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jspreturn mv;}}
出现问题:
如果出现了404页面错误,在代码逻辑没有出错的情况下,检查Artifacts下有没有lib依赖,若没有,则需添加
4、使用注解开发
创建项目,添加web框架支持
编写web.xml配置文件
编写springmvc-servlet.xml配置文件 ```xml <?xml version=”1.0” encoding=”UTF-8”?>
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 --><context:component-scan base-package="com.qing.controller"/><!-- 让Spring MVC不处理静态资源 .css .js .html等--><mvc:default-servlet-handler /><!--支持mvc注解驱动在spring中一般采用@RequestMapping注解来完成映射关系要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例这两个实例分别在类级别和方法级别处理。而annotation-driven配置帮助我们自动完成上述两个实例的注入。--><mvc:annotation-driven /><!-- 视图解析器 --><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"id="internalResourceViewResolver"><!-- 前缀 --><property name="prefix" value="/WEB-INF/jsp/" /><!-- 后缀 --><property name="suffix" value=".jsp" /></bean>
-编写一个hello.jsp页面-编写HelloController控制```javaimport org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;//在类上添加@controller注解后,这个类才会被视图解析器解析@Controllerpublic class HelloController {@RequestMapping("/hello") //网页请求http://localhost:8080/SpringMVC_03_annotation_war_exploded/hello时public String hello(Model model){//封装数据model.addAttribute("msg","Hello,SpringMVC!");return "hello"; //会被视图解析器处理 经过封装后显示对应的/WEB-INF/jsp/hello.jsp页面}}
5、RestFul风格
import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.*;@Controllerpublic class RestFulController {//原来的路径请求为:http://localhost:8080/SpringMVC_03/add?a=1&b=2//使用restful风格的路径请求为:http://localhost:8080/SpringMVC_03/add/1/2 (常用)//可以自定义请求方式 get、post等// @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.GET)@RequestMapping("/add/{a}/{b}")// @GetMapping("/add/{a}/{b}")// @PostMapping("/add/{a}/{b}")public String add(@PathVariable int a, @PathVariable int b, Model model) {//@PathVariable 代表参数为路径参数int res = a + b;model.addAttribute("msg", "结果为" + res);return "add";}}
RestFul风格使用安全,他人分辨不出参数的意义;而使用原始传参,add?a=1&b=2就可以清楚的分辨
6、重定向与转发
区别:
转发是服务器行为,只需一次跳转,用户看不到url地址上的变化。重定向是游览器端的行为,需要进行二次跳转,用户能看到url地址上的变化。
转发的速度比重定向的速度相对要高,因为转发只需要做一次请求,而重定向需要做二次请求。
转发能够获取request作用域中的数据,他只发出了一次请求,而重定向无法获取request中的数据 ,因为他发出了二次请求,每一次请求对应着一次新的request
转发只能访问当前服务器中的数据,而重定向可以访问任意服务器中的数据
转发不能解决数据的重复提交问题(就是能按F5进行刷新操作),而重定向能解决数据的重复提交。
语句:
- 原生转发:request.getRequestDispatcher(“路径”).forward(request, response);
- 原生重定向:response.sendRedirect(“路径”);
- SpringMvc转发:forward:/路径
- SpringMvc重定向:redirect:/路径
测试:
@Controllerpublic class ViewModel {@RequestMapping("/v")public String view1(Model model){model.addAttribute("msg", "ViewModel");// return "forward:/index.jsp"; //转发return "redirect:/toIndex"; //重定向 走对应的toIndex请求,再从toIndex请求中二次跳转到index.jsp页面}@RequestMapping("/toIndex")public String view2(Model model){model.addAttribute("msg", "ViewModel");return "index"; //二次跳转至index.jsp页面}}
7、前端接收数据以及回显
@Controller@RequestMapping("/user")public class UserController {//http://localhost:8080/SpringMVC_03/user/u1?&username=qing@RequestMapping("u1")public String test1(@RequestParam("username") String name, Model model) {//@RequestParam代表了前端传参的参数名,固定的//接收前端参数System.out.println("前端参数为:" + name);//将结果返回前端model.addAttribute("msg",name);//视图跳转return "hello";}//前端接收的是一个对象时:id name age//http://localhost:8080/SpringMVC_03/user/u2?id=1&name=qing&age=18@RequestMapping("u2")public String test2(User user,Model model){System.out.println(user.toString());model.addAttribute("msg",user);return "hello";}}
8、解决乱码
方法一:自定义过滤器
编写EncodingFilter类:
import javax.servlet.*;import java.io.IOException;public class EncodingFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");chain.doFilter(request,response);}@Overridepublic void destroy() {}}
配置web.xml:
<filter><filter-name>encoding</filter-name><filter-class>com.qing.filter.EncodingFilter</filter-class></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>
**注:这里第8行必须使用 / 才能使过滤器生效,过滤到所有的jsp页面
方式二:spring自带过滤器(建议使用)
配置web.xml:
<filter><filter-name>encoding</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>utf-8</param-value></init-param></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>
方式三:万能的过滤器(大佬编写)
自定义类:
/*** 解决get和post请求 全部乱码的过滤器*/public class GenericEncodingFilter implements Filter {@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {//处理response的字符编码HttpServletResponse myResponse=(HttpServletResponse) response;myResponse.setContentType("text/html;charset=UTF-8");// 转型为与协议相关对象HttpServletRequest httpServletRequest = (HttpServletRequest) request;// 对request包装增强HttpServletRequest myrequest = new MyRequest(httpServletRequest);chain.doFilter(myrequest, response);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}}//自定义request对象,HttpServletRequest的包装类class MyRequest extends HttpServletRequestWrapper {private HttpServletRequest request;//是否编码的标记private boolean hasEncode;//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰public MyRequest(HttpServletRequest request) {super(request);// super必须写this.request = request;}// 对需要增强方法 进行覆盖@Overridepublic Map getParameterMap() {// 先获得请求方式String method = request.getMethod();if (method.equalsIgnoreCase("post")) {// post请求try {// 处理post乱码request.setCharacterEncoding("utf-8");return request.getParameterMap();} catch (UnsupportedEncodingException e) {e.printStackTrace();}} else if (method.equalsIgnoreCase("get")) {// get请求Map<String, String[]> parameterMap = request.getParameterMap();if (!hasEncode) { // 确保get手动编码逻辑只运行一次for (String parameterName : parameterMap.keySet()) {String[] values = parameterMap.get(parameterName);if (values != null) {for (int i = 0; i < values.length; i++) {try {// 处理get乱码values[i] = new String(values[i].getBytes("ISO-8859-1"), "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}}hasEncode = true;}return parameterMap;}return super.getParameterMap();}//取一个值@Overridepublic String getParameter(String name) {Map<String, String[]> parameterMap = getParameterMap();String[] values = parameterMap.get(name);if (values == null) {return null;}return values[0]; // 取回参数的第一个值}//取所有值@Overridepublic String[] getParameterValues(String name) {Map<String, String[]> parameterMap = getParameterMap();String[] values = parameterMap.get(name);return values;}}
配置web.xml:
<filter><filter-name>encoding</filter-name><filter-class>com.qing.filter.GenericEncodingFilter</filter-class></filter><filter-mapping><filter-name>encoding</filter-name><url-pattern>/*</url-pattern></filter-mapping>
9、Jackson(推荐)
导包:
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.12.3</version></dependency>
9.1 格式转换
<script type="text/javascript">//编写一个JavaScript对象var user={name:"卿帆",age:20,sex:"男"}//控制台输出console.log(user);//将js对象、数组转换为字符串var obj=JSON.stringify(user);console.log(obj);//将字符串转换为json对象var json=JSON.parse(obj);console.log(json)</script>
9.2 后端传送数据给前端
测试单个对象:
// @RestController@Controllerpublic class UserController {@RequestMapping("/j1")@ResponseBody //添加此注解后不会走视图解析器,直接返回一个字符串 配合@controller使用;也可以使用@RestController直接返回字符串public String json1() throws JsonProcessingException {ObjectMapper mapper=new ObjectMapper();User user=new User("qingfan",20,"男");String string = mapper.writeValueAsString(user); //生成json字符串return string;}}
测试集合:
@RequestMapping("/j2")@ResponseBody //添加此注解后不会走视图解析器,直接返回一个字符串public String json2() throws JsonProcessingException {ObjectMapper mapper = new ObjectMapper();User user1 = new User("卿", 20, "男");User user2 = new User("陈", 20, "女");User user3 = new User("帆", 20, "男");User user4 = new User("莹", 20, "女");List<User> users = new ArrayList<>();users.add(user1);users.add(user2);users.add(user3);users.add(user4);String string = mapper.writeValueAsString(users); //生成json字符串return string;}
测试时间:
@RequestMapping("/j3")@ResponseBody //添加此注解后不会走视图解析器,直接返回一个字符串public String json3() throws JsonProcessingException {ObjectMapper mapper = new ObjectMapper();/*//默认使用时间戳//自定义时间格式SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date data = new Date();String string = mapper.writeValueAsString(sf.format(date););return string;*///不使用时间戳的方式格式化时间mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);//自定义时间格式SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");mapper.setDateFormat(sf);Date data = new Date();String string = mapper.writeValueAsString(data);return string;}
9.3 前端出现乱码
解决方式一:
将@RequestMapping("/j1")更改为:@RequestMapping(value = "/j1",produces = "application/json;charset=utf-8")
方式二:在spring配置文件中配置
<!--Json乱码问题--><mvc:annotation-driven><mvc:message-converters register-defaults="true"><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean><bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"><property name="objectMapper"><bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"><property name="failOnEmptyBeans" value="false"/></bean></property></bean></mvc:message-converters></mvc:annotation-driven>
9.4 抽象JsonUtils工具类
import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializationFeature;import java.text.SimpleDateFormat;public class JsonUtils {public static String getJson(Object object) {return getJson(object, "yyyy-MM-dd HH:mm:ss");}public static String getJson(Object object, String dateFormat) {ObjectMapper mapper = new ObjectMapper();//不使用时间戳的方式格式化时间mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);//自定义时间格式SimpleDateFormat sf = new SimpleDateFormat(dateFormat);mapper.setDateFormat(sf);try {return mapper.writeValueAsString(object);} catch (JsonProcessingException e) {e.printStackTrace();}return null;}}
10、Fastjson
导包:
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency>
测试:
@RequestMapping("/j3")@ResponseBodypublic String json3() {User user1 = new User("卿", 20, "男");User user2 = new User("陈", 20, "女");User user3 = new User("帆", 20, "男");User user4 = new User("莹宝", 20, "女");List<User> users = new ArrayList<>();users.add(user1);users.add(user2);users.add(user3);users.add(user4);return JSON.toJSONString(users);}
11、拦截器
自定义拦截器类:
import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class MtInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession();//判断登录情况if(request.getRequestURI().contains("login")){return true;}if(session.getAttribute("userLoginInfo")!=null){return true;}//没有登录时 跳转到登录页面request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);return false;}//下面2个方法可以省略@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("=======处理后=======");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("=======清理=======");}}
配置Spring配置文件
<!-- 拦截器配置--><mv:interceptors><mvc:interceptor><!-- /** 是拦截根目录下的所有请求--><mvc:mapping path="/**"/><bean class="com.qing.interceptor.MtInterceptor"/></mvc:interceptor></mv:interceptors>
12、文件上传和下载
12.1 上传
导包:
<!--文件上传需要导入的jar包--><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version></dependency><!--servlet必须使用高版本的javax.servlet-api--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version></dependency></dependencies>
编写Spring配置文件:
<!--文件上传配置--><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --><property name="defaultEncoding" value="utf-8"/><!-- 上传文件大小上限,单位为字节(10485760=10M) --><property name="maxUploadSize" value="10485760"/><property name="maxInMemorySize" value="40960"/></bean>
编写FileController:
@RestControllerpublic class FileController {//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象//批量上传CommonsMultipartFile则为数组即可@RequestMapping("/upload")public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {//获取文件名 : file.getOriginalFilename();String uploadFileName = file.getOriginalFilename();//如果文件名为空,直接回到首页!if ("".equals(uploadFileName)) {return "redirect:/index.jsp";}System.out.println("上传文件名 : " + uploadFileName);//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");//如果路径不存在,创建一个File realPath = new File(path);if (!realPath.exists()) {realPath.mkdir();}System.out.println("上传文件保存地址:" + realPath);InputStream is = file.getInputStream(); //文件输入流OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); //文件输出流//读取写出int len = 0;byte[] buffer = new byte[1024];while ((len = is.read(buffer)) != -1) {os.write(buffer, 0, len);os.flush();}os.close();is.close();return "redirect:/index.jsp";}/** 方式二 采用file.Transto 来保存上传的文件*/@RequestMapping("/upload2")public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");File realPath = new File(path);if (!realPath.exists()){realPath.mkdir();}//上传文件地址System.out.println("上传文件保存地址:"+realPath);//通过CommonsMultipartFile的方法直接写文件(注意这个时候)file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));return "redirect:/index.jsp";}}
12.2 下载
@RequestMapping("/download")@ResponseBodypublic String downloads(HttpServletResponse response , HttpServletRequest request) throws Exception{System.out.println("downloads():");//要下载的图片地址String path = request.getServletContext().getRealPath("/upload");String fileName = "1.jpg";//1、设置response 响应头response.reset(); //设置页面不缓存,清空bufferresponse.setCharacterEncoding("UTF-8"); //字符编码response.setContentType("multipart/form-data"); //二进制传输数据//设置响应头response.setHeader("Content-Disposition","attachment;fileName="+ URLEncoder.encode(fileName, "UTF-8"));File file = new File(path,fileName);//2、 读取文件--输入流InputStream input=new FileInputStream(file);//3、 写出文件--输出流OutputStream out = response.getOutputStream();byte[] buff =new byte[1024];int index=0;//4、执行 写出操作while((index= input.read(buff))!= -1){out.write(buff, 0, index);out.flush();}out.close();input.close();return "ok";}
13、SpringMvc扩展(面试)
13.1 MVC工作流程
流程详解:
- 用户发送出请求到前端控制器DispatcherServlet。
- DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
- HandlerMapping找到具体的Handler(可查找xml配置或注解配置),生成处理器对象的执行链(如果有),再一起返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter(处理器适配器)。
- HandlerAdapter经过适配调用具体的处理器(controller)。
- Controller执行完成返回ModelAndView对象。
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
- ViewReslover解析后返回具体View(视图)。
- DispatcherServlet根据View进行渲染视图(即将数据填充至视图中)。
- DispatcherServlet响应用户。
设计组件分析:
1、前端控制器DispatcherServlet(不需要程序员开发),由框架提供,在web.xml中配置。
作用:接收请求,响应结果,相当于转发器,中央处理器。
2、处理器映射器HandlerMapping(不需要程序员开发),由框架提供。
作用:根据请求的url查找Handler(处理器/Controller),可以通过XML和注解方式来映射。
3、处理器适配器HandlerAdapter(不需要程序员开发),由框架提供。
作用:按照特定规则(HandlerAdapter要求的规则)去执行Controller。
4、控制器Controller(需要工程师开发)
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
作用:接受用户请求信息,调用业务方法处理请求,也称之为后端控制器。
5、视图解析器ViewResolver(不需要程序员开发),由框架提供
作用:进行视图解析,把逻辑视图名解析成真正的物理视图。
SpringMVC框架支持多种View视图技术,包括:jstlView、freemarkerView、pdfView等。
6、视图View(需要工程师开发)
作用:把数据展现给用户的页面
View是一个接口,实现类支持不同的View技术(jsp、freemarker、pdf等)
13.2 MVC九大组件
- HandlerMapping
处理器映射器,对应的初始化方法是initHandlerMappings(context),这就是根据用户请求的资源uri来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,HandlerMapping就解决具体接收到一个请求之后使用哪个Handler进行处理 - HandlerAdapter
对应的初始化方法是initHandlerAdapters(context),从名字上看,它就是一个适配器。因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。HandlerAdapter解决如何让固定的Servlet处理方法调用灵活的Handler来进行处理
小结:Handler是用来干活的工具;HandlerMapping用于根据需要干的活找到相应的工具;HandlerAdapter是使用工具干活的人。 - HandlerExceptionResolver:根据异常设置ModelAndView,之后再交给render方法进行渲染
- ViewResolver:找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染
- RequestToViewNameTranslator:从request中获取ViewName
- LocaleResolver:解决语言国际化,从request解析出Locale
- ThemeResolver:⽤来解析主题
- MultipartResolver:⽤于上传请求
- FlashMapManager:⽤于重定向时的参数传递
13.3 MVC控制器是不是单例模式
- 控制器是单例模式
MVC如何保证线程安全:
- 不要在controller中定义成员变量,最好将控制器设计成无状态模式
- 万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例
- 在Controller中调用成员变量Service也不会造成线程安全问题:因为只是调用Service里的方法,方法都是线程安全的,多线程调用一个实例的方法,会在内存中复制变量,所以只要不在Constroller里修改Service这个实例就没问题
