SpringMVC小结

入门程序

1. 创建maven项目需要选择webapp骨架(org.apache.maven.archetypes:maven-archetype-webapp),还有添加额外配置(archetypeCatalog  internal),防止创建maven的web项目慢的问题。
2. 引入依赖(修改maven的jdk编译环境为1.8)
    spring-context
    spring-web
    spring-webmvc
    servlet-api(scope--->provide)
    jsp-api(scope--->provide)
 3. 核心配置文件的配置(mvc和context约束)
    1. 开启注解扫描 <context:component-scan base-package />
    2. 视图解析器对象 <bean class="InternalResourceViewResolver"> 拼接字符串的前面 和 拼接字符串的后面的两个set </bean>
    3. 开启SpringMVC框架注解支持 <mvc:annotation-driven/>
4. web.xml的配置
    1. 配置前端控制器,加载核心配置文件
        <servlet>
            <servlet-name>dispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>dispatcherServlet</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
5. 编写Controller和jsp页面进行测试
    1. 使用@Controller注解和jsp配置视图解析器进行测试
    2. 使用@RestController进行测试(推荐,相对简单)

MVC的执行流程

1. 配置文件的加载:
    启动项目,加载web.xml文件
        创建Servlet(前端/核心控制器-->指挥中心)的对象
        springmvc.xml的加载
            注解扫描加载SpringIOC的bean(@Controller) 
            视图解析器
            SpringMVC注解支持(@RequestMapping)
2. 请求响应:
    客户端发送请求------> 核心控制器(进入SpringMVC的请求流程)----->
    调用HandlerMapping(处理器映射器),匹配被@Controller注解注解的类中的@RequestMapping -----> 
    调用HandlerAdapter(处理器适配器)执行handle(Controller中声明的方法),handle返回结果(ModelAndView,模型视图,封装结果和视图)给HandlerAdapter ------->
    HandlerAdapter返回MOdelAndView给核心控制器,核心控制器拿到结果去找视图解析器(ViewResolver)进行视图解析----->
    视图解析器进行物理拼接形成具体的路径(View),返回给核心控制器 ------> 
    核心控制器进行视图渲染,填充数据到reuqest域中,response响应返回给客户端
3. 组件介绍
    前端/核心控制器: 中央调度,协调作用
    处理器映射器(HanderMapping): 根据请求的url查找Handler
    处理器适配器(HanderAdapter): 执行Handle
    ModelAndView: 模型及视图,封装结果视图
    ViewResolver: 视图解析器,解析逻辑视图
    Handle: 程序员编写业务逻辑处理,也就是Controller的方法

请求参数绑定

1. 基本数据类型绑定:
    原则: 请求后拼接的参数与handle的参数名保持一致
2. JavaBean对象的绑定
    1. 简单Bean直接使用form表单,name属性的值为Bean对象的属性名
    2. Bean内包含另一个Bean,name的值使用属性.属性的方式
    3. list使用属性下标点属性 ------> list[0].name
    4. map使用属性key属性的方式  -----> list['one'].name
    
3. 中文乱码问题:
    post乱码可以在web.xml中配置Spring提供的CharacterEncodingFilter
    get乱码:
        tomcat8之前,在tomcat的配置文件中配置URIEncoding="utf-8"
        new String(username.getBytes("ios-8859-1"),"UTF-8")的方式
        
4. 日期类型转换:
    1. 自定义类实现Converter<S,T>接口,使用DateFormat进行格式的转换.
    2. 在springmvc中配置自定义转换器(ConversionServiceFactoryBean).
        * 注意是context包下的转换器
        * 配置converters属性
        * 注入自定义转换器的bean

SpringMVC使用原生的Servlet

Handler方法的形参设置HttpServletRequest和HttpServletResponse就可以获取到所有的原生Servlet对象了

注解介绍

1. @RequestMapping:
    * 作用位置:
        1. 类上: 窄化请求路径,业务隔离
        2. 方法上: url请求映射
    * 属性:
        1. value: 指定请求路径,与path属性一样
        2. method: 指定请求方式,值有GET,POST,DELETE等等(枚举类型,RequestMethod)
        3. params: 用于指定限制请求参数条件
        4. headers: 用于指定请求头
        
2. @RequestParam:
    * 作用 : 在页面传递的参数名与后台不一致的时候需要使用该注解,作用于参数之前
    * 属性 :
        1. name/value : 指定页面传递的参数名
        2. required : 是否为必须,默认为true,表示该参数必须传递
        3. defaultValue : 给定参数一个默认值,当有默认值的时候required属性感觉就无效了
        
3. @RequestBody
    * 作用: 用于获取请求体的内容.(get请求不适用,POST请求适用)
        
4. @PathVariable(基于Restful风格的url支持) 
    * 作用在参数之前,目的是获取请求url中的占位符(参数)的信息
        /test/{sid} ---->  请求路径
        @PathVaribale(name="sid") Integer id ---> 方法的参数
        /test/10  ----> 客户端的访问路径
    * Restful的理解:
        通过请求参数决定对哪个资源进行操作
        通过请求方式决定采用何种操作(post,delete,put,get)
        
5. HiddenHttpMethodFilter:
    浏览器的form表单只支持get和post请求,要洗那个发送put或delete请求,需要借助SpringMVC提供的HiddenHttpMethodFilter.
    使用: 
        1. 在web中配置HiddenHttpMethodFilter过滤器
        2. 在表单中添加隐藏域,<input type="hidden" name="_method" value="PUT"/>
        3. 定义handle,声明method为RequestMethod.PUT
        
6. @RequestHeader
    用于获取请求消息头,作用在方法参数之前
    value属性可以指定获取哪个请求头信息(User-Agent比较常用)
    
7. @CookieValue
    * 获取指定名称cookie的值
    * value属性指定cookie名称(JSESSIONID)
    
8. @ModelAttribute
    * 作用位置:
        1. 方法上: 被@ModelAttribute注解修饰的方法表示该方法会在当前Controller的人格一个方法执行前先执行
            应用在表单提交实体数据不完整的情况下,为实体数据添加内容.
                方法有返回值(直接返回user),springmvc会将方法执行的返回值,存放在Model对象,最终会存放在Request中:
                    考虑在修改的时候根据id获取实体,方法返回实体,前台只需要传递修改的内容就可以了.
                方法无返回值,springmvc会将方法形参中的map中的数据存放在Model对象,最终会存放在Request中:
                    在方法中使用map保存数据,在Handler的参数前使用@ModelAttribute(name="key")获取
        2. 形式参数前(需制定value(key)属性):
            获取request中获取对应的value的数据
            
9. @SessionAttributes(只能作用于类上,指定属性)
    * 用于多个Handler间的参数共享(将数据存放在session域)
    * 使用Model对象的addAttribute方法存放数据
    * 使用ModelMap对象的get方法来获取session数据
    * 使用setComplete()方法清空session域的数据(使用的是SessionStatus对象)

Handler返回值类型

1. 字符串:简单字符串是逻辑视图名称
    * 使用返回的字符串,经过视图解析器,拼接前缀后缀,跳转到真实路径
2. void ===> 原始的Servlet操作
    * 请求路径默认是: 前缀 + handle访问路径 + 后缀
    * 使用原生Servlet进行跳转和携带参数,但是注意这个时候不会执行ViewResolver,所以必须写全路径,这个需要注意.在handle最后加个return;防止handle跳转方法完成后继续执行后续的代码.
    * 还有需要注意重定向是无法访问WEB-INF目录下的东西.
    * 直接响应数据: response.getWriter().print("content");注意处理乱码问题
3. ModelAndView
    * ModelAndView对象: ModelAndView mv = new ModelAndView();
    * 可以存放数据到对象,使用mv.addObject("key","value");实际还是存放在Model中,最后还是request域中
    * 存放跳转的页面: 使用mv.setViewName("success");
4. forward和redirect关键字的用法(返回类型还是String):
    * 返回的写法不一样,有了forward关键字后就不能调用视图解析器.
        return "forward:/WEB-INF/pages/success.jsp"
    * return "redirect:/index.jsp"; 重定向不能直接跳转到WEB-INF的目录下,并且redirect关键字不用添加项目目录
    * return "forward:testString"; 转发到指定的的Handle
      return "redirect:/user/testString";重定向到指定的的Handle

响应JSON数据

1. 在springmvc配置文件中配置静态资源不拦截(因为前端控制器会拦截所有资源):
    <mvc:resources mapping="/js/**" location="/js/" />,表示js文件下面的所有文件都不拦截
    <mvc:resources mapping="/css/**" location="/css/" />
    <mvc:resources mapping="/images/**" location="/images/" />
    > 注意静态资源放在web-app下
    
    上面的配置在mapping的后面也一定要加**,否则会失败(在我电脑上是这样),如果还是失败,可以试试在web.xml中配置:
    <servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>*.js</url-pattern>
	</servlet-mapping>
    
2. ajax请求返回json数据:
    * $.ajax({
        url:"user/testAjax",
        type:"POST",
        contentType:"application/json;charset=utf-8",
        data:'{"username":"hehe","password":"123456","age":24}',
        dataType:"json",
        success:function(data){
            alert(data);
        }
        
      })
3. 自动转换,将json字符串转换为对象,需要额外依赖jackson的jar包:
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.7</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.7</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.7</version>
    </dependency>
    
    接收json数据需要使用 ==@RequestBody==
        获取请求中的数据
        如果请求的参数是json格式,使用pojo接受,但是注意参数名和实体属性名一致
        
    响应json数据需要使用 ==@ResponseBody==
        直接将方法执行的返回值响应给客户端
        如果是pojo,将pojo对象转化为json字符串

传统方式文件上传(了解):

1. 表单的enctype属性取值必须是: multipart/form-data
   method属性值必须是post
   提供你文件选择域: <input type="file" />
2. 三方组件实现,引入额外依赖:
    commons-fileupload
    commons-io
3. 传统方式文件上传(后台代码实现):
    创建文件的上传路径(path),使用request.getSession().getServletContext().getRealPath("/uploads");
    核心解析器:
        DiskFileItemFactory ---> 磁盘文件的工厂对象
        ServletFileUpload ---> 文件上传的核心解析器,传入工厂对象
    解析request对象
        List<FileItem> list = ServletFileUpload.parseRequest(request);
    文件上传:
        遍历list,判断是否为普通表单项(isFormFiled),不是则进行上传
        获取文件上传名,修改上传名为唯一上传名(考虑使用UUID或者日期)
        fileItem.write(new File(path,filename));path表示父级目录,filename表示修改后的文件名.
        最后使用fileItem.delete(); 用来删除临时文件
        
    /**
     * 文件上传
     * @return
     */
    @RequestMapping("/fileupload1")
    public String fileuoload1(HttpServletRequest request) throws Exception {
        System.out.println("文件上传...");

        // 使用fileupload组件完成文件上传
        // 上传的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 判断,该路径是否存在
        File file = new File(path);
        if(!file.exists()){
            // 创建该文件夹
            file.mkdirs();
        }

        // 解析request对象,获取上传文件项
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 解析request
        List<FileItem> items = upload.parseRequest(request);
        // 遍历
        for(FileItem item:items){
            // 进行判断,当前item对象是否是上传文件项
            if(item.isFormField()){
                // 说明普通表单向
            }else{
                // 说明上传文件项
                // 获取上传文件的名称
                String filename = item.getName();
                // 把文件的名称设置唯一值,uuid
                String uuid = UUID.randomUUID().toString().replace("-", "");
                filename = uuid+"_"+filename;
                // 完成文件上传
                item.write(new File(path,filename));
                // 删除临时文件
                item.delete();
            }
        }

        return "success";
    }

SpringMVC的文件上传(掌握)

1. springmvc.xml配置文件上传解析器:
    类为CommonsMultipartResolver
    id为multipartResolver,这个不能改变(这个很重要)
    属性为:maxUploadSize,值为10485760
    <!--配置文件上传解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10485760"/>
    </bean>
    
2. handler的形参: (HttpServletRequest request,MultipartFile upload)
    表单的隐藏域的name属性必须和handler的形参MultipartFile的对象的名字一样.
    <form action="/file/fileupload" method="post" enctype="multipart/form-data">
        文件上传:<input type="file" name="upload">
        <input type="submit" value="提交">
    </form>
3. handler的步骤:
    1. 创建文件上传目录path
    2. 获取文件名,修改文件名为唯一名字
    3. 使用upload.transferTo(new File(newFileName))就完成了
    
        @RequestMapping("fileupload")
        public String fileUpload(HttpServletRequest request, MultipartFile upload) throws Exception {
            // 创建文件上传目录
            String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
            // 创建File
            File file = new File(realPath);
            // 判断目录是否存在
            if(!file.exists()){
                // 创建该文件夹
                file.mkdirs();
            }
            // 获取文件名,并修改文件名为唯一
            String name = upload.getOriginalFilename();
            // 生成uuid
            String replace = UUID.randomUUID().toString().replace("-", "");
            // 唯一文件名
            String fileName = replace + "_" + name;
            // 拼接最后的路径
            String newPath = realPath + fileName;
    
            // 进行文件的上传
            upload.transferTo(new File(newPath));
    
    
            return "hello";
        }

SpringMVC跨服务器方式的文件上传

1. 搭建图片服务器
    1. 修改tomcat端口号(修改3个地方)
    2. 修改tomcat的conf下的web.xml,修改readonly属性为false
    3. 在webapps下新建uploads目录
2. 加入依赖:
    jersey-core
    jersey-client
3. 步骤:
    创建客户端的对象Client.create()
    和图片服务器建立连接(client.resource())
    上传文件(webResource.put(new File(newFileName)))
    
    @RequestMapping("/fileupload3")
    public String fileuoload3(MultipartFile upload) throws Exception {
        System.out.println("跨服务器文件上传...");

        // 定义上传文件服务器路径
        String path = "http://localhost:9090/uploads/";

        // 说明上传文件项
        // 获取上传文件的名称
        String filename = upload.getOriginalFilename();
        // 把文件的名称设置唯一值,uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        filename = uuid+"_"+filename;

        // 创建客户端的对象
        Client client = Client.create();

        // 和图片服务器进行连接
        WebResource webResource = client.resource(path + filename);

        // 上传文件
        webResource.put(upload.getBytes());

        return "success";
    }

SpringMVC的异常处理

1. 不处理异常造成的问题:
    * 用户体验差
    * 系统安全性容易出现问题
2. 处理:
    1. 编写自定义异常处理类,继承Exception
        public class MyException extends Exception {
            private String msg;
            public MyException(String msg) {
                this.msg = msg;
            }
            public String getMsg() {
                return msg;
            }
            public void setMsg(String msg) {
                this.msg = msg;
            }
        }
    2. 编写异常处理器,实现HandlerExceptionResolver
        public class MyExceptionResolver implements HandlerExceptionResolver {
            @Override
            public ModelAndView resolveException(HttpServletRequest httpServletRequest,
                                                 HttpServletResponse httpServletResponse,
                                                 Object o,
                                                 Exception e) {
                // 创建自定义异常类
                MyException myException = null;
                // 转换
                if(e instanceof MyException){
                    myException = (MyException)e;
                } else{
                    myException = new MyException("程序遇到错误,但不是自定义异常类");
                }
                // 创建ModelAndView
                ModelAndView mv = new ModelAndView();
                mv.addObject("errormsg",myException.getMsg());
                mv.setViewName("error");
                return mv;
            }
        }
    3. 配置异常处理器,SpringMVC中配置异常处理器(也就是声明Bean)
        <!--配置异常处理器 -->
        <bean id="myExceptionResolver" class="com.itheima.exception.MyExceptionResolver"></bean>
    4. Controller如何进行编写呢?
        @RequestMapping("exce")
        public String testException() throws MyException {
            try {
                int i = 1 / 0;
            } catch (Exception e) {
                e.printStackTrace();
                throw new MyException("程序错误");
            }
            return "hello";
        }

SpringMVC的拦截器

1. 拦截器: 对处理器进行预处理和后处理(AOP的思想)
2. 拦截器链: 多个拦截器按一个次序形成一个链,执行顺序考虑栈的结构,前置代码是入栈,后置代码是出栈
3. 过滤器(filter)与拦截器(interceptor):
    过滤器什么都可以拦截,并且是属于servlet的,任何项目都可以用
    拦截器属于SpringMVC的,只有在SpringMVC的项目中使用,并且只能拦截控制器的访问
4. 使用拦截器的步骤:
    1. 编写拦截器: 类实现HandlerInterceptor,实现3个方法:
        boolean preHandler(): 预处理,handler执行前执行
            true: 放行,如果有多个拦截器,放行到下一个拦截器,没有后续拦截器,执行Handler方法
            false: 不放行, 到此为止,不继续向下执行
        postHandler(): 后处理,handler之后,跳转页面之前执行
        afterComletion(): 最后执行的方法,也就是成功返回页面后执行的代码
        
        public class MyInterceptor implements HandlerInterceptor {
            /**
             * Handler执行之前执行
             * @param request
             * @param response
             * @param handler
             * @return
             * @throws Exception
             */
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                System.out.println("preHandle=======================");
                return true;
            }
        
            /**
             * Handler执行之后,跳转页面之前
             * @param request
             * @param response
             * @param handler
             * @param modelAndView
             * @throws Exception
             */
            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                System.out.println("postHandle=======================");
            }
        
            /**
             * 跳转页面之后执行
             * @param request
             * @param response
             * @param handler
             * @param ex
             * @throws Exception
             */
            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                System.out.println("afterCompletion=============================");
            }
        }
    2. 配置拦截器: 
        <mvc:interceptors>
            <!--可以配置对个interceptor,表示多个拦截器-->
            <mvc:interceptor>
                <!--拦截的具体方法-->
                <mvc:mapping path="/**"/>  <!--所有的方法都拦截-->
                <!--不要拦截的具体方法,和上面那个配置一个就好-->
                <!-- <mvc:exclude-mapping path=""/>-->
                <!--配置拦截器对象-->
                <bean class=""/>
            </mvc:interceptor>
           
        </mvc:interceptors>
        
        
         <!--配置拦截器-->
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <bean class="com.itheima.interceptor.MyInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
        
        
    3. 前端:
        <% System.out.println("hello.jsp================"); %>
        
        执行效果:
            preHandle=======================
            interceptor===================
            postHandle=======================
            hello.jsp================
            afterCompletion=============================
5. 拦截器的作用:
    登录拦截器=======> 在prehandler()方法中获取session,判断有无用户,无用户跳转到登录.
posted @ 2018-10-14 10:53  庄子游世  阅读(344)  评论(0编辑  收藏  举报