之前一直想学 springMVC ,拖阿托的没学成,后来项目里用了,但那是架构师已经搭建好的,也就没有再去学,想着会用就能够了。事实证实,我仍是太年轻,虽然会用就好,可是,面试官会问啊 ! ! !。两个月以前找工做,好多面试官都问 spring MVC 相关的东西。例如:若是要解析多种视图应该如何配置? springMVC中的事物怎么配置?还有其余的一些相关问题,而后我都说不会,是架构师已经配置好的。好尴尬啊。javascript
那就从零开始学起吧。css
首先下载所须要的 jar 包,并放入 lib 中。html
jar 包连接:http://pan.baidu.com/s/1qYQ0U3ijava
而后在 web.xml中进行配置。程序员
<servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置Spring mvc下的配置文件的位置和名称 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring_mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
接着就要配置 spring_mvc.xml 这个 配置文件了。web
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置自动扫描的包 扫描 该包下面全部 有 @Controller 注解的类--> <context:component-scan base-package="com.ding.controller"/> <!-- 配置视图解析器 如何把handler 方法返回值解析为实际的物理视图 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> </bean> <mvc:annotation-driven/> </beans>
先配置须要扫描的 controller包,再配置 返回的视图解析器。面试
我配置的试图解析器的含义:若是 一个方法 最终返回是这样 : return "result" 。那么试图解析器就回去找/Web-INF/view/result.jsp,找到就顺利解析,找不到就404.如今通常视图都放在 Web-INF下面,由于这个包是受保护的,经过链接没法直接对里面的文件进行访问。ajax
接着就是建立 controller 了。因为我上面配置的时候,须要扫面的 controller包 的全称是 com.ding.controller,所以须要在这个包下面进行建立。spring
类上面的 @RequestMapping("") 能够不写,若是不写,那就直接访问方法上的 路径,可是通常状况下都会写,由于项目里要分不少模块,每一个模块都有本身的命名,写了以后更容易区分和管理。spring-mvc
这里我先写了个BaseController,等下详细介绍。
如今启动项目,并进行访问:http://localhost:8080/base_project/ding/test.do
如今已经成功了。最简单的 springMVC项目已经搭建成功。开始介绍 个人 BaseController .
在项目中, HttpServletRequest,HttpServletResponse,HttpSession 这3个东西是常常要用的到,难道每次都本身手写?太累了吧。因此,咱们写一个类,让这个类来建立,子类继承这个类,直接使用这3个变量就能够。
package com.ding.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; @Controller public class BaseController { protected HttpServletRequest request; protected HttpServletResponse response; protected HttpSession session; @ModelAttribute public void exc(HttpServletRequest request,HttpServletResponse response){ this.request=request; this.response=response; this.session=request.getSession(); } }
这样子以后,每次一进方法,就能够直接使用 request ,response ,session。简便不少。
@ModelAttribute的含义:被@ModelAttribute注释的方法会在此controller每一个方法执行前被执行,也就是说,你在调用本身须要的方法前,他就给你初始化好 request ,response ,session。
慢慢再深刻,如今我要是访问静态资源,例如 js,css这些办?据我所知有两种,第一种:
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.gif</url-pattern> </servlet-mapping> <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>*.html</url-pattern> </servlet-mapping>
在 web.xml中添加以上配置,就能够了。但我感受仍是不太好,因此我喜欢第二种:
在 spring_mvc.xml 中多加一个配置:
<!--对静态资源文件的访问--> <mvc:resources mapping="/statics/**" location="/statics/" />
这样就能够了。statics包专门用于存放静态资源。
上面说到的,若是须要解析多种视图,那该怎么办?
这就要用到 order 属性了。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/view/"></property> <property name="suffix" value=".jsp"></property> <property name="order" value="10"></property> </bean>
order值 越大,越排在后面。就是小的先来,大的排队。
很重要的一点:InternalResourceViewResolver 必须优先级最低,这固然是有缘由的:
当处理器返回逻辑视图时(也就是return “string”),要通过视图解析器链,前面的解析器能处理的,就不会继续往下传播。
若是不能处理就要沿着解析器链继续寻找,直到找到合适的视图解析器(归纳为:能解析的,不继续往下找,不能解析的,要继续往下找解析器)。
当处理器(@controller)返回的逻辑视图解析过程:
当通过视图解析器1时,若是能解析就解析并且不会再继续往下。若是不能执行就返回null,这样下面的解析器才能处理。
可是对于解析器InternalResourceViewResolver来讲,无论能不能解析它都不会返回null,也就是说它拦截了全部的逻辑视图,
让后续的解析器得不到执行,因此InternalResourceViewResolver必须放在最后。
记住一点:不能解析就返回null,这样后续解析器才能解析。
接着就是 spring MVC 的自定义拦截器了。它能够作不少东西,最简单的最多见的就是 登录拦截。
要想实现自定义拦截,须要 咱们写的类 实现 HandlerInterceptor 接口 或者 继承 HandlerInterceptorAdapter 类。
先说 实现 HandlerInterceptor 接口的写法:
package com.ding.intercepter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor{ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)throws Exception { } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaa"); return true; } }
实现接口以后,分别须要实现3个方法 分别是 perHandle , afterCompletion , postHandle
preHandle():这个方法在业务处理器处理请求以前被调用,在该方法中对用户请求request进行处理。若是程序员决定该拦截器对请求进行拦截处理后还要调用其余的拦截器,或者是业务处理器去进行处理,则返回true;若是程序员决定不须要再调用其余的组件去处理请求,则返回false。
postHandle():这个方法在业务处理器处理完请求后,可是DispatcherServlet向客户端返回请求前被调用,在该方法中对用户请求request进行处理。
afterCompletion():这个方法在DispatcherServlet彻底处理完请求后被调用,能够在该方法中进行一些资源清理的操做。
通常状况下,咱们都只用 preHandle 方法,其余两个方法不多使用。return false 表示,请求被中断,不会继续去访问 controller了。
下面给出一个简化版的登陆拦截:
package com.ding.intercepter; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class LoginInterceptor implements HandlerInterceptor{ private static String AJAX_TIME_OUT = null; static{ AJAX_TIME_OUT = "ajaxUserTimeout"; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)throws Exception { } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView arg3)throws Exception { } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { String path = request.getServletPath(); if(path.indexOf(".shtml")<0){// test/ding 不须要登陆操做,因此不拦截 return true; }else{//test/login.shtml 须要登陆操做,因此拦截 HttpSession session=request.getSession(); String user=(String) session.getAttribute("user"); if(user==null || "".equals(user)){ String type = request.getHeader("X-Requested-With"); if(type!=null && ! "".equals(type) && type.equalsIgnoreCase("XMLHttpRequest")){//ajax请求,在页面输出错误信息 PrintWriter printWriter = response.getWriter(); printWriter.print(AJAX_TIME_OUT); printWriter.flush(); printWriter.close(); }else{ response.sendRedirect("");//跳转到 登陆那里 } return false; }else{ return true; } } } }
在 jqery 中添加以下代码,捕获 ajaxUserTimeout:
$(document).on('ajaxError',function(event, request, options){ var data = request.responseText; if(data == 'ajaxUserTimeout'){ window.top.location.href= ''; } });
这样就拦截成功了。固然,还须要在 spring_mvc.xml中进行配置:
<mvc:interceptors> <mvc:interceptor> <!-- /** 表示全部的url,包括子url路径 --> <mvc:mapping path="/**"/> <bean class="com.ding.intercepter.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
接着说说 继承 HandlerInterceptorAdapter 类的写法。其实差很少,可是当我继承以后,没有错误提示,要我本身去写
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { }
感受麻烦了点。里面的内容到是差不错的。
那么新的问题就来了,若是某些请求我不要拦截器拦截呢?应该怎么办?
如今的要求是不通过拦截器了,那就要有另外的配置。
<mvc:exclude-mapping path="/ding/**"/>,他放在 <mvc:mapping path="/**" /> 就能够。
<mvc:interceptors> <mvc:interceptor> <!-- /** 表示全部的url,包括子url路径 --> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/test/**"/> <bean class="com.ding.intercepter.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
如今, http://localhost:8080/base_project/test/xiao 这个路径就不通过登陆拦截器了,直接访问 controller。
在项目中咱们常常要用到文件上传,springMVC本身就已经作好了,咱们配置一下就能够用。
要使用这个文件上传功能,须要先引入2个 jar 包。
commons-fileupload-1.3.1.jar
commons-io-2.4.jar
这两个已经在上面 jar 的压缩包里面了,无需重复下载。
我本身在用的时候,一开始没有下载并放进去,把这段配置放进去,一请求就报错。
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8" /> <!-- 编码类型 --> <property name="maxUploadSize" value="1048576000000" /> <!-- 上传的文件的最大size --> <property name="maxInMemorySize" value="40960" /> <!-- 在内存中所占内存的最大值 --> </bean>
在 java程序中:
//单文件上传 @RequestMapping("fileUpload") public String fileUpload(@RequestParam("file") CommonsMultipartFile file) throws IOException { File newFile=new File("D:/a.txt");//目标文件 file.transferTo(newFile);//经过CommonsMultipartFile的方法直接写文件(注意这个时候) return ""; }
//多文件上传 @RequestMapping("fileUpload") public String fileUpload(){ MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request; Map<String, MultipartFile> files = multiRequest.getFileMap(); Iterator<Entry<String, MultipartFile>> iter = files.entrySet().iterator(); try { while (iter.hasNext()) { Map.Entry<java.lang.String, MultipartFile> entry = (Map.Entry<String, MultipartFile>) iter.next(); String name = entry.getValue().getOriginalFilename(); File writeFile = new File(""); CommonsMultipartFile file = (CommonsMultipartFile) files.get(entry.getKey()); if(!file.isEmpty()){ file.getFileItem().write(writeFile); } } } catch (Exception e) { e.printStackTrace(); } return ""; }
总的来讲就两种方式:
file.getFileItem().write(writeFile); file.transferTo(writeFile);
具体哪一种上传效率高,还真没试过。不过用的一直是 .wtite()方法
我在上传的时候,文件中的中文乱码了,因此须要解决。
最简单的:在web.xml中添加以下配置。
<filter> <filter-name>Encoding</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> </filter> <filter-mapping> <filter-name>Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
全部请求都用utf-8 编码。
想了下,springMVC还有很多东西,例如: aop,事物的配置,springMVC与 EhCache的集成,restful,统一异常处理,quartz与 springMVC的集成,mybatis与springMVC的集成。等我先研究一下,弄懂怎么配置以后再来写。