目錄
1. SpringMvc簡(jiǎn)介
1.1 什么是MVC
1.2 什么是SpringMvc
1.3 SpringMvc 能干什么
1.4 SpringMvc 工作流程
2. SpringMvc攔截器和過(guò)濾器
2.1 攔截器
2.1.1 攔截器作用
2.1.2 攔截器和過(guò)濾器的區(qū)別
2.1.3 攔截器方法說(shuō)明
2.1.4 多個(gè)攔截器執(zhí)行順序
2.1.5 自定義攔截器
2.2?過(guò)濾器(附加)
3. 手寫(xiě)模擬SpringMvc源碼
3.1 目錄結(jié)構(gòu)如下
3.2 導(dǎo)入依賴(lài)
3.3 分析
3.4 測(cè)試
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-489342.html
1. SpringMvc簡(jiǎn)介
1.1 什么是MVC
MVC是一種軟件架構(gòu)的思想,將軟件按照模型、視圖、控制器來(lái)劃分。
M: Model,模型層,指工程中的JavaBean,作用是處理數(shù)據(jù)。
JavaBean分為兩類(lèi):
1.實(shí)體類(lèi)Bean:專(zhuān)門(mén)存儲(chǔ)業(yè)務(wù)數(shù)據(jù)的,如Student User等
2.業(yè)務(wù)處理Bean:指Service或Dao對(duì)象,專(zhuān)門(mén)用于處理業(yè)務(wù)邏輯和數(shù)據(jù)訪(fǎng)問(wèn)。
V: View,視圖層,指工程中的html或jsp等頁(yè)面,作用是與用戶(hù)進(jìn)行交互,展示數(shù)據(jù)。
C: Controller,控制層,指工程中的servlet,作用是接收請(qǐng)求和響應(yīng)瀏覽器。
MVC的工作流程:
用戶(hù)通過(guò)視圖層發(fā)送請(qǐng)求到服務(wù)器,在服務(wù)器中請(qǐng)求被Controller接收,Controller調(diào)用相應(yīng)的Model層處理請(qǐng)求,處理完畢將結(jié)果返回到Controller,Controller再根據(jù)請(qǐng)求處理的結(jié)果找到相應(yīng)的View視圖,渲染數(shù)據(jù)后最終響應(yīng)給瀏覽器。
1.2 什么是SpringMvc
SpringMVC是Spring的一個(gè)后續(xù)產(chǎn)品,是Spring的一個(gè)子項(xiàng)目。
SpringMVC是Spring為表述層開(kāi)發(fā)提供的一整套完備的解決方案。在表述層框架歷經(jīng)Strust、WebWork,Strust2等諸多產(chǎn)品的歷代更迭之后,目前業(yè)界普遍選擇了SpringMVC作為JavaEE項(xiàng)目表述層開(kāi)發(fā)的首選方案。
SpringMVC是一種基于Java的實(shí)現(xiàn)了Web MVC設(shè)計(jì)模式的請(qǐng)求驅(qū)動(dòng)類(lèi)型的輕量級(jí)Web框架,即使用了MVC架構(gòu)模式的思想,將web層進(jìn)行職責(zé)解耦,幫助我們簡(jiǎn)化開(kāi)發(fā)。
1.3 SpringMvc 能干什么
? ? 1)天生與Spring框架集成,如:(IOC,AOP)
2)支持Restful風(fēng)格
3)進(jìn)行更簡(jiǎn)潔的Web層開(kāi)發(fā)
4)支持靈活的URL到頁(yè)面控制器的映射
5)非常容易與其他視圖技術(shù)集成,如:Velocity、FreeMarker等等
6)因?yàn)槟P蛿?shù)據(jù)不存放在特定的API里,而是放在一個(gè)Model里(Map數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),因此很容易被其他框架使用)
7)非常靈活的數(shù)據(jù)驗(yàn)證、格式化和數(shù)據(jù)綁定機(jī)制、能使用任何對(duì)象進(jìn)行數(shù)據(jù)綁定,不必實(shí)現(xiàn)特定框架的API
8)更加簡(jiǎn)單、強(qiáng)大的異常處理
9)對(duì)靜態(tài)資源的支持
? ? 10)支持靈活的本地化、主題等解析
1.4 SpringMvc 工作流程
SpringMvc工作流程如下圖:
?
具體步驟:
- 第一步:發(fā)起請(qǐng)求到前端控制器DispatcherServlet。
- 第二步:前端控制器DispatcherServlet收到請(qǐng)求后調(diào)用處理器映射器HandlerMapping。
- 第三步:處理器映射器HandlerMapping 根據(jù)請(qǐng)求的URL找到具體的處理器,生成處理器對(duì)象Handler 以及處理器攔截器HandlerIntercepter(如果有則生成),并返回給向前端控制器。
- 第四步:前端控制器DispatcherServlet通過(guò)處理器適配器HandlerAdapter去調(diào)用處理器Controller。
- 第五步:調(diào)用處理器(Controller,也叫控制器)。
- 第六步:處理器Controller執(zhí)行完成給適配器返回ModelAndView
- 第七步:處理器適配器HandleAdapter將處理器Controller返回的結(jié)果ModelAndView返回給前端控制器DispatcherServlet。
- 第八步:前端控制器DispatcherServlet將ModelAndView傳給視圖解析器ViewResolver。
- 第九步:視圖解析器ViewResolver解析后向前端控制器DispatcherServlet返回View。
- 第十步:前端控制器DispatcherServlet進(jìn)行視圖渲染 (視圖渲染將模型數(shù)據(jù)(在ModelAndView對(duì)象中)填充到request域)。
- 第十一步:前端控制器DispatcherServlet向用戶(hù)響應(yīng)結(jié)果。
2. SpringMvc攔截器和過(guò)濾器
2.1 攔截器
2.1.1 攔截器作用
SpringMVC的攔截器類(lèi)似于Servlet開(kāi)發(fā)中的過(guò)濾器Filter,用于對(duì)處理器進(jìn)行預(yù)處理和后處理
將攔截器按一定的順序連接成鏈,這條鏈稱(chēng)為攔截器鏈(Interceptor chain)。在訪(fǎng)問(wèn)被攔截的方法或字段時(shí),攔截器鏈中的攔截器就會(huì)按其之前定義的順序被調(diào)用。攔截器也是AOP思想的具體實(shí)現(xiàn)。
2.1.2 攔截器和過(guò)濾器的區(qū)別
如下圖:
?
2.1.3 攔截器方法說(shuō)明
?Spring MVC也可以使用攔截器對(duì)請(qǐng)求進(jìn)行攔截處理,用戶(hù)可以自定義攔截器來(lái)實(shí)現(xiàn)特定的功能,自定義的攔截器可以實(shí)現(xiàn)HandlerInterceptor接口,也可以繼承HandlerInterceptorAdapter?適配器類(lèi) 。
①?preHandle():這個(gè)方法在業(yè)務(wù)處理器處理請(qǐng)求之前被調(diào)用,在該方法中對(duì)用戶(hù)請(qǐng)求?request?進(jìn)行處理。如果程序員決定該攔截器對(duì)請(qǐng)求進(jìn)行攔截處理后還要調(diào)用其他的攔截器,或者是業(yè)務(wù)處理器去進(jìn)行處理,則返回true;如果程序員決定不需要再調(diào)用其他的組件去處理請(qǐng)求,則返回false。
②?postHandle():這個(gè)方法在業(yè)務(wù)處理器處理完請(qǐng)求后,但是DispatcherServlet?向客戶(hù)端返回響應(yīng)前被調(diào)用,在該方法中對(duì)用戶(hù)請(qǐng)求request進(jìn)行處理。
③?afterCompletion():這個(gè)方法在?DispatcherServlet?完全處理完請(qǐng)求后被調(diào)用,可以在該方法中進(jìn)行一些資源清理的操作。
?
2.1.4 多個(gè)攔截器執(zhí)行順序
?
2.1.5 自定義攔截器
自定義攔截器步驟如下:
①創(chuàng)建攔截器類(lèi)實(shí)現(xiàn)HandlerInterceptor接口
②配置攔截器
③測(cè)試攔截器的攔截效果
這里采用Springboot框架,代碼如下:
創(chuàng)建攔截器類(lèi)實(shí)現(xiàn)HandlerInterceptor接口
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
System.out.println("執(zhí)行了preHandle方法");
if(token.equals("admin"))
return true;
else
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("執(zhí)行了postHandle方法");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("執(zhí)行了afterCompletion方法");
}
}
配置攔截器
@Configuration
public class MyConfiguration implements WebMvcConfigurer {
@Bean
public LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/test1");
}
}
2.2?過(guò)濾器(附加)
過(guò)濾器是Web開(kāi)發(fā)中很實(shí)用的一項(xiàng)技術(shù),?開(kāi)發(fā)人員可以通過(guò)過(guò)濾器對(duì)Web服務(wù)管理的資源靜態(tài)HTML文件、靜態(tài)圖片、JSP、?Servlet?等進(jìn)行攔截,從而實(shí)現(xiàn)一一?些特殊的需求,比如設(shè)置URL的訪(fǎng)問(wèn)權(quán)限、過(guò)濾敏感詞匯、壓縮響應(yīng)信息等。過(guò)濾器還適用于對(duì)用戶(hù)請(qǐng)求和響應(yīng)對(duì)象進(jìn)行檢查和修改,但是Filter本身并不生成請(qǐng)求和響應(yīng)對(duì)象,只是提供過(guò)濾功能。Filter?的完整工作流程如圖所示:
當(dāng)客戶(hù)瑞發(fā)出對(duì)Web資源的請(qǐng)求時(shí),Web服務(wù)器會(huì)根據(jù)應(yīng)用程序配置文件設(shè)置的過(guò)濾規(guī)則進(jìn)行檢查,若客戶(hù)端請(qǐng)求滿(mǎn)足過(guò)濾規(guī)則,則對(duì)客戶(hù)端請(qǐng)求響應(yīng)進(jìn)行攔截。首先按照需求對(duì)請(qǐng)求頭和請(qǐng)求數(shù)據(jù)進(jìn)行封裝,并依次通過(guò)過(guò)濾器鏈,然后把請(qǐng)求/響應(yīng)交給Web資源處理,請(qǐng)求信息在過(guò)濾器鏈中可以被修改,也可以根據(jù)條件讓請(qǐng)求不發(fā)往資源處理器,并直接向客戶(hù)機(jī)發(fā)回一個(gè)響應(yīng)。當(dāng)資源處理器完成了對(duì)資源的處理后,響應(yīng)信息將逐級(jí)逆向返回。在這個(gè)過(guò)程中,用戶(hù)可以修改響應(yīng)信息,從而完成一定的任務(wù)。?這就是過(guò)濾器的工作原理。
另外,過(guò)濾器的生命周期也是由Web服務(wù)器進(jìn)行負(fù)責(zé)的,但是相比真正的Servlet又有區(qū)別。Filter?的生命周期大致分為以下三個(gè)階段:
(1)實(shí)例化:?Web容器在部署Web應(yīng)用程序時(shí)對(duì)所有過(guò)濾器進(jìn)行實(shí)例化,此時(shí)Web容器調(diào)用的是它的無(wú)參構(gòu)造方法。
(2)初始化:實(shí)例化完成之后,馬上進(jìn)行初始化工作。Web容器回調(diào)initO方法。請(qǐng)求路徑匹配過(guò)濾器的URL映射時(shí),Web容器回調(diào)過(guò)濾器的doFilter()方法,此方法也是過(guò)濾器的核心方法。
3)銷(xiāo)毀:?Web容器在卸載Web應(yīng)用程序前回調(diào)doDestory?方法。?
在Springboot中要在啟動(dòng)類(lèi)上加上@ServletComponentScan注解
過(guò)濾器代碼如下:
@WebFilter("/*")
public class WebTestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("過(guò)濾器前進(jìn)");
chain.doFilter(request,response);
System.out.println("過(guò)濾器返回");
}
@Override
public void destroy() {
}
}
注意:在Controller業(yè)務(wù)處理中,如果有請(qǐng)求的轉(zhuǎn)發(fā),攔截器會(huì)攔截多次,而過(guò)濾器并不會(huì)。
3. 手寫(xiě)模擬SpringMvc源碼
3.1 目錄結(jié)構(gòu)如下
?
3.2 導(dǎo)入依賴(lài)
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 解析xml文件-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
3.3 分析
在啟動(dòng)Tomcat后,會(huì)自動(dòng)解析webapp中的WEB-INF中的web.xml,?所以我們?cè)趙eb.xml文件配置如下:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Application</display-name>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.example.demo.springmvc.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>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
這里的DispatcherServlet為我們自己創(chuàng)建的類(lèi)。當(dāng)Tomcat啟動(dòng)后,就會(huì)解析Web.xml文件,并且創(chuàng)建我們自定義的DispatcherServlet類(lèi)。在上面目錄中我們創(chuàng)建了ApplicationContext類(lèi),這里相當(dāng)于Spring容器,這塊的代碼屬于Spring源碼部分,在這里省略。然后就會(huì)執(zhí)行DispatcherServlet類(lèi)中的init()方法,該方法作用為創(chuàng)建Spring容器,從Springmvc.xml文件中讀取base-package中的包路徑,這里Spring容器會(huì)掃描這里的包路徑并且生成Controller類(lèi)型的Bean對(duì)象,init方法代碼如下:
private ApplicationContext applicationContext; //Spring容器
@Override
public void init() throws ServletException {
String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation"); //這里獲取Web.xml文件中的contextConfigLocation參數(shù),這里為classpath:springmvc.xml。
applicationContext = new ApplicationContext(contextConfigLocation);//這里為創(chuàng)建Spring容器,從Springmvc.xml文件中讀取base-package中的包路徑,這里Spring容器會(huì)掃描這里的包路徑并且生成Controller類(lèi)型的Bean.
applicationContext.refresh();
initHandleMappinng(applicationContext);
}
Spring容器將Spring.xml文件中的包路徑下的Controller生成Bean對(duì)象后,執(zhí)行DispatcherServlet中的initHandleMappinng方法,該方法會(huì)遍歷Spring容器中beanDefinitionConcurrentHashMap,遍歷其中的所有的Controller類(lèi)型的Bean對(duì)象的Class對(duì)象,然后判斷每一個(gè)Class對(duì)象中的所有方法,將有@RequestMapping注解的方法,進(jìn)行封裝成一個(gè)MyHandle對(duì)象,其中包含@RequestMapping注解的Value值,該方法名,Class對(duì)象等,然后將這個(gè)MyHandle對(duì)象放到集合中。代碼如下:
public void initHandleMappinng(ApplicationContext applicationContext){
if(applicationContext.beanDefinitionConcurrentHashMap.size()==0){
throw new RuntimeException("Spring容器為空");
}
for (Map.Entry<String, BeanDefinition> stringBeanDefinitionEntry : applicationContext.beanDefinitionConcurrentHashMap.entrySet()) {
Class clazz = stringBeanDefinitionEntry.getValue().getClazz();
for (Method declaredMethod : clazz.getDeclaredMethods()) {
boolean annotationPresent = declaredMethod.isAnnotationPresent(RequestMapping.class);
if(annotationPresent==true){
String value = declaredMethod.getAnnotation(RequestMapping.class).value();
MyHandle myHandle=new MyHandle(value,declaredMethod,clazz);
myHandleList.add(myHandle);
}
}
}
}
當(dāng)有g(shù)et請(qǐng)求的時(shí)候就會(huì)執(zhí)行DispatcherServlet中的doGet方法,然后我們的業(yè)務(wù)邏輯如下:在這里我們會(huì)遍歷我們存放MyHandle對(duì)象的集合中的元素,尋找瀏覽器url請(qǐng)求路徑和我們MyHandle對(duì)象中儲(chǔ)存的@RequestMapping中相等的對(duì)象,然后設(shè)置個(gè)Object數(shù)組,經(jīng)過(guò)一系列的關(guān)于@RequestParam參數(shù)的判斷,將瀏覽器請(qǐng)求路徑中的參數(shù)放到對(duì)應(yīng)位置的Object數(shù)組中,然后通過(guò)method.invoke執(zhí)行這個(gè)方法就可以執(zhí)行我們的方法并獲得返回值,然后我們就可以通過(guò)PrintWriter writer = response.getWriter(); writer.print(); 將返回的數(shù)據(jù)打印到瀏覽器上。代碼如下:
public void excuteDispatch(HttpServletRequest request,HttpServletResponse response){
MyHandle handle = getHandle(request);
if(handle==null){
try {
response.getWriter().print("404");
} catch (IOException e) {
e.printStackTrace();
}
}
else {
Method method = handle.getMethod();
Class<?>[] parameterTypes = method.getParameterTypes();
Object[] params=new Object[parameterTypes.length];
Map<String, String[]> parameterMap = request.getParameterMap();
for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
String key = stringEntry.getKey();
String value = stringEntry.getValue()[0];
int i = GetRequestParams(method, key);
if(i>=0)
params[i]=value;
else {
//反射獲取的是arg0,官方這里用的不是反射機(jī)制
}
}
try {
Object invoke = method.invoke(handle.getClazz().newInstance(), params);
PrintWriter writer = response.getWriter();
writer.print(invoke);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
其余的代碼省略,全部代碼可以下載我的文件資源進(jìn)行查看。
3.4 測(cè)試
我們自己模擬寫(xiě)了一個(gè)簡(jiǎn)單的SpringMvc框架,啟動(dòng)Tomcat后然后我們進(jìn)行驗(yàn)證如下:
測(cè)試代碼如下:
@Controller("mycontroller")
public class MyController {
@RequestMapping("/test")
public String test(@RequestParam("name") String name, HttpServletResponse response){
return name;
}
}
運(yùn)行結(jié)果如下圖:
?由此可見(jiàn)我們的測(cè)試結(jié)果非常完美。
這篇文章也結(jié)束了,SpringMvc源碼模擬可以在我的文件資源下載,鏈接為:
https://download.csdn.net/download/qq_43649937/87558006文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-489342.html
?
到了這里,關(guān)于SpringMvc攔截器和手寫(xiě)模擬SpringMvc工作流程源碼詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!