一、概述
SpringMVC屬於Spring Framework的后續產品,已經融合到Spring Web Flow中。SpringMVC基於Model2而實現,利用處理器分離了模型對象、視圖、控制,達到了松散耦合的效果,提高了系統的可重用性、可維護性以及可擴展性。其功能與Struts類似,只是實現原理和方式上有所不同。
優點:
- 使用簡單,學習成本低
- 功能強大,很容易寫出性能優秀的程序
- 十分靈活,並且可與Spring無縫銜接
二、框架
SpringMVC框架主要由DispatcherServlet、HandlerMapping、Controller、ViewResolver四個接口組成。
1、前端控制器DispatcherServlet
是整個SpringMVC的核心。從名稱來看,它是一個Servlet,負責統一分發所有請求。
- 攔截符合特定格式的URL請求
- 初始化DispatcherServlet上下文對應的WebApplicationContext,並與業務層、持久化層建立聯系
- 初始化SpringMVC的各個組件,並裝配到DispatcherServlet中
在web.xml文件中進行配置,負責接收HTTP請求、組織協調SpringMVC的各個組成部分。
<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*:/springMVC.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>
如果不指定<param-value>
的值,則默認配置文件為/WEB-INF/<servlet-name>
-servlet.xml。<load-on-startup>
是啟動順序,通常讓Servlet跟隨Servlet容器一起啟動。<url-pattern>
定義要攔截的URL請求。
攔截規則:
*.xxx
,指定要攔截的特定類型,最簡單實用的方式,並且不會攔截靜態文件/
,使用REST風格進行攔截,但是會導致靜態文件被攔截不能正常顯示/*
,不能像Struts那樣使用,會導致不能訪問jsp
如果使用/
進行攔截,並且希望正常訪問靜態文件,可以在DispatcherServlet之前,使用DefaultServlet先攔截特定類型的請求(如:*.js、*.css等)。
2、處理器映射HandlerMapping
負責完成請求到控制器的映射。在servlet的配置文件中,進行uri與控制器的映射。同時,還可以對控制器進行攔截。
SpringMVC默認的處理器映射,直接將uri與實現類進行綁定,書寫方便,但是耦合性高。
<bean id="defaultHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean name="/hello.html" class="com.demo.ssm.HelloController"></bean>
使用SimpleUrlHandlerMapping,將uri與類的id進行綁定,彼此的耦合性低,更加靈活。
<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/hello.html" value-ref="hello"></entry>
</map>
</property>
</bean>
<bean id="hello" class="com.demo.ssm.controller.HelloController"></bean>
對控制器進行攔截,首先聲明攔截器:
<bean id="myInterceptor" class="com.demo.ssm.interceptor.MyInterceptor"></bean>
然后利用SimpleUrlHandlerMapping,映射攔截器與控制器:
<property name="interceptors">
<list>
<ref bean="myInterceptor" />
</list>
</property>
3、控制器Controller
負責處理用戶請求,完成之后返回ModelAndView對象給前端控制器。因為需要考慮並發,所以必須保證線程安全並且可重用。
SpringMVC中的Controller與Struts中的Action基本相同。通過實現Controller接口或繼承父類的方式編寫控制器。
實現Controller接口:
public class HelloController implements Controller {
// 相當於servlet的doGet和doPost方法
public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response) throws Exception {
// 接收數據
// 調用服務層
return new ModelAndView("success","username","sean");
}
}
繼承AbstractController類,該類與接口類似,需要重寫里邊的方法。
繼承MultiActionController類,可以實現多個方法,處理多個請求。
public class MultiController extends MultiActionController {
// 自定義處理請求的方法
public ModelAndView insert(HttpServletRequest request,HttpServletResponse response) throws Exception {
return new ModelAndView("insertSuccess");
}
public ModelAndView update(HttpServletRequest request,HttpServletResponse response) throws Exception {
return new ModelAndView("updateSuccess");
}
}
相應的配置文件:
<bean id="multi" class="com.demo.ssm.controller.MultiController"></bean>
<bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<!-- 使用通配符進行模糊匹配 -->
<entry key="/multi-*.html" value-ref="multi"></entry>
</map>
</property>
</bean>
<bean id="multiActionController" class="org.springframework.web.servlet.mvc.multiaction.MultiActionController">
<property name="methodNameResolver" ref="methodNameResolver"></property>
<property name="delegate">
<ref bean="multi" />
</property>
</bean>
<bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<props>
<prop key="/multi-insert.html">insert</prop>
<prop key="/multi-update.html">update</prop>
</props>
</property>
</bean>
繼承AbstractCommandController類,用於獲取頁面的參數,將參數封裝到指定的對象模型中。
public class CommandController extends AbstractCommandController {
public CommandController() {
// User是用於接收請求參數的數據模型類
this.setCommandClass(User.class);
}
protected ModelAndView handle(HttpServletRequest request,HttpServletResponse response,
Object command, BindException errors) throws Exception {
// command參數是指定的數據模型對象
User user = (User)command;
return new ModelAndView("command");
}
}
4、視圖解析器ViewResolver
負責對ModelAndView對象的解析,並查找對應的View對象。SpringMVC框架默認通過轉發進行頁面跳轉,如果想通過重定向的方式進行跳轉,有以下幾種實現方式。
在控制器中,返回一個RedirectView對象,由對象來指定重定向的URL:
View view = new RedirectView("/index.jsp");
return new ModelAndView(view);
通過配置文件設置,在控制器返回對象的方法中只需要設置一個與bean id一致的字符串即可:
<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver"></bean>
<bean id="index" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="/index.jsp"></property>
</bean>
return new ModelAndView("index");
直接跳轉:
return "redirect:/index.jsp";
如果一個配置文件中出現多個視圖解析器,可以通過設置order屬性來設定優先級。值越低,優先級越高。
三、工作原理
- 將客戶端請求提交給DispatcherServlet
- 根據
<servlet-name>
servlet.xml的配置,查找HandlerMapping - 通過HandlerMapping找到處理請求的具體Controller
- Controller調用業務邏輯處理
- 處理完成之后,返回ModelAndView對象給DispatcherServlet
- 通過ViewResolver找到負責顯示的具體View
- 由View將結果渲染到客戶端
四、常用注解
- @Controller:聲明Action組件,負責注冊bean到Spring上下文
- @RequestMapping:用於為控制器指定可以處理的url請求
- @RequestParam:用於指定參數的name屬性
- @RequestBody:用於讀取Request請求的body部分數據
- @ResponseBody:用於將控制器方法返回的對象寫入到Response對象的body數據區
- @PathVariable:用於指定url作為參數
- @Resource用於注入,( 由j2ee提供 ) 默認按名稱裝配
- @Autowired用於注入,(由spring提供) 默認按類型裝配
- @ExceptionHandler:用於異常處理的方法
- @ControllerAdvice:用於使控制器成為全局的異常處理類
- @ModelAttribute:用於優先調用被注解的方法,或注解參數中的隱藏對象
五、攔截器
Spring提供了HandlerInterceptor接口和HandlerInterceptorAdapter適配器。實現這個接口或繼承此類,就可以實現自己的攔截器。接口HandlerInterceptor包含三個方法,每個方法的參數handler,用來指向下一個攔截器。
- preHandle(),在Action之前執行的預處理,可以進行編碼、安全控制等處理
- postHandle(),在生成View之前執行的后處理,調用了Service並返回ModelAndView,但未進行頁面渲染,可以修改ModelAndView
- afterCompletion(),最后執行的返回處理,這時已經進行了頁面渲染,可以進行日志記錄、釋放資源等處理
在實現了接口之后,就可以在配置文件中進行攔截器的配置。
攔截所有url:
<mvc:interceptors>
<bean class="com.core.mvc.MyInteceptor" />
</mvc:interceptors>
攔截匹配的url:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/*" />
<bean class="com.core.mvc.MyInteceptor" />
</mvc:interceptor>
</mvc:interceptors>
HandlerMapping上的攔截器:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean class="com.core.mvc.MyInteceptor" />
</list>
</property>
</bean>
注意:如果配置了<mvc:annotation-driven />
,會自動注冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter這兩個bean。這樣就不能再注入interceptors屬性,也就無法指定攔截器了。
六、異常處理
可以在配置文件中設置SimpleMappingExceptionResolver,也可以實現HandlerExceptionResolver接口,編寫自己的異常處理。通過exceptionMappings屬性的配置,可以將不同的異常映射到不同的頁面。通過defaultErrorView屬性的配置,可以為所有異常指定一個默認的異常處理頁面。
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView">
<value>/error/error</value>
</property>
<property name="defaultStatusCode">
<value>500</value>
</property>
</bean>
七、獲取Spring管理的Bean
首先在配置文件中添加:
<bean class="com.xxxx.SpringContextHolder" lazy-init="false" />
然后定義一個Spring上下文持有類:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class SpringContextHolder implements ApplicationContextAware {
// 聲明靜態變量, 保證在任何代碼任何地方任何時候能夠取得ApplicaitonContext
private static ApplicationContext applicationContext;
// 實現ApplicationContextAware接口的context注入函數, 將其存入靜態變量
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
// 取得ApplicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
// 從靜態變量ApplicationContext中取得Bean, 自動轉型為所賦值對象的類型
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
@SuppressWarnings("unchecked")
public static <T> T getBean(Class<T> clazz) {
return (T) applicationContext.getBeansOfType(clazz);
}
// 清除applicationContext靜態變量
public static void cleanApplicationContext() {
applicationContext = null;
}
}
參考文章: