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> 

 

  
posted @ 2018-06-06 16:18  KyleInJava  阅读(207)  评论(0编辑  收藏  举报