SpringMVC 快速入门
核心类
DispatcherServlet 前端控制器,一个Servlet,配置在web.xml中初始化,所有的请求由它来分发给其他Controller
<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/springMvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.shtml</url-pattern> </servlet-mapping>
默认在DispatcherServlet的初始化过程中,框架会在web应用的 WEB-INF文件夹下寻找名为[servlet-name]-servlet.xml 的配置文件,生成文件中定义的bean,由spring统一管理,这里我们自定义为springMvc.xml,并匹配以shtml后缀的请求。
具体匹配有多种方式
1./WEB-INF/<servlet-name>-servlet.xml
2.WEB-INF/classes/springMVC.xml
3.classpath*:springmvc-mvc.xml
4.classpath*: springmvc*.xml
匹配规则
一般模式
接受定义为*.do ,*.jsp,*.html,*.shtml等请求,如上述配置*.shtml,这是最传统的方式,当然不会拦截js,css,jpg等静态文件
REST风格
接受如ProductDetail/xxxxxx Product/add 等请求
视图解析器
视图解析器主要是对视图中的Model数据的解释,如PDF EXCEL 等 ,在jsp中常用的视图解析器是InternalResourceViewResovler,它会要求一个前缀和一个后缀
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/page/" p:suffix=".jsp" />
属性编辑器
表单数据的自动装入,但一些特殊情况下 如 整型(1355xx),new Date() ,外键对象 就需要自定义编辑器来处理数据。自定义springMVC的属性编辑器主要有两种方式,一种是使用@InitBinder标签在运行期注册一个属性编辑器,这种编辑器只在当前Controller里面有效;还有一种是实现自己的 WebBindingInitializer,然后定义一个
AnnotationMethodHandlerAdapter的bean,在此bean里面进行注册
,这种属性编辑器是全局的。
@InitBinder
如对日期格式的处理,只对当前Controller有效
@InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Date.class, new CustomDateEditor( new SimpleDateFormat("yyyy-MM-dd"), true)); }
PropertyEditor 是一个接口,要实现自己的 PropertyEditor 类我们可以实现这个接口,然后实现里面的方法。但是 PropertyEditor 里面定义的方法太多了,这样做比较麻烦。在 java 中有一个封装类是实现了 PropertyEditor 接口的,它是 PropertyEditorSupport 类。所以如果需要实现自己的 PropertyEditor 的时候只需要继承 PropertyEditorSupport 类,然后重写其中的一些方法。一般就是重写 setAsText 和 getAsText 方法就可以了, setAsText 方法是用于把字符串类型的值转换为对应的对象的,而 getAsText 方法是用于把对象当做字符串来返回的。在 setAsText 中我们一般先把字符串类型的对象转为特定的对象,然后利用 PropertyEditor 的 setValue 方法设定转换后的值。在 getAsText 方法中一般先使用 getValue 方法取代当前的对象,然后把它转换为字符串后再返回给 getAsText 方法。
@InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Date.class, new CustomDateEditor( new SimpleDateFormat("yyyy-MM-dd"), false)); // 定义一个 User 属性编辑器 PropertyEditor userEditor = new PropertyEditorSupport() { @Override public String getAsText() { User user = (User) getValue(); return user.getDepart().getName(); } @Override public void setAsText(String text) throws IllegalArgumentException { setValue(text); } }; // 使用 WebDataBinder 注册 User 类型的属性编辑器 binder.registerCustomEditor(User.class, userEditor); }
WebBindingInitializer
如果需要定义全局的类型转换器就需要实现自己的 WebBindingInitializer 对象,然后把该对象注入到 AnnotationMethodHandlerAdapter 中,这样 Spring 在遇到自己不能解析的对象的时候就会到全局的 WebBindingInitializer 的 initBinder 方法中去找,每次遇到不认识的对象时, initBinder 方法都会被执行一遍。
public class MyWebBindingInitializer implements WebBindingInitializer { @Override public void initBinder(WebDataBinder binder, WebRequest request) { binder.registerCustomEditor(Date.class, new CustomDateEditor( new SimpleDateFormat("yyyy-MM-dd"), true)); } }
定义WebBindingInitializer 对象之后 Spring 还是不能识别其中指定的对象,这是因为我们只是定义WebBindingInitializer 对象,还没有把它交给 Spring,Spring 不知道该去哪里找解析器。要让 Spring 能够识别还需要我们在 SpringMVC 的配置文件中定义一个 AnnotationMethodHandlerAdapter 类型的 bean 对象,然后利用自己定义的 WebBindingInitializer 覆盖它的默认属性 webBindingInitializer。
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="cacheSeconds" value="0" /> <property name="webBindingInitializer"> <bean class="cn.base.web.initializer.MyWebBindingInitializer" /> </property> </bean>
拦截器
SpringMVC 中的Interceptor 拦截器是相当重要,它的主要作用是拦截用户的请求并进行相应的处理,比如通过它来进行权限验证,全局日志的控制等。
Interceptor
SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
public class LoginInterceptor implements HandlerInterceptor { @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { // TODO Auto-generated method stub } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { // TODO Auto-generated method stub } @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { // TODO Auto-generated method stub return false; }
HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
1.preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
2.postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2 里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。
3.afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
WebRequestInterceptor
WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,那么这个WebRequest 是什么呢?这个WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。
public class MyWebRequestInterceptor implements WebRequestInterceptor { @Override public void afterCompletion(WebRequest arg0, Exception arg1) throws Exception { // TODO Auto-generated method stub } @Override public void postHandle(WebRequest arg0, ModelMap arg1) throws Exception { // TODO Auto-generated method stub } @Override public void preHandle(WebRequest arg0) throws Exception { // TODO Auto-generated method stub } }
1.preHandle(WebRequest request) 方法。该方法将在请求处理之前进行调用,也就是说会在Controller 方法调用之前被调用。这个方法跟HandlerInterceptor 中的preHandle 是不同的,主要区别在于该方法的返回值是void ,也就是没有返回值,所以我们一般主要用它来进行资源的准备工作,比如我们在使用Hibernate 的时候可以在这个方法中准备一个Hibernate 的Session 对象,然后利用WebRequest 的setAttribute(name, value, scope) 把它放到WebRequest 的属性中。这里可以说说这个setAttribute 方法的第三个参数scope ,该参数是一个Integer 类型的。在WebRequest 的父层接口RequestAttributes 中对它定义了三个常量:
SCOPE_REQUEST :它的值是0 ,代表只有在request 中可以访问。
SCOPE_SESSION :它的值是1 ,如果环境允许的话它代表的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。
SCOPE_GLOBAL_SESSION :它的值是2 ,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session 范围内可以访问。
3.afterCompletion(WebRequest request, Exception ex) 方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest 参数就可以把我们在preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在Controller 中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null 。
mvc:interceptors标签
mvc标签中有一个mvc:interceptors是用于声明SpringMVC的拦截器,利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,postHandle与afterCompletion方法后执行。。
<mvc:interceptors> <!-- 直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 --> <bean class="cn.base.web.Interceptor.LoginInterceptor" /> <mvc:interceptor> <mvc:mapping path="/admin/" /> <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 --> <bean class="cn.base.web.Interceptor.MyWebRequestInterceptor" /> </mvc:interceptor> </mvc:interceptors>
文件上传
没有什么好说的,导入包common-upload 与common-io包
配置
<!-- 文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- one of the properties available; the maximum file size in bytes --> <property name="maxUploadSize" value="-1" /> <property name="defaultEncoding" value="UTF-8"></property> <property name="maxInMemorySize" value="10240"></property> <property name="uploadTempDir" value="/WEB-INF/upload/uploadTempDir"></property> </bean>
代码
@RequestMapping("/user_add.shtml") public String user_add(String username, MultipartHttpServletRequest mutiRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { String date = request.getParameter("adddate"); System.out.println("username : " + username + "adddate : " + date); MultipartFile uploadFile = mutiRequest.getFile("photo"); if (uploadFile != null) { String filename = uploadFile.getOriginalFilename(); InputStream in = uploadFile.getInputStream(); int len = 0; byte buffer[] = new byte[1024]; FileOutputStream out = new FileOutputStream(request.getRealPath("/WEB-INF/upload") + "\\" + filename); while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } out.flush(); out.close(); in.close(); logger.info("文件名称:[" + filename + "]: 上传成功啦!"); } return "/user/user_add"; }
JSON视图
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!-- 配置JSON视图解析--> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean> <bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean> <!-- 配置JSP视图解析。prefix:前缀, suffix:后缀 --> <bean id="jspView" class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/page/" p:suffix=".jsp" /> <!-- 扫描注解类 --> <context:component-scan base-package="cn.base.web.controller" /> <context:component-scan base-package="cn.base.web.dao.impl" /> <context:component-scan base-package="cn.base.web.service.impl" /> </beans>
异常处理
数据验证
国际化