SpringMVC从入门到入土!

1.Web、MVC、WebMVC概述
    1)完成一次web请求的过程
        Web浏览器发起请求
        Web服务器接收请求并处理请求,最后产生响应(一般为html)。
        web服务器处理完成后,返回内容给web客户端,客户端对接收的内容进行处理并显示出来。

        从这里可以看出,在web中,都是web客户端发起请求,web服务器接收处理请求并产生响应。
        一般Web服务器是不能主动通知Web客户端更新内容。虽然有些技术可以帮我们实现
        这样的效果,如服务器推技术(Comet)、还有HTML5中的websocket等。

    2)MVC模型(Model-View-Controller)
        是一种架构型的模式,本身不引入新功能,只是帮助我们将开发的代码结构,组织的更加合理。

        Model(模型)
            数据模型,提供要展示的数据,因此包含数据和行为,行为是用来处理这些数据的。
        不过现在一般都分离开来:Value Object(数据) 和 服务层(行为)。也就是数据由实
        体类或者javabean来提供,行为由service层来提供.
        
        View(视图)
            负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。

        Controller(控制器)
            接收用户请求,委托给模型进行处理,处理完毕后把返回的模型数据交给给视图。也就是说控制器在中间起到一个调度的作用。


        在标准的MVC中,模型能主动推数据给视图进行更新(可以采用观察者设计模式实现,在模型上注册视图,当模型更新时自动更新视图),但在Web开发中模型是无法主动推给视图,即无法主动更新用户界面,因为在Web的访问是请求-响应的模式。必须由客户端主动发出请求后,服务器才能把数据返回。

    3)WebMVC
        Web中MVC里面的模型-视图-控制器的概念和 标准MVC概念一样,但是在Web MVC模式下,
        模型无法【主动】推数据给视图,如果用户想要视图更新,需要再发送一次请求(即请求-响应模型)。

        在我们之前的学习中,其实就是把Servlet作为Controller(控制器),把jsp作为View(视图)
        ,把javabean作为Model(模型)中的数据,service层作为Model(模型)中的行为.

        注意:MVC和三层架构的区别
        


2.SpringWebMVC概述(SpringMVC)
    1)SpringWebMVC简称SpringMVC
        SpringMVC就是Spring框架提供的一个模块,通过实现MVC模式来很好地将数据、
        业务与展现进行分离,SpringMVC框架的目的是要简化我们日常的Web开发。

        SpringMVC框架跟其他的WebMVC框架一样,都是请求驱动,并且设计围绕一个
        能够分发请求到控制器以及提供其他加快web应用开发功能的核心Servlet(叫做
        DispatcherServlet,即前端控制器)。Spring的DispatcherServlet实现比
        其他框架中还要多的功能。它和spring的ioc容器完全整合,并且允许使用spring
        中其他的所有功能。

        SpringMVC框架设计的一个核心的原则就是"开闭原则",对扩展开放,对修改关闭.
        所以SpringMVC框架中很多方法都是final的,不允许用户随意覆盖,但是却提供给用
        户很多可扩展的机制。SpringMVC目前已经成为非常流行的web应用的框架。

        
    2)SpringMVC框架的获取
        由于SpringMVC是Spring框架中的一个模块,所以我们下载Spring框架即可,因为里面包含了Spring框架的各个模块的相关东西,当然也包含了SpringMVC的.(jar包、API文档、源代码)
        
        spring-aop-3.2.4.RELEASE.jar
        spring-aspects-3.2.4.RELEASE.jar
        spring-beans-3.2.4.RELEASE.jar
        spring-context-3.2.4.RELEASE.jar
        spring-context-support-3.2.4.RELEASE.jar
        spring-core-3.2.4.RELEASE.jar
        spring-expression-3.2.4.RELEASE.jar
        spring-instrument-3.2.4.RELEASE.jar
        spring-instrument-tomcat-3.2.4.RELEASE.jar
        spring-jdbc-3.2.4.RELEASE.jar
        spring-jms-3.2.4.RELEASE.jar
        spring-orm-3.2.4.RELEASE.jar
        spring-oxm-3.2.4.RELEASE.jar
        spring-struts-3.2.4.RELEASE.jar
        spring-test-3.2.4.RELEASE.jar
        spring-tx-3.2.4.RELEASE.jar
        spring-web-3.2.4.RELEASE.jar
        spring-webmvc-3.2.4.RELEASE.jar
        spring-webmvc-portlet-3.2.4.RELEASE.jar

    3)SpringMVC框架的核心组件
        1.DispatcherServlet: 前端控制器,用来过滤客户端
        发送过来,想要进行逻辑处理的请求。

        2.Controller/Handler: 控制器/处理器。开发人员
        自定义,用来处理用户请求的,
        并且处理完成之后返回给用户指定视图的对象。

        3.HandlerMapping: 处理器映射器。DispatcherServlet接收到客户端请求的URL
        之后,根据一定的匹配规则,再把请求转发给对应的Handler,这个匹配规则由HandlerMapping决定。
        
        4.HandlerAdaptor:处理器适配器。处理器适配器用来适配每一个要执行的Handler对象。
        通过HandlerAdapter可以支持任意的类作为处理器
        
        5.ViewResolver:视图解析器。Handler返回的是逻辑视图名,需要有一个解析器能够将逻辑
        视图名转换成实际的物理视图。而且Spring的可扩展性决定了视图可以由很多种,所以需要不同
        的视图解析器,解析不同的视图。但是一般由jsp充当视图的情况居多


        SpringMVC框架提供一个核心的Servlet对象(DispatcherServlet,前端控制器)来对服务器接
        收到的请求进行解析,当这个请求被DispatcherServlet获取到之后,DispatherServlet需要根
        据HandlerMapping对象的映射关系,将这个请求转交给真正能够处理客户端请求的Controller控制
        器(我们要写的就是这个东西,相当于之前的servlet)来处理。Controller处理完成后返回
        ModelAndView对象,也就是模型和视图的结合体。ViewResolver视图解析器根据ModelAndView
        中的逻辑视图名找到真正的物理视图,同时使用ModelAndView中模型里面的数据对视图进行渲染。
        最后把准备好的视图展现给用户

3.SpringMVC框架在项目中的搭建
    第一步:构建Web项目
    第二步:导入所需jar包
    第三步:配置前端控制器DispatcherServlet
    第四步:编写Controller控制器(也称为Handler处理器)
    第五步:配置处理器映射器(可省去,有默认配置)
    第六步:配置处理器适配器(可省去,有默认配置)
    第七步:配置视图解析器(可省去,有默认配置,但是前缀和后缀都为"")
    第八步:配置处理器

    1)构建Web项目
        在自己Eclipse中创建一个动态web项目(DynamicWebProject),注意JDK版本和项目版本的选择
    
    
    2)导入所需的jar包
        在lib目录下放入如下jar包,这是初始jar包,根据后续需求会陆续加入jar包
        commons-logging-1.2.jar
        spring-beans-3.2.4.RELEASE.jar
        spring-context-3.2.4.RELEASE.jar
        spring-core-3.2.4.RELEASE.jar
        spring-expression-3.2.4.RELEASE.jar
        spring-web-3.2.4.RELEASE.jar
        spring-webmvc-3.2.4.RELEASE.jar


    3)配置前端控制器DispatcherServlet
        SpringMVC的核心控制器就是一个Servlet对象,继承自HttpServlet,所以需要在web.xml文件中配置。

        SpringMVC是Spring提供的一个模块,Spring所有的模块都是基于Spring IOC功能的。所以SpringMVC的DispatcherServlet
        对象在初始化之前也会去实例化Spring的容器对象(ApplicationContext),那么就需要读取Spring的配置文件。
        默认SpringMVC会在你web应用的WEB-INF目录下查找一个名字为[servlet-name]-servlet.xml文件,
        并且创建在这个文件中定义的bean对象。如果你提供的spring配置文件的名字或者位置和默认的不同,那么需
        要在配置servlet时同时指定配置文件的位置。

        例如:
          <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
          </servlet>
          <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>*.action</url-pattern>
          </servlet-mapping>

        如上配置,框架会自动去当前应用的WEB-INF目录下查找名字为SpringMVC-servlet.xml文件(默认前缀和<servlet-name>标签中的值一致)。
        也可以自己指定配置文件的名字和位置:
            
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-web-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>*.action</url-pattern>
        </servlet-mapping>

        
        注意:
            配置文件在WEB-INF下:
            <param-value>/WEB-INF/spring-web-mvc.xml</param-value>
            配置文件在classpath下:
            <param-value>classpath:spring-web-mvc.xml</param-value>

        注意:
            <url-pattern>*.action</url-pattern>
            也可以配置成 注意这里是/ 不是/*
            <url-pattern>/</url-pattern>

    4)编写Controller控制器
        Controller控制器,是MVC中的部分C,因为此处的控制器主要负责功能处理部分:
        1、收集、验证请求参数并封装到对象上;
        2、将对象交给业务层,由业务对象处理并返回模型数据;
        3、返回ModelAndView(Model部分是业务层返回的模型数据,视图部分为逻辑视图名)。
         
        前端控制器(DispatcherServlet)主要负责整体的控制流程的调度部分:
        1、负责将请求委托给控制器进行处理;
        2、根据控制器返回的逻辑视图名选择具体的视图进行渲染(并把模型数据传入)。
        因此MVC中完整的C(包含控制逻辑+功能处理)由(DispatcherServlet + Controller)组成。
        
        Controller接口中只有一个需要实现的方法就是handleRequest方法,
        方法中接收两个参数,分别对应Servlet对象中的request,response对象。可以从request
        中获取客户端提交过来的请求参数。返回值ModelAndView,既包含要返回给客户端浏览
        器的逻辑视图又包含要对视图进行渲染的数据模型。

        例如:
        public class HelloWorldController implements Controller{
            @Override
            public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
                String name = request.getParameter("name");
                //ModelAndView对象中包括了要返回的逻辑视图,以及数据模型
                ModelAndView mv = new ModelAndView();
                //设置逻辑视图名称
                mv.setViewName("hello");
                //设置数据模型
                mv.addObject("name", name);

                return mv;
            }
        }
    
    5)配置映射器(可省去,有默认配置)
        注意:如果xml文件不能自动提示,那么可以在Eclipse中把schame配置过来即可,schame文件也在下载的spring的压缩包中
        Spring容器需要根据映射器来将用户提交的请求url和后台Controller/Handler进行绑定,所以需要配置映射器。
        例如:
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

        BeanNameUrlHandlerMapping:表示将请求的URL和Bean名字映射,如URL为 
        "/hello",则Spring配置文件必须有一个名字为"/hello"的Bean.
        注意:这里/代表的含义是url中项目名后面的/


    6)配置适配器(可省去,有默认配置)
        想要正确运行自定义处理器,需要配置处理器适配器,在spring的配置文件中(就是本例中的SpringMVC-servlet.xml),进行如下配置:
        
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

        SimpleControllerHandlerAdapter:表示所有实现了org.springframework.web.servlet.mvc.Controller
        接口的Bean可以作为SpringMVC中的处理器。如果需要其他类型的处理器可以通过实现HadlerAdapter来解决。


    7)配置视图解析器(可省去,有默认配置,但是前缀和后缀都为"")
        当处理器执行完成后,返回给spring容器一个ModelAndView对象,这个对象需要能够被解析成与之相对应的视图,并且
        使用返回的Model数据对视图进行渲染。
        
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>  
            <property name="prefix" value="/WEB-INF/jsp/"/>  
            <property name="suffix" value=".jsp"/>  
        </bean>  

        如果配置设置为如上操作,那么在自定义的Handler中返回的视图的名字不能有后缀.jsp,并且页面一定放在/WEB-INF目录下。
        
        InternalResourceViewResolver:用于支持Servlet、JSP视图解析;
        viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,classpath中必须包含jstl的相关jar包;
        prefix和suffix:视图页面的前缀和后缀(前缀+逻辑视图名+后缀),比如传进来的逻辑视图名为hello,则该该jsp视图页面应该存放在"WEB-INF/jsp/hello.jsp"
        
        注意:放在WEB-INF下的页面,只能通过内部跳转的方式访问到,因为客户端访问不到WEB-INF目录,而且服务器端可以访问到WEB-INF目录
        注意:需要引入jstl相关jar包
        注意:页面中的路径问题
             <%
                String path = request.getContextPath();
                String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
             %>

             <base href="<%=basePath%>" />

    8)配置处理器
        把编写好的handler/controller在spring中进配置,让其接受Spring IoC容器管理
        <bean name="/hello.action" class="com.briup.web.controller.HelloWorldController"/>  

    

    注意: 对于Spring配置文件中的处理器适配器,处理器映射器,都可以省去不写,springMVC框架中会有默认配置的,视图解析器也可以不配置,因为在org.springframework.web.servlet.DispatcherServlet这个类的同包下,有一个DispatcherServlet.properties文件,里面就是SpringMVC默认的配置,是当用户的Spring配置文件中没有指定配置时使用的默认策略(你不配置那么用户默认的,你配置了,那么就使用你的配置)

    从默认的配置中可以看出DispatcherServlet在启动时会自动注册这些特殊的Bean,无需我们注册,如果我们注册了,默认的将不会注册。
 
    因此之前的BeanNameUrlHandlerMapping、SimpleControllerHandlerAdapter是不需要注册的,DispatcherServlet默认会注册这两个Bean。
    

    整个访问的流程:
    1、  首先用户发送请求,前端控制器DispatcherServlet收到请求后自己不进行处理,而是委托给其他的解析器进行处理,前端控制器作为统一访问点,进行全局的流程控制;
    2、  DispatcherServlet把请求转交给HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器)
    3、  DispatcherServlet再把请求转交给HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器(适配器模式).简单点说就是让我们知道接下来应该调用Handler处理器里面的什么方法
    4、  HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
    5、  ModelAndView的逻辑视图名交给ViewResolver解析器, ViewResolver解析器把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
    6、  View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
    7、最后返回到DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

4.DispatcherServlet中的映射路径
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    1)拦截所有请求
        此处需要特别强调的是 <url-pattern>/</url-pattern>使用的是/,而不是/*,
        如果使用/*,那么请求时可以通过DispatcherServlet转发到相应的Controller中,
        但是Controller返回的时候,如返回的jsp还会再次被拦截,这样导致404错误,即访问不到jsp。
    
    2)自定义拦截请求
        拦截*.do、*.html、*.action, 例如/user/add.do
        这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。

        拦截/,例如:/user/add
        可以实现REST风格的访问
        弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。

        拦截/*,这是一个错误的方式,请求可以走到Controller中,但跳转到jsp时再次被拦截,不能访问到jsp。


    3)静态资源的访问,如jpg,js,css
        如果DispatcherServlet拦截"*.do"这样的有后缀的URL,就不存在访问不到静态资源的问题。
      如果DispatcherServlet拦截"/",为了实现REST风格,拦截了所有的请求,那么同时对*.js,*.jpg等静态文件的访问也就被拦截了。
        例如:
        <link href="css/hello.css" rel="stylesheet" type="text/css"/>
        <script type="text/javascript" src="js/hello.js"></script>
        <img alt="none" src="images/logo.png">    

        解决方式一:利用Tomcat的defaultServlet来处理静态文件
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.jpg</url-pattern>
        </servlet-mapping>

        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.js</url-pattern>
        </servlet-mapping>

        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.css</url-pattern>
        </servlet-mapping>

        特点:1.要配置多个,每种文件配置一个。
            2.要写在DispatcherServlet的前面(和tomcat版本有关),让defaultServlet先拦截请求,
    这样请求就不会进入Spring了
            3. 高性能。

        
        解决方式二: 使用<mvc:resources>标签,例如:
        <mvc:resources mapping="/images/**" location="/images/"/>  
        <mvc:resources mapping="/js/**" location="/js/"/>  
        <mvc:resources mapping="/css/**" location="/css/"/> 
        
        mapping: 映射
        两个*,表示映射指定路径下所有的URL,包括子路径
        location:本地资源路径

        这样如果有访问/images或者/js或者/css路径下面的资源的时候,spring就不会拦截了


        解决方式三: 使用<mvc:default-servlet-handler/>标签
        在spring配置文件中加入此标签配置即可


5.spring提供的编码过滤器
    查看这个过滤器类源码便可知这里所传的俩个参数的作用
    <filter>
        <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

1.Controller接口及其实现类
    Controller是控制器接口,此处只有一个方法handleRequest,用于进行请求的功能处理,处理完请求后返回ModelAndView(Model模型数据部分 和 View视图部分)。
    
    如果想直接在处理器/控制器里使用response向客户端写回数据,可以通过返回null来告诉DispatcherServlet我们已经写出响应了,不需要它进行视图解析

    Spring默认提供了一些Controller接口的实现类以方便我们使用,在Eclipse中选择Controller接口然后右键open type Hierarchy即可查看改接口的实现类,每个实现类都有自己特殊的功能,这里以实现类AbstractController为例简单介绍下。
    查看AbstractController类中代码可知,我们写一个Controller的时候可以继承AbstractController然后实现handleRequestInternal方法即可。


    提供了【可选】的会话的串行化访问功能,例如:
    //即同一会话,线程同步
    public class HelloWorldController extends AbstractController{
        @Override
        protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
                throws Exception {
            
            String name = request.getParameter("name");
            
            //ModelAndView对象中包括了要返回的逻辑视图,以及数据模型
            ModelAndView mv = new ModelAndView();
            //设置视图名称,可以是字符串 也可以是视图对象
            mv.setViewName("hello");
            //设置数据模型
            mv.addObject("name", name);
            
            return mv;
        }


    }

    <bean name="/hello" class="com.briup.web.controller.HelloWorldController">
        <property name="synchronizeOnSession" value="test"></property>
    </bean>

    

    直接通过response写响应,例如:
    public class HelloWorldController extends AbstractController{
        @Override
        protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
                throws Exception {
            
            response.getWriter().write("Hello World!!");        
            //如果想直接在该处理器/控制器写响应 可以通过返回null告诉DispatcherServlet自己已经写出响应了,不需要它进行视图解析

            return null;
        }

    }
    

    强制请求方法类型,例如:
    //只支持post和get方法
    <bean name="/hello" class="com.briup.web.controller.HelloWorldController">
        <property name="supportedMethods" value="POST,GET"></property>
    </bean>


    当前请求的session前置条件检查,如果当前请求无session将抛出HttpSessionRequiredException异常,例如:
    //在进入该控制器时,一定要有session存在,否则抛出HttpSessionRequiredException异常。

    <bean name="/hello" class="com.briup.web.controller.HelloWorldController">
        <property name="requireSession" value="true"/>
    </bean>


2.自定义适配器
    一般情况下,springMVCSimpleControllerHandlerAdapter会是我们常用的适配器,也是SpringMVC中默认的适配器,该适配器中的主要代码如下:
    public class SimpleControllerHandlerAdapter implements HandlerAdapter {
        public boolean supports(Object handler) {
            return (handler instanceof Controller);
        }
        public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {

            return ((Controller) handler).handleRequest(request, response);
        }
    }
    从代码中可以看出,它首先会判断我们的handler是否实现了Controller接口,如果实现了,那么会调用Controller接口中的handleRequest方法

    那么根据这种方式能看出,我们也可以有自己的适配器的实现,那么就可以让任意类成为SpringMVC中的handler了,无论我们的类是否实现了Controller接口
    
    例如:
        自己的接口:
        public interface MyHandler {
            public ModelAndView handler_test(HttpServletRequest request, HttpServletResponse response)throws Exception;
        }
        自己的适配器:
        public class MyHandlerAdapter implements HandlerAdapter{
            @Override
            public boolean supports(Object handler) {
                return (handler instanceof MyHandler);
            }

            @Override
            public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                return ((MyHandler)handler).handler_test(request, response);
            }

            @Override
            public long getLastModified(HttpServletRequest request, Object handler) {
                return -1L;
            }

        }
        
        自己的hander:(就是我们之前写的Controller)
        public class TestController implements MyController{
            @Override
            public ModelAndView handler_test(HttpServletRequest request, HttpServletResponse response) throws Exception {
                String name = request.getParameter("name");
                ModelAndView mv = new ModelAndView("hello");
                mv.addObject("name", name);
                return mv;
            }
        }

        最后在spring的配置中把我们的适配器进行配置即可正常使用.

3.处理器拦截器
    SpringMVC的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
    1)常见应用场景
        1、日志记录
        2、权限检查
        3、性能监控
        4、通用行为 例如读取用户cookie
        5、OpenSessionInView 例如在Hibernate中,
        在进入处理器前打开Session,在完成后关闭Session。
        等
    2)拦截器接口
        public interface HandlerInterceptor {
            boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;

            void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;

            void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
        }

        preHandle方法 
            预处理回调方法,实现处理器的预处理,第三个参数为的处理器(本次请求要访问的那个Controller)
            返回值:true表示继续流程(如调用下一个拦截器或处理器)
                    false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应
        
        postHandle方法
            后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理,modelAndView也可能为null。    
        
        afterCompletion方法
            整个请求处理完毕回调方法,即在视图渲染完毕时回调
    
    3)拦截器适配器
        有时候我们可能只需要实现三个回调方法中的某一个,如果实现HandlerInterceptor 接口的话,三个方法必须实现,不管你需不需要,此时spring 提供了一个HandlerInterceptorAdapter 适配器(适配器模式),允许我们只实现需要的回调方法。
        在HandlerInterceptorAdapter中,对HandlerInterceptor 接口中的三个方法都进行了空实现,其中preHandle方法的返回值,默认是true
    
    4)测试一个拦截器
        拦截器代码:
        public class MyInterceptor1 extends HandlerInterceptorAdapter{
            @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");
            }
        }

        配置文件:
        <bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>

        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
            <property name="interceptors">
                <list>
                    <ref bean="handlerInterceptor1"/>
                </list>
            </property>
        </bean>        

        访问一个测试的Controller查看结果:
        MyInterceptor1 preHandle
        TestController执行
        MyInterceptor1 postHandle
        MyInterceptor1 afterCompletion

    5)测试俩个拦截器
        俩个拦截器的代码和上面类似,只是每个输出的内容不同
        配置文件:
        <bean name="handlerInterceptor1" class="com.briup.web.interceptor.MyInterceptor1"/>
        <bean name="handlerInterceptor2" class="com.briup.web.interceptor.MyInterceptor1"/>

        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
            <property name="interceptors">
                <list>
                    <ref bean="handlerInterceptor1"/>
                    <ref bean="handlerInterceptor2"/>
                </list>
            </property>
        </bean>

        访问一个测试的Controller查看结果:
        MyInterceptor1 preHandle
        MyInterceptor2 preHandle
        TestController执行
        MyInterceptor2 postHandle
        MyInterceptor1 postHandle
        MyInterceptor2 afterCompletion
        MyInterceptor1 afterCompletion
        
        注意:<list>标签中引用拦截器的顺序会影响结果输出的顺序

    
    6)如果Controller等采用的注解配置,那么拦截器需要mvc标签进行配置
        注意:每个<mvc:interceptor>只能配置一个拦截器
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <ref bean="handlerInterceptor1"/>
            </mvc:interceptor>
        </mvc:interceptors>

        例如1: 注意/*和/**的区别
        <mvc:interceptors>
            <!-- 下面所有的mvc映射路径都会被这个拦截器拦截 -->
            <bean class="com.briup.web.interceptor.MyInterceptor1" />

            <mvc:interceptor>
                <mapping path="/**"/>
                <exclude-mapping path="/admin/**"/>
                <bean class="com.briup.web.interceptor.MyInterceptor2" />
            </mvc:interceptor>
            <mvc:interceptor>
                <mapping path="/secure/*"/>
                <bean class="com.briup.web.interceptor.MyInterceptor3" />
            </mvc:interceptor>
        </mvc:interceptors>
    

    7)拦截器是单例
        因此不管多少用户请求多少次都只有一个拦截器实现,即线程不安全。
        所以在必要时可以在拦截器中使用ThreadLocal,它是和线程绑定,
        一个线程一个ThreadLocal,A 线程的ThreadLocal只能看到A线程的ThreadLocal,
        不能看到B线程的ThreadLocal。
        
    8)记录执行Controller所用时间
        public class TimeInterceptor extends HandlerInterceptorAdapter{
            //拦截器是单例,不是线程安全的,所以这里使用ThreadLocal
            private ThreadLocal<Long> local = new ThreadLocal<>();
            
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                long start = System.currentTimeMillis();
                local.set(start);
                return true;
            }
            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                    throws Exception {
                long end = System.currentTimeMillis();
                System.out.println("共耗时:"+(end-local.get()));
            }
        }


    9)登录检查
        public class LoginInterceptor extends HandlerInterceptorAdapter{
            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                    throws Exception {
                //请求到登录页面放行
                if(request.getServletPath().startsWith("/login")) {
                    return true;
                }

                //如果用户已经登录放行
                if(request.getSession().getAttribute("username") != null) {
                    return true;
                }

                //重定向到登录页面
                response.sendRedirect(request.getContextPath() + "/login");

                return false;
            }
        }

    
    注意:推荐能使用servlet规范中的过滤器Filter实现的功能就用Filter实现,因为HandlerInteceptor只有在SpringWebMVC环境下才能使用,因此Filter是最通用的、最先应该使用的。

4.基于注解的SpringMVC
    
    1)用于支持注解的配置
        使用基于注解的配置可以省略很多操作,更方便。我们之前所看到的所有的xml配置,如果替换成基于注解只需要在spring的xml文件中做如下配置:
        <mvc:annotation-driven/>
        
        在Spring中,
        处理器列可以使用   @Controller注解
        业务逻辑层可以使用 @Service注解
        数据持久层可以使用 @Repository注解

        如果在处理器上使用 @Controller注解,那么还需要在配置文件中指定哪个包下面的类使用了该注解:
        <context:component-scan base-package="com.briup.web.controller"></context:component-scan>


    2)基于注解的Controller
        使用注解后,就不需要再实现特定的接口,任意一个javaBean对象都可以当做处理器对象,对象中任意一个方法都可以作为处理器方法。
        只需
            在类上加上 @Controller注解
            方法上加上 @RequestMapping注解
        即可

        例如:
        web.xml中:
        <servlet>
            <servlet-name>SpringMVC</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-web-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>SpringMVC</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
        

        src下面的spring-web-mvc.xml中:
        <mvc:annotation-driven/>
        <context:component-scan base-package="com.briup.web.controller"></context:component-scan>
        
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>  
            <property name="prefix" value="/WEB-INF/jsp/"/>  
            <property name="suffix" value=".jsp"/>  
        </bean>


        自定义的Controller中:
        @Controller
        public class HomeController {
            @RequestMapping("/home")
            public ModelAndView home(){
                ModelAndView mv = new ModelAndView("index");
                return mv;
            }
        }


        如上代码,使用 @Controller表明HomeController类是一个处理器类,
        通过 @RequestMapping("/home")表明当url请求名为/home时,
        调用home方法执行处理,当处理完成之后返回ModelAndView对象。
        因为在spring-web-mvc.xml中配置了视图解析器的前缀和后缀,所以最后视图home.jsp被返回
    

    3)基于注解的Controller的返回值
        1.返回ModelAndView,和之前一样

        2.返回String,表示跳转的逻辑视图名字,模型可以通过参数传过来
            @Controller
            public class HomeController {
                @RequestMapping("/home")
                public String home(Model model){
                    model.addAttribute("msg", "hello world");
                    return "index";
                }
            }
        
        3.声明返回类型为void
            可以通过参数获取request和response,分别使用服务器内部跳转和重定向,自己来决定要跳转的位置。
            @Controller
            public class HomeController {
                @RequestMapping("/home")
                public void home(HttpServletRequest request,HttpServletResponse response){
                    String username = request.getParameter("username");
                    response.setContentType("text/html;charset=utf-8");
                    response.getWriter().write("hello world! "+username);
                    //或者使用servlet的方式进行跳转/重定向
                    
                }
            }

5.Spring2.5中引入注解对处理器(handler)支持
    @Controller
        用于标识是处理器类;
    @RequestMapping
        请求到处理器功能方法的映射规则;
    @RequestParam
        请求参数到处理器功能处理方法的方法参数上的绑定;
    @ModelAttribute
        请求参数到命令对象的绑定;
    @SessionAttributes
        用于声明session 级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名称,则这些属性会透明的保存到session 中
    @InitBinder
        自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型;


6.Spring3引入了更多的注解,其中包含了对RESTful架构风格的支持
    @CookieValue
        cookie数据到处理器功能处理方法的方法参数上的绑定;
    @RequestHeader
        请求头数据到处理器功能处理方法的方法参数上的绑定;
    @RequestBody
        请求的body体的绑定
    @ResponseBody
        处理器功能处理方法的返回值作为响应体
    @ResponseStatus
        定义处理器功能处理方法/异常处理器返回的状态码和原因;
    @ExceptionHandler
        注解式声明异常处理器;
    @PathVariable
        请求URI 中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;

7.Spring3中引入的mvc命名空间
    mvc这个命名空间是在Spring3中引入的,其作用是用来支持mvc的配置
    需要在<bean>中声明出这个命名空间及其对应的schemaLocation中的值
    <mvc:annotation-driven>
        自动注册基于注解风格的处理器和适配器:
        在spring2.5中是DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter

        在spring3中是RequestMappingHandlerMapping和RequestMappingHandlerAdapter.
        同时还支持各种数据的转换器.

    <mvc:interceptors>
        配置自定义的处理器拦截器,例如:
        <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/**"/>
                <ref bean="handlerInterceptor1"/>
            </mvc:interceptor>
        </mvc:interceptors>

    <mvc:view-controller>
        收到相应请求后直接选择相应的视图,例如:
        <mvc:view-controller path="/hello" view-name="test"></mvc:view-controller>

    <mvc:resources>
        逻辑静态资源路径到物理静态资源路径的对应.例如:
        <mvc:resources mapping="/images/**" location="/images/"/>  
        <mvc:resources mapping="/js/**" location="/js/"/>  
        <mvc:resources mapping="/css/**" location="/css/"/> 
    
    <mvc:default-servlet-handler>
        当在web.xml中DispatcherServlet使用<url-pattern>/</url-pattern> 映射的时候,会静态资源也映射了,如果配置了这个mvc标签,那么再访问静态资源的时候就转交给默认的Servlet来响应静态文件,否则报404 找不到静态资源错误。

8.@Controller和@RequestMapping注解
    1)声明处理器
    @Controller
    public class HelloWorldController {
        
    }

    2)映射处理器中的【功能处理方法】
    @Controller
    public class HelloWorldController {
        @RequestMapping("/home")
        public ModelAndView home(){
            ModelAndView mv = new ModelAndView("index");
            return mv;
        }
    }
    
    表明该方法映射的url路径为/home

    3)@RequestMapping也可以写在处理器类上
    @RequestMapping("/test")
    @Controller
    public class HomeController {
        @RequestMapping("/home")
        public ModelAndView home(){
            ModelAndView mv = new ModelAndView("index");
            return mv;
        }
    }
    表明该方法映射的url路径为/test/home
    

    注意:功能处理方法的方法可以是String类型,表示逻辑视图的名字,可以不用返回ModelAndView对象
    例如:
    @Controller
    public class HelloWorldController {
        @RequestMapping("/home")
        public String home(){
            return "index";
        }
    }

9.请求映射
    假设浏览器发送了一个请求如下:
    -------------------------------
    POST /login    HTTP1.1
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,en;q=0.8,zh;q=0.5,en-US;q=0.3
    Connection: keep-alive
    Cookie: JSESSIONID=DBC6367DEB1C024A836F3EA35FCFD5A2
    Host: 127.0.0.1:8989
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0

    username=tom&password=123
    --------------------------------


    http协议的请求格式如下:
    ---------------------------------
    请求方法 URL 协议版本号
    请求头信息
    请求头信息
    请求头信息
    ..
    回车换行
    请求正文
    ---------------------------------

    
    从格式中我们可以看到【请求方法、URL、请求头信息、请求正文】这四部分一般是可变的,
    因此我们可以把请求中的这些信息在处理器的【功能处理方法】中进行的映射,因此请求的映射分为如下几种:
        URL路径映射
            使用URL映射到处理器的功能处理方法;
        请求方法映射限定
            例如限定功能处理方法只处理GET请求;
        请求参数映射限定
            例如限定只处理包含username参数的请求;
        请求头映射限定
            例如限定只处理"Accept=application/json"的请求。


10.URL路径映射
    1)普通URL路径映射
        @RequestMapping(value="/test")
        @RequestMapping("/hello")
            注解中只出现一个参数且参数名为value的话,可以将参数名去掉
        @RequestMapping(value={"/test", "/user/hello"})
            多个URL路径可以映射到同一个处理器的功能处理方法。
    
    2)URI模板模式映射
        @RequestMapping(value="/users/{userId}")
            {XXX}占位符, 请求的URL可以是"/users/123456"或"/users/abcd",之后可以通过@PathVariable可以提取URI模板模式中的{XXX}中的值
        @RequestMapping(value="/users/{userId}/create") 
            这样也是可以的,请求的URL可以是"/users/123/create"
        @RequestMapping(value="/users/{userId}/topics/{topicId}")
            这样也是可以的,请求的URL可以是"/users/123/topics/123"

    3)Ant风格的URL路径映射
        @RequestMapping(value="/users/**")
            可以匹配"/users/abc/abc",但"/users/123"将会被【URI模板模式映射中的"/users/{userId}"模式优先映射到】
        @RequestMapping(value="/product/?")
            可匹配"/product/1"或"/product/a",但不匹配"/product"或"/product/aa";
            ?代表有且只有一个字符
        @RequestMapping(value="/product*")
            可匹配"/productabc"或"/product",但不匹配"/productabc/abc";
            *代表0~n个字符
        @RequestMapping(value="/product/*")
            可匹配"/product/abc",但不匹配"/productabc";
        @RequestMapping(value="/products/**/{productId}")
            可匹配"/products/abc/abc/123"或"/products/123",也就是Ant风格和URI模板变量风格可混用;
            **代表所有的子路径

    4)正则表达式风格的URL路径映射
        从Spring3.0 开始支持正则表达式风格的URL路径映射,格式为{变量名:正则表达式},
        之后通过@PathVariable可以提取{XXX:正则表达式匹配的值}中的XXX这个变量的值。

        @RequestMapping(value="/products/{categoryCode:\\d+}-{pageNumber:\\d+}")
            可以匹配"/products/123-1",但不能匹配"/products/abc-1",这样可以设计更加严格的规则。
        @RequestMapping(value="/user/{userId:^\\d{4}-[a-z]{2}$}")
            可以匹配"/user/1234-ab"
        
        注意:\d表示数字,但是\在java的字符串中是特殊字符,所以需要再加一个\进行转义即可
        (参照之前js的学习文档,和java的正则几乎一致,js正则中的一个/变为java中的俩个/即可)
            括号:
                [abc]     查找方括号之间的任何字符。
                [^abc]     查找任何不在方括号之间的字符。
                [0-9]     查找任何从 0 至 9 的数字。
                [a-z]     查找任何从小写 a 到小写 z 的字符。
                [A-Z]     查找任何从大写 A 到大写 Z 的字符。
                [A-z]     查找任何从大写 A 到小写 z 的字符。
                (red|blue|green)     查找任何指定的选项。
            
            元字符:
                .     查找单个任意字符,除了换行和行结束符.如果要表示.这个字符,需要转义
                \w     查找单词字符。     字母 数字 _
                \W     查找非单词字符。非 字母 数字 _
                \d     查找数字。
                \D     查找非数字字符。
                \s     查找空白字符。
                \S     查找非空白字符。
                \b     匹配单词边界。
                \B     匹配非单词边界。
                \0     查找 NUL 字符。
                \n     查找换行符。
                \f     查找换页符。
                \r     查找回车符。
                \t     查找制表符。
                \v     查找垂直制表符。

            量词:
                n+         匹配任何包含至少一个 n 的字符串。
                n*         匹配任何包含零个或多个 n 的字符串。
                n?         匹配任何包含零个或一个 n 的字符串。
                n{X}     匹配包含 X 个 n 的序列的字符串。
                n{X,Y}     匹配包含 X 到 Y 个 n 的序列的字符串。
                n{X,}     匹配包含至少 X 个 n 的序列的字符串。
                n$         匹配任何结尾为 n 的字符串。
                ^n         匹配任何开头为 n 的字符串。
                ?=n     匹配任何其后紧接指定字符串 n 的字符串。
                ?!n     匹配任何其后没有紧接指定字符串 n 的字符串。


        正则表达式风格的URL路径映射是一种特殊的URI模板模式映射
        URI模板模式映射不能指定模板变量的数据类型,如是数字还是字符串;
        正则表达式风格的URL路径映射,可以指定模板变量的数据类型,可以将规则写的相当复杂。


SpringMVC中的数据验证
    通常在项目中使用较多的是前端校验,比如页面中js校验。对于安全要求较高的建议在服务端同时校验

    SpringMVC使用hibernate的实现的校验框架validation,所以需要导入相关依赖的jar包
        classmate-1.1.0.jar
        hibernate-validator-5.1.3.Final.jar
        jboss-logging-3.1.4.GA.jar
        validation-api-1.1.0.Final.jar
    
    数据校验之后,如果有错误信息,那么需要使用spring提供的标签库中的标签在页面中显示校验信息
    <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>

    例如:
        valid.jsp页面主要代码:
        <sf:form method="post" modelAttribute="teacher">
            <sf:label path="name">用户名:</sf:label> 
            <sf:input path="name"/>
            <sf:errors path="name" cssStyle="color:red"></sf:errors><br>

            <sf:label path="age"> 年   龄:</sf:label>
            <sf:input path="age"/>
            <sf:errors path="age" cssStyle="color:red"></sf:errors><br>

            <sf:label path="dob"> 生   日:</sf:label>  
            <sf:input path="dob"/>
            <sf:errors path="dob" cssStyle="color:red"></sf:errors><br>

            <input type="submit" value="提交"/>
        </sf:form>

        注意:
        1.需要访问一个Controller再跳转到这个页面,同时需要向模型中添加一个名字叫teacher的对象(这就是我们之前说的命令/表单对象),否则跳转到这个页面的时候会报错
        2.表单中没有这种action属性值,那么默认把数据提交给当前页面,但是提交方式是post
        3.input标签中的path属性的值对应的是表单对象中的属性
        4.Controller中映射的url为:/valid/user/add , 如果是get方式访问这个url那么就把valid.jsp显示给用户,如果是post方式访问这个url,就表示要提交表单的数据。
        5.在Controller中,在需要验证的参数前面加入@Valid注解
        6.方法参数列表中,加入BindingResult对象,用来接收验证的错误信息,并根据这个进行不同情况的跳转
        7.在被验证的表单对象所属类中,给需要验证的属性上加入指定注解

        Controller中代码:
        
        @Controller
        @RequestMapping("/valid")
        public class ValidController {
            
            @RequestMapping(value="/user/add", method = {RequestMethod.GET})
            public String test(Model model){
                if(!model.containsAttribute("teacher")){
                    model.addAttribute("teacher", new Teacher());
                }
                return "valid";
            }
            
            @RequestMapping(value="/user/add",method = {RequestMethod.POST})
            public String addTeacher(@Valid Teacher teacher,BindingResult bindingResult){
                //如果验证数据中有错误信息,将保存在bindingResult对象中
                if(bindingResult.hasErrors()){
                    List<ObjectError> errorList = bindingResult.getAllErrors();             
                    for(ObjectError error : errorList){                 
                        System.out.println(error.getDefaultMessage());             
                    }
                    //验证不通过在跳到valid页面,因为页面上有显示错误的标签
                    return "valid";
                }
                
                //没有错误则跳到hello页面
                return "hello";
            }
            
        }

        Teacher类中代码:
        public class Teacher {
    
            private long id;
            @Size(min=5,max=8)
            private String name;
            private Integer age;
            private Date dob;

            get/set
        }
        
    
    常用的数据校验的注解

        @Null        值只能为null
        @NotNull    值不能为null
        @NotEmpty    值不为null且不为空
        @NotBlank    值不为null且不为空(先去除首尾空格)
        @Pattern    正则表达式验证
        @Size        限制长度在x和y之间

        @Max        最大值
        @Min        最小值

        @Future        必须是一个将来的日期(和现在比)
        @Past        必须是一个过去的日期(和现在比)

        @Email        校验email格式

    注意:日期属性上要加@DateTimeFormat(pattern="yyyy-MM-dd"),否则页面传的字符串是不能自动转为为日期的,这个注解既能按照我们要求的格式把String转为Date,又能把Date转为String

    SpringMVC中上传
    使用上传功能需要引入俩个jar包:
        commons-fileupload-1.2.2.jar
        commons-io-2.0.1.jar

    利用spring中提供的MultipartFile接口实现上传功能
    MultipartFile类中两个方法区别:
    getName : 获取表单中文件组件的名字
    getOriginalFilename : 获取上传文件的原名
    transferTo(File newFile);把上传的文件转存到指定文件中


    spring配置文件中加入以下配置:
    <!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 --> 
    <!-- 注意:bean的名字不要改,一定要叫multipartResolver --> 
    <bean name="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 
        <property name="defaultEncoding" value="UTF-8"/> 
        <!-- 指定所上传文件的总大小不能超过指定字节大小 --> 
        <property name="maxUploadSize" value="20000000"/>
    </bean>

    
    jsp页面代码:
    <form action="upload/test" method="post" enctype="multipart/form-data">
        <input type="file" name="file"><br>
        <input type="file" name="file"><br>
        <input type="submit" value="上传">
    </form>


    Controller中的代码:
    @Controller
    @RequestMapping("/upload")
    public class UploadController {
        
        @RequestMapping("/show")
        public String showUploadPage(){
            return "upload";
        }

        @RequestMapping("/test")
        public String upload(@RequestParam("file") MultipartFile[] files, HttpServletRequest request) {
            if (files != null && files.length > 0) {
                for (MultipartFile file : files) {
                    // 保存文件
                    saveFile(request, file);
                }
            }
            // 重定向
            return "redirect:/upload/show";
        }

        private void saveFile(HttpServletRequest request, MultipartFile file) {
            // 判断文件是否为空
            if (!file.isEmpty()) {
                try {
                    //保存的文件路径
                    //需要的话可以给文件名上加时间戳
                    String filePath = request.getServletContext().getRealPath("/") + "upload/"
                            + file.getOriginalFilename();
                    File newFile = new File(filePath);
                    //文件所在目录不存在就创建
                    if (!newFile.getParentFile().exists()){
                        newFile.getParentFile().mkdirs();
                    }

                    // 转存文件
                    file.transferTo(newFile);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }

    }


    注意:在上传文件的同时,还可以接收其他正常的单个的值,例如username、age等,同时也可以把这些单个的值自动封装成User对象

6.SpringMVC中下载
    SpringMVC的下只需要自己设置response信息中的各个部分就可以,可以使用之前学习过的ResponseEntity<T>来完成

    @RequestMapping("/show")
    public String showDownLoadPage(){
        return "download";
    }
    
    @RequestMapping("/test")
    public ResponseEntity<byte[]> test(String fileName,HttpServletRequest request) throws IOException {
        //获得下载文件所在路径 可以指向系统中的任意一个有权访问的路径
        String downLoadPath = request.getServletContext().getRealPath("/download");

        //创建要下载的文件对象
        File file = new File(downLoadPath,fileName);

        //处理一下要下载的文件名字,解决中文乱码
        String downFileName = new String(fileName.getBytes("UTF-8"), "iso-8859-1");

        //创建响应头信息的对象
        HttpHeaders headers = new HttpHeaders();
        //设置下载的响应头信息,通过浏览器响应正文的内容是用户要下载的,不用浏览器解析
        headers.setContentDispositionFormData("attachment", downFileName);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        
        //通过响应内容、响应头信息、响应状态来构建一个响应对象并返回
        return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
    }
    
    页面代码:
    <a href="download/test?fileName=测试.txt">点击下载</a>


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值