什么是MVC
最典型的MVC就是JSP+servlet+JavaBean的模式
Spring-web
SpringMVC
public class MyServlet extends HttpServlet{ @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ String username = req.getParameter("username"); req.getSession().setAttribute("username", username); req.getRequestDispatcher("index.jsp").forward(req, resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ this.doPost(req. resp); } }
web.xml配置servlet
springmvc.xml
idea,添加lib文件夹
public class HelloController{ public void hello(){ System.out.println(this.getClass().getName()); } public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{ // 创建对象 ModelAndView mv = new ModelAndView(); // 添加视图名称, 要跳转的页面名称 mv.setViewName("hello"); // 向前端页面添加属性值 mv.addObject("hello", "hello, springmvc"); return null; } }
Spring的处理过程
@Controller public class HelloController{ @RequestMapping("/hello") public String hello(){ map.put("hello", "hello,SpringMVC"); return "hello"; } @RequestMapping("/hello") public String hello2(Map<String, String> map){ map.put("hello", "hello, SpringMVC"); return "hello"; } @RequestMapping(value = "/hello", method = RequestMethod.POST) public String hello2(Map<String, String> map){ map.put("hello", "hello, SpringMVC"); return "hello"; } @RequestMapping(value = "/hello", param = {"username"}) public String hello2(Map<String, String> map){ map.put("hello", "hello, SpringMVC"); return "hello"; } @RequestMapping(value = "/hello", param = {"username=zs", "age"}) public String hello2(Map<String, String> map){ map.put("hello", "hello, SpringMVC"); return "hello"; } // headers 表示限制请求头中的相关属性值, 用来做请求的限制 @RequestMapping(value = "/hello", headers = {"User-Agent=..."}) public String hello2(Map<String, String> map){ map.put("hello", "hello, SpringMVC"); return "hello"; } // produces: 限制请求中的Content-Type // consumers: 限制响应中的Content-Type // @RequestMapping可以进行模糊匹配 // ?: 替代任意一个人字符 // *: 提供多个字符 // **: 替代多层路径 }
常用的注解
参数列表
@Controller public class PathVariableController{ @RequestMapping("/testPathVariable/{name}") public String testPathVariable(@PathVariable String name){// rest方式用的比较多 request.getParameter("name"); System.out.println(name); return "hello"; } } @Controller public class PathVariableController{ @RequestMapping("/testPathVariable/{id}") public String testPathVariable(@PathVariable("id") String name){// rest方式用的比较多 request.getParameter("name"); System.out.println(name); return "hello"; } } @Controller public class PathVariableController{ @RequestMapping("/testPathVariable/{id}") public String testPathVariable(@PathVariable("id") Integer id, @PathVariable("name") String name){// rest方式用的比较多 request.getParameter("name"); System.out.println(name); return "hello"; } }
web.xml
springmvc.xml
如果不使用rest,那么增删改查就需要建立四个接口,如果用rest那么用GET,POST,PUT,DELETE四个提交方式对应增删改查
public class UserDao{ public void save(User user){ System.out.println("save"); } public void update(Integer id){ System.out.println("update"); } public void delete(Integer id){ System.out.println("delete"); } public void query(){ System.out.println("query"); } } public class User{ } @Controller public class UserController{ @Autowired private User userDao; @RequestMapping("/save") public String save(){ userDao.save(new User()); return "success"; } @RequestMapping("/update") public String update(Integer id){ userDao.update(id); return "success"; } @RequestMapping("/delete") public String delete(Integer id){ userDao.delete(id); return "success"; } @RequestMapping("/query") public String query(){ userDao.query(); return "success"; } }
REST
- POST:创建 /user/1
- GET:获取 /user
- PUT:更新资源 /user/1
- DELETE:删除资源 /user/1
导包
web.xml
public class UserDao{ public void save(User user){ System.out.println("save"); } public void update(Integer id){ System.out.println("update"); } public void delete(Integer id){ System.out.println("delete"); } public void query(){ System.out.println("query"); } } public class User{ } @Controller public class UserController{ @Autowired private User userDao; @RequestMapping(value = "/user", method = RequestMethod.POST) public String save(){ userDao.save(new User()); return "success"; } @RequestMapping(value = "/user", method = RequestMethod.PUT) public String update(Integer id){ userDao.update(id); return "success"; } @RequestMapping(value = "/user", method = RequestMethod.DELETE) public String delete(Integer id){ userDao.delete(id); return "success"; } @RequestMapping(value = "/user", method = RequestMethod.GET) public String query(){ userDao.query(); return "success"; } } public class MyFilter implements Filter{ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain){ filterChain.doFilter(servletRequest, servletResponse);
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setCharacterEncoding("UTF-8"); } }
SpringMVC的使用
RequestParam和PathVariable不要混淆
- RequestParam获取的是问号后面的键值对
- PathVariable获取的是路径后面的值
RequestParam的参数
- value:获取的参数值
- required:表示当前属性值是否存在,默认值是true,如果没有,400,bad request
- defaultValue:如果有传递参数就是用传递进来的参数,如果没有就是用默认值
乱码问题解决
需要设置过滤request和response的编码方式,可以自己手动编写过滤器,也可以由现成的框架实现
post:分别设置request和response编码格式
get:在tomcat的server.xml文件中,添加URIEncoding=utf-8
调整顺序可以解决乱码问题
在一个应用程序中,可能包含n多个过滤器,一般没有顺序要求,如果设置了编码过滤器,必须要将编码过滤器设置到最上面,保证编码过滤器,优先执行
编码过滤器,要放在最上面的位置
@Controller public class RequestController{ @RequestMapping("/testRequest") public String testRequest(@RequestParam(value = "username", required = false) String name){ System.out.println(name); return "success"; } } // 获取请求头信息 @Controller public class RequestController{ @RequestMapping("/testRequest") public String testRequestHeader(@testRequestHeader String userAgent){ System.out.println(userAgent); return "success"; } // request.getCookie() @RequestMapping("/testCookie") public String testCookie(@CookieValue("JSESSIONID"), String jsid){ System.out.println(jsid); return "success"; } } @Controller public class UserController22{ @RequestMapping("/testUser") public String testUser(User user){ System.out.println(user); return "success"; } }
每次tomcat可以启动,可以自己启动
SessionAttribute
@Controller @SessionAttributes(value = {"username", "msg"}, types = Integer.class) // 每次向request设置作品用于的时候, 顺带向session中保存一份 public class OutputController{ @RequestMapping("/output") public String output(Map<String, String> map){ map.put("msg", "hello,output"); System.out.println(model.getClass()); return "success"; } @RequestMapping("/output1") public String output(Map<String, String> map){ map.put("msg", "hello,output1"); return "success"; } @RequestMapping("/output2") public String output(Map<String, String> map){ model.addAttribute("msg", "hello, output2"); return "success"; } @RequestMapping("/output3") public String output(Map<String, String> map){ map.put("msg", "hello,output3"); return "success"; } @RequestMapping("/output4") public ModelAndView output(Map<String, String> map){ ModelAndView mv = new ModelAndView(); mv.setViewName("success"); mv.addObject("msg", "output4"); return mv; } @RequestMapping("/testSession") public String testSession(Model model){ model.addAtttibute("username", "zs"); return "success"; } }
被@ModelAttribute注释的方法会在此controller每个方法执行前被执行,因此对于一个controller映射多个URL的用法来说,要谨慎使用
@Controller public class UserController{ Object o1 = null; Object o2 = null; Model m1 = null; @RequestMapping("update") public String update(@ModelAttribute("user2") User user, Model model){ o2 = user; System.out.prinltn(user); System.out.println(o1 == o2);// true 指向同一个对象 System.out.println(m1 == model);// false return "success"; } @ModelAttribute public void testModelAttribute(Model model){ User user = new User(); user.setId(1); user.setName("name"); user.setPassword("1234"); model.addAttribute("user2", user); o1 = user; m1 = model1; } }
SessionAttributes要注意,在使用的时候如果没有对应的值
@Controller public class UserController{ Object o1 = null; Object o2 = null; Model m1 = null; @RequestMapping("update") public String update(@ModelAttribute("user2") User user, Model model){ o2 = user; System.out.prinltn(user); System.out.println(o1 == o2);// true 指向同一个对象 System.out.println(m1 == model);// false return "success"; } // 加一个user2标识, 可以匹配到user2 @ModelAttribute("user2") public void testModelAttribute(Model model){ User user = new User(); user.setId(1); user.setName("name"); user.setPassword("1234"); model.addAttribute("user2", user); o1 = user; m1 = model1; } }
forward转发
- 在使用转发的时候,要添加forward: 前缀
- 可以实现多次转发请求,可以回到页面,也可以转发到其他页面请求中
@Controller public class ForWardController{ @RequestMapping("/forward") public String forward(){ System.out.printfln("forward"); return "forward:/index.jsp"; } @RequestMapping("/forward2") public String forward2(){ System.out.printfln("forward2"); return "forward:/forward"; } }
redirct重定向
重定向操作也不会经过视图处理器
@Controller public class Redirctontroller{ @RequestMapping("/redirect") public String forward(){ System.out.printfln("forward"); return "redirect:/index.jsp"; } @RequestMapping("/redirect2") public String forward2(){ System.out.printfln("redirect2"); return "redirect:/redirect"; } }
转发与重定向的区别
- 转发是服务端
- 重定向是客户端
springmvc.xml
默认的servlet处理类
保证动态请求和静态资源能够访问
自定义视图解析器
- InternalResourceViewResolver
public class MyViewResolver implements ViewResolver, Ordered{ private int order = 0; @Override public View resolveViewName(String viewName, Locale locale) throws Exception{ if(viewName.startsWith("test")){ System.out.println(viewName); return MyView(); }else{ return null; } } } public void setOrder(int order){ this.order = order; } @Override public int getOrder(){ return 0; } public class MyView implements View{ @Override public String getContentType(){ return null; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception{ System.out.println("model:" + model); PrintWriter writer = response.getWriter(); writer.write("test"); writer.write("gogogo"); } } @Controller public class ViewResolverController{ @RequestMapping("/test") public String testView1(){ System.out.println("testView"); return "test:/index"; } @RequestMapping("/test2") public String testView2(){ System.out.println("testView"); return "test2:/index"; } }
springmvc.xml
value的值越大,优先级越小
添加自定义的类型转换器到ConversionServiceFactoryBean中
@Component public class MyConverter implements Converter<String, User>{ @Override public Object convert(Object source){ User user = null; if(source != null && "".equals(source) && (source.split("-").length == 4)){ user = new User(); user.setId(Integer.parseInt(source.split("-")[0])); user.setName(source.split("-")[1]); user.setAge(Integer.parseInt(source.split("-")[2])); user.setPassword(source.split("-")[3]); } return null; } } // 自定义类型转换器的时候, 注意对应的属性值跟方法中参数值对应起来 @Controller public class MyConverterController{ @RequestMapping("/converter") public String testConverter(User user, Model model){ System.out.println(user); model.addAttribute("user", user); return "success"; } }
自定义日期转换器
springmvc.xml
- 如果需要添加日期格式化器,只需要在实体类上添加@DataTimeFormat("格式")即可
- 如果配置日期格式化器的时候,同时配置了类型转换器,那么就会失效
- 需要使用 FormattingConversionServiceFactoryBean 对象
// 如果使用默认的类型转换器, 那么在输入日期的时候使用/作为分隔 @Controller public class DateConverterController{ @RequestMapping("/testDateConverter") public String testDateConverter(User user){ System.out.println(user); return "success"; } } public class User{ @DateTimeFormat(pattern = "yyyy-MM-dd") private Date birth; }
数据校验
JSR303提供的标准校验
Hibernate Validator 扩展注解
JSR校验框架
导包
日期格式化器
public class Person{ @NouNull private Integer id; @Length(min = 3, max = 10) private String name; private Integer age; private String gender; @Past // 设置的日期只能是之前的日期 private Date birth; @Email private String gmail; } @Controller public class ValidationController{ @RequestMapping("/validation") public String validate(@Valid Person person, BindingResult){ if(BindingResult.hasErrors()){ System.out.println("验证失败"); // 获取到当前所有的错误 List<FieldError> fieldErrors = bindingResult.getFieldErrors(); for(FieldError fieldError : fieldErrors){ System.out.println(fieldError.getField()); System.out.println(fieldError.getDefaultMessage()); map.put(fieldError.getField(), fieldError.getDefaultMessage()); } model.addAttribute("errors", map); return "forward:/login.jsp"; }else{ System.out.println("登陆成功"); return "success"; } } }
springmvc_json
@RequestBody 表示把当前请求的内容
@ResponseBody 解析字符串为标签
@Controller public class JsonController{ @ResponseBody @RequestMapping("/json") public String json(){ List<User> list = new ArrayList<User>(); list.add(new User("test1", 12, "man")); list.add(new User("test2", 13, "woman")); list.add(new User("test3", 14, "man")); return list; } @ResponseBody // 该注解会解析字符串为标签 @RequestMapping("/json2") public String json2(){ return "<h1>test</h1>"; } @RequestMapping("/testRequestBody") public String testRequestBody(@RequestBody String body){ System.out.println(body); return "success"; } @RequestMapping("/testRequestJson") public String testRequestJson(@RequestBody User user){ System.out.println(user); return "success"; } } public class User{ private Integer id; private String name; private Integer age; private String gender; public User(){ } public User(String name, Integer age, String gender){ } }
自定义响应ResponseEntity
@Controller public class Entity{ @RequestMapping("test") public String test(HttpEntity<String> httpEntity){ System.out.println(httpEntity); String body = httpEntity.getBody(); return "success"; } // 自定义响应相关的信息, 包含body和header @RequestMapping("testResponseEntity") public String teestResponseEntity(){ String body = "<h1>gogogo</h1>"; MultiValueMap<String, String> header = new HttpHeaders(); header.add("set-cookie", "name=zs"); return new ResponseEntity<String>(body, header, HttpStatus.OK); } }
servlet下载文件
@Controller public class DownController{ @RequestMapping("/download") public String download(HttpServletRequest request){ // 获取下载路径 ServletContext servletContext = request.getServletContext(); servletContext.getRealPath("/scripts/..."); // 通过io流对文件读写 FileInputStream FileInputStream = new FileInputStream(realPath); byte[] bytes = new byte[FileInputStream.available()]; FileInputStream.read(bytes); FileInputStream.close(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.set("Content-Disposition", "attachment:filename=..."); return new ResponseEntity<byte[]>(bytes, httpHeaders, HttpStatus.OK); } }
servlet上传文件
springmvc.xml
添加jar包
多文件上传
@Controller public class UploadController{ @RequestMappin("upload") public String upload(@RequestParam("file") MultipartFile multipartFile, @RequestParam(value = "desc", required = false) String desc){ System.out.println(desc); for(MultipartFile file: multipartFile){ if(!file.isEmpty()){ System.out.println(file.getOriginalFilename()); multipartFile.transferTo(new File("d:\file\" + multipartFile.getOriginalFilename())); } } return "success"; } }
拦截器和过滤器的区别
springmvc.xml
执行顺序,preHandler->目标方法->postHandler->页面跳转->afterCompletion
如果执行过程中抛出异常,那么afterCompletion依然会继续执行
执行顺序
public class MyInterceptor implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response){ System.out.println(this.getClass().getName()); return true;// 返回指控制流程是否停止 } public boolean postHandle(HttpServletRequest request, HttpServletResponse response){ System.out.println(this.getClass().getName()); return true; } public boolean afterCompletion(HttpServletRequest request, HttpServletResponse response){ System.out.println(this.getClass().getName()); return true; } } @Controller public class InterceptorController{ @RequestMapping("/testInterceptor") public String testInterceptor(){ // 如果此处抛出异常, after依然会执行 System.out.println(this.getClass().getName()); return "success"; } }
国际化
login_en_US.properties
login_zh_CN.properties
jsp文件body内容
springmvc.xml
jsp文件的body标签前不要漏下以下配置
@Controller public class I18nController{ @Autowired private MessageSource messageSource @RequestMapping("/i18n") public String i18n(Locale locale){ System.out.println(locale); String username = MessageSource.getMesssage("username", null, locale); System.out.println(username); return "login"; } @RequestMapping("/i18n") public String i18n(@RequestParam("locale") String localStr, Locale locale, HttpSession session){ Locale locale1 = null; System.out.println(locale); if(localStr != null && !"".equals(localStr)){ locale1 = new Locale(localStr.split("_")[0], localStr.split("_")[1]); }else{ locale1 = locale; } session.setAttribute(SessionLocaleResolver.class.getName() + ".LOCALE", locale1); return "login"; } } public class MyLocaleResolver implements LocaleResolver{ public Locale resolveLocale(HttpServletRequest request){ Locale locale = null; String localeStr = request.getParameter("locale"); if(localeStr != null && !"".equals(localStr)){ locale = new Locale(localeStr.split("_")[0].localeStr.split("_")) }else{ locale = request.getLocale(); } return locale; } }
mvc异常处理机制
容器启动好后,进入DispatcherServlet之后,会对HandlerExceptionResolver进行初始化操作
可以通过@ControllerAdvice注解进行标注,表示为全局异常处理类
如果有了全局异常,若当前类抛出了异常会现在本类查找,然后再查找全局异常
@Controller public class ExceptionController{ @RequestMapping("/exception1") public String exception(){ System.out.println(this.getClass().getName()); int i = 10/0; return "success"; } @ExceptionHandler(value = ({ArithmeticExcepton.class, NullPointerException.class})) public String handlerException(Exception exception){ ModelAndView mv = new ModelAndView(); mv.setViewName("error"); mv.addObject("exce", exception); return mv; } @ExceptionHandler(value = (Exception.class)) public String handlerException2(Exception exception){ ModelAndView mv = new ModelAndView(); mv.setViewName("error"); mv.addObject("exce", exception); return mv; } } @ControllerAdvice public class MyGlobalExceptionHandler{ @ExceptionHandler(value = ({ArithmeticExcepton.class, NullPointerException.class})) public String handlerException(Exception exception){ ModelAndView mv = new ModelAndView(); mv.setViewName("error"); mv.addObject("exce", exception); return mv; } @ExceptionHandler(value = (Exception.class)) public String handlerException2(Exception exception){ ModelAndView mv = new ModelAndView(); mv.setViewName("error"); mv.addObject("exce", exception); return mv; } }
ResponseStatus
@Controller public class ExceptionController{ @ResponseStatus(reason = "", value = HttpStatus.NOT_ACCEPTABLE) @RequestMapping("/exception") public String handlerException(Exception exception){ System.out.println("exception") return "success"; } }
@ResponseStatus,可以标注在方法中,但是不推荐使用
@Controller public class ExceptionController{ @RequestMapping("/exception") public String handlerException(String username){ System.out.println("exception") if("admin".equals(username)){ return "success"; }else{ throw new UsernameException(); } return "success"; } }
Springmvc流程图