springmvc框架原理及搭建
1、底層原理
springmvc框架由DispatchServlet,handlerMapping,handlerAdapter,和檢視解析器組成
- DispatchServlet為springmvc核心攔截器,攔截所有請求並解析url(例如:http://localhost:8080/demo/example/model,擷取時候,去掉協議,去掉埠號,去掉工程名,即為example/model)
- handlerMapping為處理器對映(通過處理器對映,你可以將web請求對映到正確的處理器(handler)上。Spring內建了很多處理器對映策略),HandlerMapping是把一個URL指定到一個Controller上,就像應用系統的
- handlerAdapter(HandlerAdapter字面上的意思就是處理介面卡,它的作用用一句話概括就是呼叫具體的方法對使用者發來的請求來進行處理。當handlerMapping獲取到執行請求的controller時,DispatcherServlte會根據controller對應的controller型別來呼叫相應的HandlerAdapter來進行處理。)
- 檢視解析器通過返回值找到相應的頁面進行轉發
springmvc框架業務邏輯
- 由使用者傳送請求到DispatchServlet,DispatchServlet通過url找到handlerMapping(handler Mapping可以理解為一個map,儲存的是鍵值對,此處為key為url和value為一個handler型別的物件,通過url來尋找物件,handler中儲存的為類名,方法名,引數列表,有這三個即可通過反射呼叫)
- 如果第一步找不到即報404,能找到的話,則將handler物件返回給DispatchServlet
- DispatchServlet將獲取到的handler物件交給handlerAdapter,handlerAdapter拿出類名,方法名,引數列表進行呼叫
- 將handlerAdapter呼叫的返回值給DispatchServlet
- DispatchServlet將第四步的返回值交還給檢視解析器
- 檢視解析器通過返回值找到相應的頁面進行轉發(一定是轉發,不是重定向)
2、搭建springmvc框架
(1)導包
maven中如果只搭建springmvc,只需要在pom.xml中匯入兩個包,spring-mvc和spring-webmvc
這裡說一下,新建的maven war專案會報錯,因為webapp下缺少WEB-INF目錄和web.xml檔案,需要手動新增
匯入包
<!--pom.xml檔案-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>springmvc0815</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring.version>4.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
spring包不管用那個版本的一定要同一版本好,此處我做了版本管理,方便以後修改spring版本號
(2)配置DispatchServlet
配置Servlet有兩種方法,配xml或者配webservlet註解,但是此處DispatchServlet為maven提供的jar包,如果要加註解,則需要修改原始碼重新編譯,所以此處我們選擇配置xml檔案。
servlet為單例項,多執行緒,物件是在首次訪問的時候創建出來的,也就是說第一個訪問的人,速度要慢一些,為了提高效能,我們要讓他在伺服器啟動的時候就建立物件
新增<load-on-startup>1</load-on-startup>
<!--web.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<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>
</web-app>
啟動,會發現控制檯報錯
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/springmvc-servlet.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:344)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:452)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:663)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:629)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:677)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:548)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:489)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1188)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1132)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1021)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5085)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5397)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1410)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1400)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330)
... 28 more
Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml],找不到檔案
我們回過頭來看springmvc的底層原理,第一步使用者傳送請求給dispatchservlet,dispatchservlet拿url去handlermapping尋找,前提是handlermapping裡面要有內容,那我們考慮如何往handlermapping中放東西
框架在於可供所有人使用,所以框架的搭建者不知道你會向handler mapping中放什麼內容,這個時候,他都去了一個配置檔案,spring-servlet.xml,使用框架的人需要往handlermapping中放什麼,自己去配置檔案中寫,而spring-servlet.xml預設放在/WEB-INF目錄下
那麼檔名和路徑能不能修改,我們來看dispatchservlet原始碼
/**
* Suffix for WebApplicationContext namespaces. If a servlet of this class is
* given the name "test" in a context, the namespace used by the servlet will
* resolve to "test-servlet".
*/
public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";
/**
* Set a custom namespace for this servlet,
* to be used for building a default context config location.
*/
public void setNamespace(String namespace) {
this.namespace = namespace;
}
/**
* Return the namespace for this servlet, falling back to default scheme if
* no custom namespace was set: e.g. "test-servlet" for a servlet named "test".
*/
public String getNamespace() {
return (this.namespace != null ? this.namespace : getServletName() + DEFAULT_NAMESPACE_SUFFIX);
}
如果namespace為空,則配置檔名為servlet-name加字尾"-servlet"
如果namespace不為空,則配置檔名為namespace加字尾"-servlet"
所以我們想修改名字需要配namespace
<!--web.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>namespace</param-name>
<param-value>springmvc</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>
</web-app>
load-on-startup一定要配在servlet節點的最後一行,不然會報錯
(3)配置springmvc.xm
首先,在WEB-INF下建立springmvc.xml,要帶spring的名稱空間
<!--springmvc名稱空間頭部-->
<?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:util="http://www.springframework.org/schema/util"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
">
</beans>
- 開啟註解驅動<mvc:annotation-driven></mvc:annotation-driven>
- 包掃描<context:component-scan base-package="com.test.controller"></context:component-scan>,會掃描包下所有的類,在需要被掃描到的類上加@controller註解,在需要被掃描到的方法上加@RequestMapping註解。
<!--MyController.java-->
package com.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping("/index")
public String index(){
return "abcd";
}
}
- 此時,加了@controller註解的類MyController會被掃描到,掃描完後,會繼續掃描類中的方法,什麼樣的方法能被放到handlerMapping中呢,方法要對應url,我們就把url加在方法上,他的註解叫@RequestMapping,他就相當於webservlet,例如此方法中加了@RequestMapping("/index")註解,當輸入/index地址的時候,在伺服器啟動的時候,handlerMapping開始載入,讀取配置檔案,看到requestMaping註解,就會把“/index"作為key,然後把獲取到當前的類名,方法名,引數列表作為value封裝到handler物件中
- 配置檢視解析器,也就是一個bean結點。需要配置三個屬性viewClass(檢視型別)——jstl型別。prefix(字首),suffix(字尾)
<!--springmvc配置檔案--> <?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:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd "> <mvc:annotation-driven></mvc:annotation-driven> <context:component-scan base-package="com.test.controller"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <property name="prefix" value="/WEB-INF/pages"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
此時我們依賴jstl,需要在pox.xml中匯入jstl包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
關於字首和字尾:接著上面的流程,我們拿到返回值/abcd,handlerAdapter將返回值(/abcd)給到檢視解析器後,會把字首(/WEB-INF/pages)放到返回值前,把字尾(.jsp)放到返回值後,以webapp為根目錄去找這個檔案(/WEB-INF/pages/abcd.jsp),進行轉發
3、框架的使用
(1)controller往作用於中載入
使用modelandview或者modelmap
@Controller
public class MyController {
//方法1
@RequestMapping("/index")
public String list(ModelMap map){
map.addAttribute("attributeName", attributeValue);
}
//方法2
@RequestMapping("/index2")
public ModelAndView list(){
ModelAndView modelAndView = new ModelAndView("abcd");//abcd為要轉發的頁面
modelAndView.addObject("attributeName", attributeValue);
return modelAndView;
}
}
modelmap底層仍會封裝出modelandview
另:@RequestMapping("index")預設為value屬性,get和post方法都會進入此方法
@RequestMapping(value="/index2",method=RequestMethod.POST)只會接受post方法
@RequestMapping(value="/index2",method=RequestMethod.GET)只會接受get方法
(2)controller中接引數
//方法1
@RequestMapping(value="/index2",method=RequestMethod.POST)
public String index(String name){
System.out.println(name);//只需要與前端那麼屬性值一樣即可接收
return "abcd";
}
//方法2
//假設pojo中存在Student型別的物件,具有name,sex,age三個屬性
@RequestMapping(value="/index2",method=RequestMethod.POST)
public String index(Student student){
System.out.println(student);//只要pojo物件的屬性名,與前端頁面的屬性名一致,直接用物件接受即可
return "abcd";
}