您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring MVC的工作流程和使用”,在日常操作中,相信很多人在Spring MVC的工作流程和使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Spring MVC的工作流程和使用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
流程:
(1)首先用戶發送請求——>DispatcherServlet,前端控制器收到請求后自己不進行處理,而是委托給其他的解析器進行處理,作為統一訪問點,進行全局的流程控制;
(2)DispatcherServlet——>HandlerMapping,映射處理器將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象;
(3)DispatcherServlet——>HandlerAdapter,處理器適配器將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持很多類型的處理器;
(4)HandlerAdapter——>調用處理器相應功能處理方法,并返回一個ModelAndView對象(包含模型數據、邏輯視圖名);
(5)ModelAndView對象(Model部分是業務對象返回的模型數據,View部分為邏輯視圖名)——> ViewResolver, 視圖解析器將把邏輯視圖名解析為具體的View;
(6)View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構;
(7)返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。
配置DispatcherServlet
DispatcherServlet是一個Servlet,所以可以配置多個DispatcherServlet。
DispatcherServlet是前置控制器,配置在web.xml文件中的。攔截匹配的請求,Servlet攔截匹配規則要自已定義,把攔截下來的請求,依據某某規則分發到目標Controller(我們寫的Action)來處理。
web.xml:
<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-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
這里主要有點需要注意:
(1)classpath:springmvc-servlet.xml 用于加載類路徑下的springmvc配置文件,文件名可以自定義。如果不定義<init-param>標簽則默認加載配置文件的路徑是WEB-INF下。
(2)<url-pattern>/</url-pattern>表示所有請求都會被過濾。
springmvc-servlet.xml:
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 默認的注解映射的支持 --> <mvc:annotation-driven /> <!-- 靜態資源加載 --> <mvc:resources location="/statics/" mapping="/statics/**" /> <!-- 掃包 --> <context:component-scan base-package="cn.xxxx.controller"/> <!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 全局異常處理 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="cn.xxxx.exception.LoginException">error</prop> </props> </property> </bean> </beans>
<context:component-scan/> 掃描指定的包中的類上的注解,常用的注解有:
@Controller 聲明Action組件
@Service 聲明Service組件 @Service("myMovieLister")
@Repository 聲明Dao組件
@Component 泛指組件, 當不好歸類時.
@RequestMapping("/menu") 請求映射
@Resource 用于注入,( j2ee提供的 ) 默認按名稱裝配,@Resource(name="beanName")
@Autowired 用于注入,(srping提供的) 默認按類型裝配
@Transactional( rollbackFor={Exception.class}) 事務管理
@ResponseBody
@Scope("prototype") 設定bean的作用域
<mvc:annotation-driven /> 是一種簡寫形式,完全可以手動配置替代這種簡寫形式,簡寫形式可以讓初學都快速應用默認配置方案。<mvc:annotation-driven /> 會自動注冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,是spring MVC為@Controllers分發請求所必須的。
并提供了:數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,讀寫XML的支持(JAXB),讀寫JSON的支持(Jackson)。
Spring 統一異常處理有 3 種方式,分別為:
使用 @ ExceptionHandler 注解
實現 HandlerExceptionResolver 接口
使用 @controlleradvice 注解
使用該注解有一個不好的地方就是:進行異常處理的方法必須與出錯的方法在同一個Controller里面。使用如下:
@Controller public class GlobalController { /** * 用于處理異常的 * @return */ @ExceptionHandler({MyException.class}) public String exception(MyException e) { System.out.println(e.getMessage()); e.printStackTrace(); return "exception"; } @RequestMapping("test") public void test() { throw new MyException("出錯了!"); } }
可以看到,這種方式最大的缺陷就是不能全局控制異常。每個類都要寫一遍。
這種方式可以進行全局的異常控制。例如:
@Component public class ExceptionTest implements HandlerExceptionResolver{ /** * TODO 簡單描述該方法的實現功能(可選). * @see org.springframework.web.servlet.HandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception) */ public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { System.out.println("This is exception handler method!"); return null; } }
上文說到 @ ExceptionHandler 需要進行異常處理的方法必須與出錯的方法在同一個Controller里面。那么當代碼加入了 @ControllerAdvice,則不需要必須在同一個 controller 中了。這也是 Spring 3.2 帶來的新特性。從名字上可以看出大體意思是控制器增強。 也就是說,@controlleradvice + @ ExceptionHandler 也可以實現全局的異常捕捉。
請確保此WebExceptionHandle 類能被掃描到并裝載進 Spring 容器中。
@ControllerAdvice @ResponseBody public class WebExceptionHandle { private static Logger logger = LoggerFactory.getLogger(WebExceptionHandle.class); /** * 400 - Bad Request */ @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { logger.error("參數解析失敗", e); return ServiceResponseHandle.failed("could_not_read_json"); } /** * 405 - Method Not Allowed */ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ServiceResponse handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { logger.error("不支持當前請求方法", e); return ServiceResponseHandle.failed("request_method_not_supported"); } /** * 415 - Unsupported Media Type */ @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ServiceResponse handleHttpMediaTypeNotSupportedException(Exception e) { logger.error("不支持當前媒體類型", e); return ServiceResponseHandle.failed("content_type_not_supported"); } /** * 500 - Internal Server Error */ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ExceptionHandler(Exception.class) public ServiceResponse handleException(Exception e) { if (e instanceof BusinessException){ return ServiceResponseHandle.failed("BUSINESS_ERROR", e.getMessage()); } logger.error("服務運行異常", e); e.printStackTrace(); return ServiceResponseHandle.failed("server_error"); } }
如果 @ExceptionHandler 注解中未聲明要處理的異常類型,則默認為參數列表中的異常類型。所以還可以寫成這樣:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler() @ResponseBody String handleException(Exception e){ return "Exception Deal! " + e.getMessage(); } }
繼承 ResponseEntityExceptionHandler 類來實現針對 Rest 接口 的全局異常捕獲,并且可以返回自定義格式:
@Slf4j @ControllerAdvice public class ExceptionHandlerBean extends ResponseEntityExceptionHandler { /** * 數據找不到異常 * @param ex * @param request * @return * @throws IOException */ @ExceptionHandler({DataNotFoundException.class}) public ResponseEntity<Object> handleDataNotFoundException(RuntimeException ex, WebRequest request) throws IOException { return getResponseEntity(ex,request,ReturnStatusCode.DataNotFoundException); } /** * 根據各種異常構建 ResponseEntity 實體. 服務于以上各種異常 * @param ex * @param request * @param specificException * @return */ private ResponseEntity<Object> getResponseEntity(RuntimeException ex, WebRequest request, ReturnStatusCode specificException) { ReturnTemplate returnTemplate = new ReturnTemplate(); returnTemplate.setStatusCode(specificException); returnTemplate.setErrorMsg(ex.getMessage()); return handleExceptionInternal(ex, returnTemplate, new HttpHeaders(), HttpStatus.OK, request); } }
1、@Controller 只是定義了一個控制器類,而使用@RequestMapping 注解的方法才是真正處理請求的處理器
需要我們把這個控制器類交給Spring 來管理。有兩種方式:
(1)在SpringMVC 的配置文件中定義MyController 的bean 對象。
(2)在SpringMVC 的配置文件中告訴Spring 該到哪里去找標記為@Controller 的Controller 控制器。
<!--方式一--> <bean class="com.host.app.web.controller.MyController"/> <!--方式二--> < context:component-scan base-package = "com.host.app.web" />//路徑寫到controller的上一層(掃描包詳解見下面淺析)
2、@RequestMapping
RequestMapping是一個用來處理請求地址映射的注解,可用于類或方法上。用于類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。
RequestMapping注解有六個屬性,下面我們把她分成三類進行說明(下面有相應示例)。
2.1、 value, method;
value: 指定請求的實際地址,指定的地址可以是URI Template 模式(后面將會說明);
method: 指定請求的method類型, GET、POST、PUT、DELETE等;
2.2、consumes,produces
consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;
produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;
2.3、params,headers
params: 指定request中必須包含某些參數值是,才讓該方法處理。
headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。
@Resource和@Autowired都是做bean的注入時使用,其實@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要導入,但是Spring支持該注解的注入。
3.1、共同點
兩者都可以寫在字段和setter方法上。兩者如果都寫在字段上,那么就不需要再寫setter方法。
3.2、不同點
(1)@Autowired
@Autowired為Spring提供的注解,需要導入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
public class TestServiceImpl { // 下面兩種@Autowired只要使用一種即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) { // 用于屬性的方法上 this.userDao = userDao; } }
@Autowired注解是按照類型(byType)裝配依賴對象,默認情況下它要求依賴對象必須存在,如果允許null值,可以設置它的required屬性為false。如果我們想使用按照名稱(byName)來裝配,可以結合@Qualifier注解一起使用。如下:
public class TestServiceImpl { @Autowired @Qualifier("userDao") private UserDao userDao; }
(2)@Resource
@Resource默認按照ByName自動注入,由J2EE提供,需要導入包javax.annotation.Resource。@Resource有兩個重要的屬性:name和type,而Spring將@Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。所以,如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不制定name也不制定type屬性,這時將通過反射機制使用byName自動注入策略。
public class TestServiceImpl { // 下面兩種@Resource只要使用一種即可 @Resource(name="userDao") private UserDao userDao; // 用于字段上 @Resource(name="userDao") public void setUserDao(UserDao userDao) { // 用于屬性的setter方法上 this.userDao = userDao; } }
注:最好是將@Resource放在setter方法上,因為這樣更符合面向對象的思想,通過set、get去操作屬性,而不是直接去操作屬性。
@Resource裝配順序:
①如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常。
②如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常。
③如果指定了type,則從上下文中找到類似匹配的唯一bean進行裝配,找不到或是找到多個,都會拋出異常。
④如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配。
@Resource的作用相當于@Autowired,只不過@Autowired按照byType自動注入。
代表的是:該Controller的所有方法在調用前,先執行此@ModelAttribute方法,可用于注解和方法參數中,可以把這個@ModelAttribute特性,應用在BaseController當中,所有的Controller繼承BaseController,即可實現在調用Controller時,先執行@ModelAttribute方法。最主要的作用是將數據添加到模型對象中,用于視圖頁面展示時使用。
@SessionAttributes即將值放到session作用域中,寫在class上面。
用于將請求URL中的模板變量映射到功能處理方法的參數上,即取出uri模板中的變量作為參數。如:
@requestParam主要用于在SpringMVC后臺控制層獲取參數,類似一種是request.getParameter("name"),它有三個常用參數:defaultValue = "0", required = false, value = "isApp";defaultValue 表示設置默認值,required 銅過boolean設置是否是必須要傳入的參數,value 值表示接受的傳入的參數名稱。
作用: 該注解用于將Controller的方法返回的對象,通過適當的HttpMessageConverter轉換為指定格式后,寫入到Response對象的body數據區。
使用時機:返回的數據不是html標簽的頁面,而是其他某種格式的數據時(如json、xml等)使用;
相當于通用的注解,當不知道一些類歸到哪個層時使用,但是不建議。
用于注解dao層,在daoImpl類上面注解。
方式一、通過常見的類路徑和方法路徑結合訪問controller方法
方式二、使用uri模板
@Controller @RequestMapping ( "/test/{variable1}" ) public class MyController { @RequestMapping ( "/showView/{variable2}" ) public ModelAndView showView( @PathVariable String variable1, @PathVariable ( "variable2" ) int variable2) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName( "viewName" ); modelAndView.addObject( " 需要放到 model 中的屬性名稱 " , " 對應的屬性值,它是一個對象 " ); return modelAndView; } }
除了在請求路徑中使用URI 模板,定義變量之外,@RequestMapping 中還支持通配符“* ”。如下面的代碼我就可以使用/myTest/whatever/wildcard.do 訪問到Controller 的testWildcard 方法。如:
@Controller @RequestMapping ( "/myTest" ) public class MyController { @RequestMapping ( "*/wildcard" ) public String testWildcard() { System. out .println( "wildcard------------" ); return "wildcard" ; } }
當@RequestParam中沒有指定參數名稱時,Spring 在代碼是debug 編譯的情況下會默認取更方法參數同名的參數,如果不是debug 編譯的就會報錯。
(1)params屬性
@RequestMapping (value= "testParams" , params={ "param1=value1" , "param2" , "!param3" }) public String testParams() { System. out .println( "test Params..........." ); return "testParams" ; }
用@RequestMapping 的params 屬性指定了三個參數,這些參數都是針對請求參數而言的,它們分別表示參數param1 的值必須等于value1 ,參數param2 必須存在,值無所謂,參數param3 必須不存在,只有當請求/testParams.do 并且滿足指定的三個參數條件的時候才能訪問到該方法。所以當請求/testParams.do?param1=value1¶m2=value2 的時候能夠正確訪問到該testParams 方法,當請求/testParams.do?param1=value1¶m2=value2¶m3=value3 的時候就不能夠正常的訪問到該方法,因為在@RequestMapping 的params 參數里面指定了參數param3 是不能存在的。
(2)method屬性
@RequestMapping (value= "testMethod" , method={RequestMethod. GET , RequestMethod. DELETE }) public String testMethod() { return "method" ; }
在上面的代碼中就使用method 參數限制了以GET 或DELETE 方法請求/testMethod 的時候才能訪問到該Controller 的testMethod 方法。
(3)headers屬性
@RequestMapping (value= "testHeaders" , headers={ "host=localhost" , "Accept" }) public String testHeaders() { return "headers" ; }
headers 屬性的用法和功能與params 屬性相似。在上面的代碼中當請求/testHeaders.do 的時候只有當請求頭包含Accept 信息,且請求的host 為localhost 的時候才能正確的訪問到testHeaders 方法
(1 )HttpServlet 對象,主要包括HttpServletRequest 、HttpServletResponse 和HttpSession 對象。 這些參數Spring 在調用處理器方法的時候會自動給它們賦值,所以當在處理器方法中需要使用到這些對象的時候,可以直接在方法上給定一個方法參數的申明,然后在方法體里面直接用就可以了。但是有一點需要注意的是在使用HttpSession 對象的時候,如果此時HttpSession 對象還沒有建立起來的話就會有問題。
(2 )Spring 自己的WebRequest 對象。 使用該對象可以訪問到存放在HttpServletRequest 和HttpSession 中的屬性值。
(3 )InputStream 、OutputStream 、Reader 和Writer 。 InputStream 和Reader 是針對HttpServletRequest 而言的,可以從里面取數據;OutputStream 和Writer 是針對HttpServletResponse 而言的,可以往里面寫數據。
(4 )使用@PathVariable 、@RequestParam 、@CookieValue 和@RequestHeader 標記的參數。
(5 )使用@ModelAttribute 標記的參數。
(6 )java.util.Map 、Spring 封裝的Model 和ModelMap 。 這些都可以用來封裝模型數據,用來給視圖做展示。
(7 )實體類。 可以用來接收上傳的參數。
(8 )Spring 封裝的MultipartFile 。 用來接收上傳文件的。
(9 )Spring 封裝的Errors 和BindingResult 對象。 這兩個對象參數必須緊接在需要驗證的實體對象參數之后,它里面包含了實體對象的驗證結果。
(1 )一個包含模型和視圖的ModelAndView 對象。
(2 )一個模型對象,這主要包括Spring 封裝好的Model 和ModelMap ,以及java.util.Map ,當沒有視圖返回的時候視圖名稱將由RequestToViewNameTranslator 來決定。
(3 )一個View 對象。這個時候如果在渲染視圖的過程中模型的話就可以給處理器方法定義一個模型參數,然后在方法體里面往模型中添加值。
(4 )一個String 字符串。這往往代表的是一個視圖名稱。這個時候如果需要在渲染視圖的過程中需要模型的話就可以給處理器方法一個模型參數,然后在方法體里面往模型中添加值就可以了。
(5 )返回值是void 。這種情況一般是我們直接把返回結果寫到HttpServletResponse 中了,如果沒有寫的話,那么Spring 將會利用RequestToViewNameTranslator 來返回一個對應的視圖名稱。如果視圖中需要模型的話,處理方法與返回字符串的情況相同。
(6 )如果處理器方法被注解@ResponseBody 標記的話,那么處理器方法的任何返回類型都會通過HttpMessageConverters 轉換之后寫到HttpServletResponse 中,而不會像上面的那些情況一樣當做視圖或者模型來處理。
(7 )除以上幾種情況之外的其他任何返回類型都會被當做模型中的一個屬性來處理,而返回的視圖還是由RequestToViewNameTranslator 來決定,添加到模型中的屬性名稱可以在該方法上用@ModelAttribute(“attributeName”) 來定義,否則將使用返回類型的類名稱的首字母小寫形式來表示。使用@ModelAttribute 標記的方法會在@RequestMapping 標記的方法執行之前執行。
請求路徑上有個id的變量值,可以通過@PathVariable來獲取 @RequestMapping(value = "/page/{id}", method = RequestMethod.GET)
@RequestParam用來獲得靜態的URL請求入參 spring注解時action里用到。
handler method 參數綁定常用的注解,我們根據他們處理的Request的不同內容部分分為四類:(主要講解常用類型)
A、處理requet uri 部分(這里指uri template中variable,不含queryString部分)的注解: @PathVariable;
B、處理request header部分的注解: @RequestHeader, @CookieValue;
C、處理request body部分的注解:@RequestParam, @RequestBody;
D、處理attribute類型是注解: @SessionAttributes, @ModelAttribute;
當使用@RequestMapping URI template 樣式映射時, 即 someUrl/{paramId}, 這時的paramId可通過 @Pathvariable注解綁定它傳過來的值到方法的參數上。
示例代碼:
@Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } }
到此,關于“Spring MVC的工作流程和使用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。