springmvc
一、什么是spring MVC
spring MVC是一个基于MVC模式的一个轻量级web框架,它本身就是spring框架的一个模块,所以可以和spring框架无缝结合。其可以简化web开发,是一个比struts2更加方便的MVC框架。
二、springMVC的执行流程
1.DispatcherServlet :前端控制器,DispatcherServlet拦截请求对URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping;
2.HandlerMapping : 处理器映射,DispatcherServlet调用该处理映射器,获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回给DispatcherServlet ;
3.HandlerAdapter :处理器适配器,DispatcherServlet或得了Handler对象之后,就会调用对应的HandlerAdapter来对请求进行处理(执行对应的拦截器和Handler),并返回ModelAndView给DispatcherServlet。
4.Handler :处理器,HandlerAdapter执行Handler的方法并返回ModelAndView给HandlerAdapter。
5.ViewResolver:视图解析器,DispatcherServlet得到了ModelAndView后,调用ViewResolver来解析得到view(真正的视图对象)。
6.DispatcherServlet 有了view之后,就可以利用Model中的数据对视图进行渲染,并响应用户的请求。
流程图大致如下:
三、springMVC的使用
1.引入jar包,引入springMVC相关jar包,文件上传jar包,json转换相关jar包
2.在web.xml中配置springMVC核心过滤器
<!-- 配置spring MVC核心过滤器 --> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!-- 该配置文件默认是WEB-INF目录下上面的servlet-name-servlet.xml,如:dispatcherServlet-servlet.xml --> <param-value>classpath*:spring-mvc.xml</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解决中文乱码问题(无法解决get请求的乱码问题) --> <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>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- contextLoaderListener监听器的作用就是启动Web容器时,自动装配ApplicationContext的配置信息 --> <!-- 如果只用springMVC这个配置可以不要 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
3.总共有三种方式来创建处理器Handler,
①实现Controller接口,并实现其handleRequest方法,这种方式一个实现类中只有一个方法会被请求到。
②继承MultiActionController类,自己新建方法来进行映射,这种方式一个类中可以有多个方法来响应请求。
③用注解的方式声明,@Controller或@RestController
public class HelloWorldController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("传入的数据为"); String userName=request.getParameter("userName"); System.out.println("userName:"+userName); //封装数据,可以直接使用request对象,也可以使用封装等方式,真正使用时可以选择一种 request.setAttribute("rUserName", userName); Map<String, String > map=new HashMap<String, String>(); map.put("mUserName",userName); //返回视图层,如果使用map封装数据,需要作为(第二个)参数传递,也是request作用域。 return new ModelAndView("forward:/index.jsp",map); } } public class HelloSpringMvcController extends MultiActionController { public ModelAndView add(HttpServletRequest request,HttpServletResponse response){ String userName=request.getParameter("userName"); System.out.println("传入的数据为userName:"+userName); //封装数据,可以直接使用request对象,也可以使用封装等方式,真正使用时可以选择一种 request.setAttribute("rUserName", userName); Map<String, String > map=new HashMap<String, String>(); map.put("mUserName",userName); //返回视图层,如果使用map封装数据,需要作为(第二个)参数传递,也是request作用域。 return new ModelAndView("/index.jsp",map); } public ModelAndView update(HttpServletRequest request,HttpServletResponse response){ String userName=request.getParameter("userName"); System.out.println("传入的数据为userName:"+userName); //封装数据,可以直接使用request对象,也可以使用封装等方式,真正使用时可以选择一种 request.setAttribute("rUserName", userName); return new ModelAndView("/index.jsp"); } } @Controller @RequestMapping("/annotion") public class AnnotionController{ /** * 下面5个参数是springMVC默认支持的参数类型,我们只需在方法上给出这些参数就能够使用 * HttpServletRequest request * HttpServletResponse response * HttpSession session * Model modelModel * Map modelMap */ @RequestMapping("/get") public ModelAndView get(HttpServletRequest request,HttpServletResponse response, HttpSession session,Model model,ModelMap modelMap){ String userName=request.getParameter("userName"); request.setAttribute("request", "request:"+userName); session.setAttribute("session", "session:"+userName); model.addAttribute("model", "model:"+userName); ModelAndView modelAndView = new ModelAndView("/index2.jsp"); modelAndView.addObject("modelAndView", "modelAndView:"+userName); return modelAndView; } }
4.springmvc.xm配置文件
①通过BeanNameUrlHandlerMapping来映射请求和处理器之间对应的关系,通过name来进行匹配
<bean id="helloweb" class="test.controller.HelloWebController"></bean> <!-- 使用BeanNameUrlHandlerMapping来对处理器进行映射,这种映射关系,一个请求就对应着一个Controller类 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> <!-- 声明bean的name,因为使用了BeanNameUrlHandlerMapping,其实bean的id和name也可以当作name,也可以使用id,但是推荐使用name--> <bean name="/helloworld" class="test.controller.HelloWorldController"></bean>
②通过SimpleUrlHandlerMapping来映射
<!-- 使用BeanNameUrlHandlerMapping来对处理器进行映射,这种映射关系,一个请求就对应着一个Controller类 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <!-- 配置URL与ACTION对象ID进行映射 ,<prop key="second.action">second</prop>,其中key匹配url信息,value为controller的ID --> <props> <prop key="helloweb">helloweb</prop> <prop key="hellojava">hellojava</prop> </props> </property> </bean>
③和上面方式结合,在一个处理器内部映射多个方法对请求进行处理
<!-- 使用BeanNameUrlHandlerMapping来对处理器进行映射,这种映射关系,一个请求就对应着一个Controller类 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <!-- 配置URL与ACTION对象ID进行映射 ,<prop key="second.action">second</prop>,其中key匹配url信息,value为action的ID --> <props> <prop key="helloweb">helloweb</prop> <prop key="hellojava">hellojava</prop> <prop key="hellospringmvc">hellospringmvc</prop> </props> </property> </bean> <bean id="hellospringmvc" class="test.controller.HelloSpringMvcController" > <property name="methodNameResolver" ref="parameterMethodNameResolver"></property> </bean> <!-- 定义通过方法名调用控制器相关方法的规则 --> <bean id="parameterMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> <!-- 在url中使用do=方法名方式识别相关方法,例如:studentMulti.action?do=add,将调用add方法;这里的do不是固定的,可以改为其它 --> <property name="paramName" value="do" /> <!-- 如果没有指定方法名时,默认 调用控制器的list方法 --> <property name="defaultMethodName" value="list" /> </bean>
④对返回的视图加上前缀和后缀
<!-- 支持servlet与jsp视图解析,可进行进一步处理,此步可省略 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 可以加前缀或后缀 --> <!-- <property name="prefix" value="/jsp/"/> <property name="suffix" value=".jsp"/> --> </bean>
⑤使用注解扫描,这样就能用注解替代上面繁琐的配置,简化开发
<!-- 开启注解扫描 --> <mvc:annotation-driven/> <context:component-scan base-package="test"> <!-- 可以设置扫描哪些包或者注解 --> <!-- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> --> </context:component-scan>
5.springMVC的拦截器
public class MyInterceptor1 implements HandlerInterceptor { /** * 访问请求资源前执行,返回true则继续执行,返回false则不执行。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyInterceptor1:preHandle"); return true; } /** * 访问请求资源后(但在渲染视图之前),如果没有异常,将执行此方法 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("MyInterceptor1:postHandle"); } /** * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,不管理有没有异常都一定执行此方法 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyInterceptor1:afterCompletion"); } }
public class MyInterceptor2 implements HandlerInterceptor { /** * 访问请求资源前执行,返回true则继续执行,返回false则不执行。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyInterceptor2:preHandle"); return true; } /** * 访问请求资源后(但在渲染视图之前),如果没有异常,将执行此方法 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("MyInterceptor2:postHandle"); } /** * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,不管理有没有异常都一定执行此方法 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyInterceptor2:afterCompletion"); } }
<!-- 拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/annotion/get"/> <bean class="test.Interceptor.MyInterceptor1"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/annotion/get"/> <bean class="test.Interceptor.MyInterceptor2"></bean> </mvc:interceptor> </mvc:interceptors>
6.springMVC异常处理
①实现HandlerExceptionResolver接口对异常进行处理,并将该实现类交给容器进行管理
public enum StatusCode { id_not_null(100001,"id不能为空"), success(0, "操作成功") , error(9999, "服务器异常"); private int status; private String info; private StatusCode(int status, String info) { this.status = status; this.info = info; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } }
public class ServiceException extends Exception { private static final long serialVersionUID = 1L; private int code; private String name; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } public ServiceException(){} public ServiceException(int code,String name){ this.code = code; this.name = name; } }
@Component public class ServiceExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ServiceException serviceException = null; if(ex instanceof ServiceException){ serviceException = (ServiceException)ex; }else{ serviceException = new ServiceException(StatusCode.error.getStatus(),StatusCode.error.getInfo()); ex.printStackTrace(); } ModelAndView mv = new ModelAndView(); response.setStatus(HttpStatus.OK.value()); //设置状态码 response.setContentType(MediaType.APPLICATION_JSON_VALUE); //设置ContentType response.setCharacterEncoding("UTF-8"); //避免乱码 response.setHeader("Cache-Control", "no-cache, must-revalidate"); try { response.getWriter().write("{\"success\":false,\"code\":\""+serviceException.getCode()+"\",\"msg\":\"" + serviceException.getName() + "\"}"); } catch (IOException e) { e.printStackTrace(); } return mv; } }
②使用@ControllerAdvice和@ExceptionHandler来对异常进行统一处理
@ControllerAdvice public class BaseController { /** 基于@ExceptionHandler异常处理 */ @ResponseBody @ExceptionHandler public Map<String, Object> handleAndReturnData(HttpServletRequest request, HttpServletResponse response, Exception ex) { Map<String, Object> data = new HashMap<String, Object>(); ServiceException e = null; if(ex instanceof ServiceException) { e = (ServiceException)ex; }else{ e = new ServiceException(StatusCode.error.getStatus(),StatusCode.error.getInfo()); } data.put("code", e.getCode()); data.put("msg", e.getName()); data.put("success", false); return data; } }
③定义一个父类,并用@ExceptionHandler来标注一个方法来对异常进行处理
public class BaseController { /** 基于@ExceptionHandler异常处理 */ @ResponseBody @ExceptionHandler public Map<String, Object> handleAndReturnData(HttpServletRequest request, HttpServletResponse response, Exception ex) { Map<String, Object> data = new HashMap<String, Object>(); ServiceException e = null; if(ex instanceof ServiceException) { e = (ServiceException)ex; }else{ e = new ServiceException(StatusCode.error.getStatus(),StatusCode.error.getInfo()); } data.put("code", e.getCode()); data.put("msg", e.getName()); data.put("success", false); return data; } }
@RequestMapping("/exception") public class ExceptionController extends BaseController{ @RequestMapping("/info") public User info(User user) throws ServiceException{ throw new ServiceException(StatusCode.id_not_null.getStatus(),StatusCode.id_not_null.getInfo()); } }
7.文件上传下载
@Controller @RequestMapping("file") public class FileUDController { @ResponseBody @RequestMapping("/upload") public String upload(HttpServletRequest request, @RequestParam("files") MultipartFile[] files) throws Exception { String path = "C:/file"; for(MultipartFile file : files){ //如果文件不为空,写入上传路径 if(!file.isEmpty()) { //上传文件路径 //上传文件名 String filename = file.getOriginalFilename(); File filepath = new File(path,filename); //判断路径是否存在,如果不存在就创建一个 if (!filepath.getParentFile().exists()) { filepath.getParentFile().mkdirs(); } //将上传文件保存到一个目标文件当中 file.transferTo(new File(path + File.separator + filename)); } } return "success"; } @ResponseBody @RequestMapping("/download") public String download(HttpServletRequest request,HttpServletResponse response, @RequestParam("fileName") String fileName) throws Exception { //下载文件路径,使用nio方式 Path path = Paths.get("C:/file" + File.separator + fileName); response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(fileName, "UTF-8") + "\""); response.setContentType("application/octet-stream;charset=UTF-8"); OutputStream outputStream = response.getOutputStream(); Files.copy(path, response.getOutputStream()); outputStream.flush(); outputStream.close(); return "success"; } @RequestMapping("download2") public ResponseEntity<byte[]> download(@RequestParam("fileName") String fileName) throws IOException { String path = "C:/file"; File file = new File(path + File.separator + fileName); HttpHeaders headers = new HttpHeaders(); //使用URLEncoder.encode是为了解决get请求文件名乱码的问题 headers.setContentDispositionFormData("attachment", URLEncoder.encode(fileName, "UTF-8")); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED); } }
<!-- 文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 上传文件大小上限,单位为字节(10MB) --> <property name="maxUploadSize"> <value>10485760</value> </property> <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --> <property name="defaultEncoding"> <value>UTF-8</value> </property> </bean>