springMvc源码刨析笔记
springMvc源碼刨析筆記
MVC 全名是 Model View Controller,是 模型(model)-視圖(view)-控制器(controller) 的縮寫, 是?種?于設(shè)計(jì)創(chuàng)建 Web 應(yīng)?程序表現(xiàn)層的模式。
1.執(zhí)行流程
2.九大組件
//文件上傳組件initMultipartResolver(context);//初始化本地語言環(huán)境initLocaleResolver(context);//初始化模板處理器initThemeResolver(context);//初始化HandleMappinginitHandlerMappings(context);//初始化參數(shù)適配器initHandlerAdapters(context);//初始化異常攔截器initHandlerExceptionResolvers(context);//初始化試視圖預(yù)處理器initRequestToViewNameTranslator(context);//試圖轉(zhuǎn)換器initViewResolvers(context);//falshMap管理器initFlashMapManager(context);2.自定義springMvc框架
1.聲明創(chuàng)建(LgDispatcherServlet)并繼承Servlet
springmvc.properties
scanPackage=com.lagou.demoLgDispatcherServlet 實(shí)現(xiàn)了servlet對象的init() doGet() doPost()方法
public class LgDispatcherServlet extends HttpServlet {@Overridepublic void init(ServletConfig config) throws ServletException {}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {} }2.聲明注解
LagouAutowired,LagouController,LagouRequestMapping,LagouService
@Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface LagouAutowired {String value() default ""; }3.加載配置文件
private Properties properties = new Properties(); // 1 加載配置文件 springmvc.propertiesString contextConfigLocation = config.getInitParameter("contextConfigLocation");doLoadConfig(contextConfigLocation); // 加載配置文件private void doLoadConfig(String contextConfigLocation) {InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);try {//加載文件properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}4.掃描包
private List<String> classNames = new ArrayList<>(); // 緩存掃描到的類的全限定類名 // 2 掃描相關(guān)的類,掃描注解doScan(properties.getProperty("scanPackage"));private void doScan(String scanPackage) {//替換了包路徑名 /替換為.String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");File pack = new File(scanPackagePath);File[] files = pack.listFiles();for(File file: files) {if(file.isDirectory()) { // 子package// 遞歸doScan(scanPackage + "." + file.getName()); // com.lagou.demo.controller}else if(file.getName().endsWith(".class")) {//添加到全局類中String className = scanPackage + "." + file.getName().replaceAll(".class", "");classNames.add(className);}}}5.初始化對象
// ioc容器// 基于classNames緩存的類的全限定類名,以及反射技術(shù),完成對象創(chuàng)建和管理private void doInstance() {if(classNames.size() == 0) return;try{for (int i = 0; i < classNames.size(); i++) {String className = classNames.get(i); // com.lagou.demo.controller.DemoController// 反射Class<?> aClass = Class.forName(className);// 區(qū)分controller,區(qū)分service'if(aClass.isAnnotationPresent(LagouController.class)) {// controller的id此處不做過多處理,不取value了,就拿類的首字母小寫作為id,保存到ioc中String simpleName = aClass.getSimpleName();// DemoControllerString lowerFirstSimpleName = lowerFirst(simpleName); // demoControllerObject o = aClass.newInstance();ioc.put(lowerFirstSimpleName,o);}else if(aClass.isAnnotationPresent(LagouService.class)) {LagouService annotation = aClass.getAnnotation(LagouService.class);//獲取注解value值String beanName = annotation.value();// 如果指定了id,就以指定的為準(zhǔn)if(!"".equals(beanName.trim())) {ioc.put(beanName,aClass.newInstance());}else{// 如果沒有指定,就以類名首字母小寫beanName = lowerFirst(aClass.getSimpleName());ioc.put(beanName,aClass.newInstance());}// service層往往是有接口的,面向接口開發(fā),此時(shí)再以接口名為id,放入一份對象到ioc中,便于后期根據(jù)接口類型注入Class<?>[] interfaces = aClass.getInterfaces();for (int j = 0; j < interfaces.length; j++) {Class<?> anInterface = interfaces[j];// 以接口的全限定類名作為id放入ioc.put(anInterface.getName(),aClass.newInstance());}}else{continue;}}}catch (Exception e) {e.printStackTrace();}}// 首字母小寫方法public String lowerFirst(String str) {char[] chars = str.toCharArray();if('A' <= chars[0] && chars[0] <= 'Z') {chars[0] += 32;}return String.valueOf(chars);}6.實(shí)例化對象(AutoWired)
// 實(shí)現(xiàn)依賴注入doAutoWired();// 實(shí)現(xiàn)依賴注入private void doAutoWired() {if(ioc.isEmpty()) {return;}// 有對象,再進(jìn)行依賴注入處理// 遍歷ioc中所有對象,查看對象中的字段,是否有@LagouAutowired注解,如果有需要維護(hù)依賴注入關(guān)系for(Map.Entry<String,Object> entry: ioc.entrySet()) {// 獲取bean對象中的字段信息 也就是獲取方法的屬性Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();// 遍歷判斷處理for (int i = 0; i < declaredFields.length; i++) {Field declaredField = declaredFields[i]; // @LagouAutowired private IDemoService demoService;if(!declaredField.isAnnotationPresent(LagouAutowired.class)) {continue;}// 有該注解LagouAutowired annotation = declaredField.getAnnotation(LagouAutowired.class);String beanName = annotation.value(); // 需要注入的bean的idif("".equals(beanName.trim())) {// 沒有配置具體的bean id,那就需要根據(jù)當(dāng)前字段類型注入(接口注入) IDemoServicebeanName = declaredField.getType().getName();}// 開啟賦值declaredField.setAccessible(true);try {declaredField.set(entry.getValue(),ioc.get(beanName));} catch (IllegalAccessException e) {e.printStackTrace();}}}}7.HandlerMapping處理器映射器(url與method)
//private Map<String,Method> handlerMapping = now HashMap<>(); // 存儲url和Method之間的映射關(guān)系private List<Handler> handlerMapping = new ArrayList<>(); ===============================================================private void initHandlerMapping() {if(ioc.isEmpty()) {return;}for(Map.Entry<String,Object> entry: ioc.entrySet()) {// 獲取ioc中當(dāng)前遍歷的對象的class類型Class<?> aClass = entry.getValue().getClass();if(!aClass.isAnnotationPresent(LagouController.class)) {continue;}String baseUrl = "";if(aClass.isAnnotationPresent(LagouRequestMapping.class)) {LagouRequestMapping annotation = aClass.getAnnotation(LagouRequestMapping.class);baseUrl = annotation.value(); // 等同于/demo}// 獲取方法Method[] methods = aClass.getMethods();for (int i = 0; i < methods.length; i++) {Method method = methods[i];// 方法沒有標(biāo)識LagouRequestMapping,就不處理if(!method.isAnnotationPresent(LagouRequestMapping.class)) {continue;}// 如果標(biāo)識,就處理LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class);String methodUrl = annotation.value(); // /queryString url = baseUrl + methodUrl; // 計(jì)算出來的url /demo/query// 把method所有信息及url封裝為一個(gè)HandlerHandler handler = new Handler(entry.getValue(),method, Pattern.compile(url));// 計(jì)算方法的參數(shù)位置信息 // query(HttpServletRequest request, HttpServletResponse response,String name)Parameter[] parameters = method.getParameters();for (int j = 0; j < parameters.length; j++) {Parameter parameter = parameters[j];if(parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {// 如果是request和response對象,那么參數(shù)名稱寫HttpServletRequest和HttpServletResponsehandler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);}else{handler.getParamIndexMapping().put(parameter.getName(),j); // <name,2>}}// 建立url和method之間的映射關(guān)系(map緩存起來)handlerMapping.add(handler);}}}public class Handler {private Object controller; // method.invoke(obj,)private Method method;private Pattern pattern; // spring中url是支持正則的 路徑private Map<String,Integer> paramIndexMapping; // 參數(shù)順序,是為了進(jìn)行參數(shù)綁定,key是參數(shù)名,value代表是第幾個(gè)參數(shù) <name,2>}8.post請求參數(shù)處理
@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 處理請求:根據(jù)url,找到對應(yīng)的Method方法,進(jìn)行調(diào)用// 獲取uri // String requestURI = req.getRequestURI(); // Method method = handlerMapping.get(requestURI);// 獲取到一個(gè)反射的方法// 反射調(diào)用,需要傳入對象,需要傳入?yún)?shù),此處無法完成調(diào)用,沒有把對象緩存起來,也沒有參數(shù)!!!!改造initHandlerMapping(); // method.invoke() //// 根據(jù)uri獲取到能夠處理當(dāng)前請求的hanlder(從handlermapping中(list))Handler handler = getHandler(req);if(handler == null) {resp.getWriter().write("404 not found");return;}// 參數(shù)綁定// 獲取所有參數(shù)類型數(shù)組,這個(gè)數(shù)組的長度就是我們最后要傳入的args數(shù)組的長度Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();// 根據(jù)上述數(shù)組長度創(chuàng)建一個(gè)新的數(shù)組(參數(shù)數(shù)組,是要傳入反射調(diào)用的)Object[] paraValues = new Object[parameterTypes.length];// 以下就是為了向參數(shù)數(shù)組中塞值,而且還得保證參數(shù)的順序和方法中形參順序一致Map<String, String[]> parameterMap = req.getParameterMap();// 遍歷request中所有參數(shù) (填充除了request,response之外的參數(shù))for(Map.Entry<String,String[]> param: parameterMap.entrySet()) {// name=1&name=2 name [1,2]String value = StringUtils.join(param.getValue(), ","); // 如同 1,2// 如果參數(shù)和方法中的參數(shù)匹配上了,填充數(shù)據(jù)if(!handler.getParamIndexMapping().containsKey(param.getKey())) {continue;}// 方法形參確實(shí)有該參數(shù),找到它的索引位置,對應(yīng)的把參數(shù)值放入paraValuesInteger index = handler.getParamIndexMapping().get(param.getKey());//name在第 2 個(gè)位置paraValues[index] = value; // 把前臺傳遞過來的參數(shù)值填充到對應(yīng)的位置去}int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0paraValues[requestIndex] = req;int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1paraValues[responseIndex] = resp;// 最終調(diào)用handler的method屬性try {handler.getMethod().invoke(handler.getController(),paraValues);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}private Handler getHandler(HttpServletRequest req) {if(handlerMapping.isEmpty()){return null;}///demo/queryString url = req.getRequestURI();for(Handler handler: handlerMapping) {Matcher matcher = handler.getPattern().matcher(url);if(!matcher.matches()){continue;}return handler;}return null;}9.加入到web.xml
<servlet><servlet-name>lgoumvc</servlet-name><servlet-class>com.lagou.edu.mvcframework.servlet.LgDispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>springmvc.properties</param-value></init-param></servlet><servlet-mapping><servlet-name>lgoumvc</servlet-name><url-pattern>/*</url-pattern></servlet-mapping>自定義答案的核心類(LgDispatcherServlet)
package com.lagou.edu.mvcframework.servlet;import com.lagou.demo.service.IDemoService; import com.lagou.edu.mvcframework.annotations.*; import com.lagou.edu.mvcframework.pojo.Handler; import org.apache.commons.lang3.StringUtils;import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern;public class LgDispatcherServlet extends HttpServlet {private Properties properties = new Properties();private List<String> classNames = new ArrayList<>(); // 緩存掃描到的類的全限定類名// ioc容器private Map<String,Object> ioc = new HashMap<String,Object>();private Map<Handler,List<String>> securityMap = new HashMap<>();// handlerMapping//private Map<String,Method> handlerMapping = now HashMap<>(); // 存儲url和Method之間的映射關(guān)系private List<Handler> handlerMapping = new ArrayList<>();@Overridepublic void init(ServletConfig config) throws ServletException {// 1 加載配置文件 springmvc.propertiesString contextConfigLocation = config.getInitParameter("contextConfigLocation");doLoadConfig(contextConfigLocation);// 2 掃描相關(guān)的類,掃描注解doScan(properties.getProperty("scanPackage"));// 3 初始化bean對象(實(shí)現(xiàn)ioc容器,基于注解)doInstance();// 4 實(shí)現(xiàn)依賴注入doAutoWired();// 5 構(gòu)造一個(gè)HandlerMapping處理器映射器,將配置好的url和Method建立映射關(guān)系initHandlerMapping();System.out.println("lagou mvc 初始化完成....");// 等待請求進(jìn)入,處理請求}/*構(gòu)造一個(gè)HandlerMapping處理器映射器最關(guān)鍵的環(huán)節(jié)目的:將url和method建立關(guān)聯(lián)*/private void initHandlerMapping() {if(ioc.isEmpty()) {return;}for(Map.Entry<String,Object> entry: ioc.entrySet()) {// 獲取ioc中當(dāng)前遍歷的對象的class類型Class<?> aClass = entry.getValue().getClass();if(!aClass.isAnnotationPresent(LagouController.class)) {continue;}String baseUrl = "";if(aClass.isAnnotationPresent(LagouRequestMapping.class)) {LagouRequestMapping annotation = aClass.getAnnotation(LagouRequestMapping.class);baseUrl = annotation.value(); // 等同于/demo}// 獲取方法Method[] methods = aClass.getMethods();for (int i = 0; i < methods.length; i++) {Method method = methods[i];// 方法沒有標(biāo)識LagouRequestMapping,就不處理if(!method.isAnnotationPresent(LagouRequestMapping.class)) {continue;}// 如果標(biāo)識,就處理LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class);String methodUrl = annotation.value(); // /queryString url = baseUrl + methodUrl; // 計(jì)算出來的url /demo/query// 把method所有信息及url封裝為一個(gè)HandlerHandler handler = new Handler(entry.getValue(),method, Pattern.compile(url));// 計(jì)算方法的參數(shù)位置信息 // query(HttpServletRequest request, HttpServletResponse response,String name)Parameter[] parameters = method.getParameters();for (int j = 0; j < parameters.length; j++) {Parameter parameter = parameters[j];if(parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {// 如果是request和response對象,那么參數(shù)名稱寫HttpServletRequest和HttpServletResponsehandler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);}else{handler.getParamIndexMapping().put(parameter.getName(),j); // <name,2>}}//*******************針對Security的處理,start************************// 必須先有Controller的權(quán)限,然后才能有Handler方法的權(quán)限,所以有如下情況// 1 如果Controller和Handler都配置了@Security(取交集)// 2 如果僅僅Controller配置了@Security(以Controller為準(zhǔn))// 3 如果僅僅Handler方法配置了@Security(以Handler方法為準(zhǔn))if(aClass.isAnnotationPresent(Security.class) && method.isAnnotationPresent(Security.class)) {Security controllerSecurity = aClass.getAnnotation(Security.class);String[] controllerUsernames = controllerSecurity.value();List<String> controllerUnameList = Arrays.asList(controllerUsernames);Security handlerSecurity = method.getAnnotation(Security.class);String[] handlerUsernames = handlerSecurity.value();List<String> handlerUnameList = Arrays.asList(handlerUsernames);// 求交集controllerUnameList = new ArrayList<>(controllerUnameList);handlerUnameList = new ArrayList<>(handlerUnameList);controllerUnameList.retainAll(handlerUnameList);securityMap.put( handler, controllerUnameList);}else if(aClass.isAnnotationPresent(Security.class)) {Security controllerSecurity = aClass.getAnnotation(Security.class);String[] controllerUsernames = controllerSecurity.value();List<String> controllerUnameList = Arrays.asList(controllerUsernames);securityMap.put( handler, controllerUnameList);}else if(method.isAnnotationPresent(Security.class)) {Security handlerSecurity = method.getAnnotation(Security.class);String[] handlerUsernames = handlerSecurity.value();List<String> handlerUnameList = Arrays.asList(handlerUsernames);securityMap.put( handler, handlerUnameList);}//*******************針對Security的處理,end************************// 建立url和method之間的映射關(guān)系(map緩存起來)handlerMapping.add(handler);}}}// 實(shí)現(xiàn)依賴注入private void doAutoWired() {if(ioc.isEmpty()) {return;}// 有對象,再進(jìn)行依賴注入處理// 遍歷ioc中所有對象,查看對象中的字段,是否有@LagouAutowired注解,如果有需要維護(hù)依賴注入關(guān)系for(Map.Entry<String,Object> entry: ioc.entrySet()) {// 獲取bean對象中的字段信息Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();// 遍歷判斷處理for (int i = 0; i < declaredFields.length; i++) {Field declaredField = declaredFields[i]; // @LagouAutowired private IDemoService demoService;if(!declaredField.isAnnotationPresent(LagouAutowired.class)) {continue;}// 有該注解LagouAutowired annotation = declaredField.getAnnotation(LagouAutowired.class);String beanName = annotation.value(); // 需要注入的bean的idif("".equals(beanName.trim())) {// 沒有配置具體的bean id,那就需要根據(jù)當(dāng)前字段類型注入(接口注入) IDemoServicebeanName = declaredField.getType().getName();}// 開啟賦值declaredField.setAccessible(true);try {declaredField.set(entry.getValue(),ioc.get(beanName));} catch (IllegalAccessException e) {e.printStackTrace();}}}}// ioc容器// 基于classNames緩存的類的全限定類名,以及反射技術(shù),完成對象創(chuàng)建和管理private void doInstance() {if(classNames.size() == 0) return;try{for (int i = 0; i < classNames.size(); i++) {String className = classNames.get(i); // com.lagou.demo.controller.DemoController// 反射Class<?> aClass = Class.forName(className);// 區(qū)分controller,區(qū)分service'if(aClass.isAnnotationPresent(LagouController.class)) {// controller的id此處不做過多處理,不取value了,就拿類的首字母小寫作為id,保存到ioc中String simpleName = aClass.getSimpleName();// DemoControllerString lowerFirstSimpleName = lowerFirst(simpleName); // demoControllerObject o = aClass.newInstance();ioc.put(lowerFirstSimpleName,o);}else if(aClass.isAnnotationPresent(LagouService.class)) {LagouService annotation = aClass.getAnnotation(LagouService.class);//獲取注解value值String beanName = annotation.value();// 如果指定了id,就以指定的為準(zhǔn)if(!"".equals(beanName.trim())) {ioc.put(beanName,aClass.newInstance());}else{// 如果沒有指定,就以類名首字母小寫beanName = lowerFirst(aClass.getSimpleName());ioc.put(beanName,aClass.newInstance());}// service層往往是有接口的,面向接口開發(fā),此時(shí)再以接口名為id,放入一份對象到ioc中,便于后期根據(jù)接口類型注入Class<?>[] interfaces = aClass.getInterfaces();for (int j = 0; j < interfaces.length; j++) {Class<?> anInterface = interfaces[j];// 以接口的全限定類名作為id放入ioc.put(anInterface.getName(),aClass.newInstance());}}else{continue;}}}catch (Exception e) {e.printStackTrace();}}// 首字母小寫方法public String lowerFirst(String str) {char[] chars = str.toCharArray();if('A' <= chars[0] && chars[0] <= 'Z') {chars[0] += 32;}return String.valueOf(chars);}// 掃描類// scanPackage: com.lagou.demo package----> 磁盤上的文件夾(File) com/lagou/demoprivate void doScan(String scanPackage) {String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");File pack = new File(scanPackagePath);File[] files = pack.listFiles();for(File file: files) {if(file.isDirectory()) { // 子package// 遞歸doScan(scanPackage + "." + file.getName()); // com.lagou.demo.controller}else if(file.getName().endsWith(".class")) {String className = scanPackage + "." + file.getName().replaceAll(".class", "");classNames.add(className);}}}// 加載配置文件private void doLoadConfig(String contextConfigLocation) {InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);try {properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();}}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 處理請求:根據(jù)url,找到對應(yīng)的Method方法,進(jìn)行調(diào)用// 獲取uri // String requestURI = req.getRequestURI(); // Method method = handlerMapping.get(requestURI);// 獲取到一個(gè)反射的方法// 反射調(diào)用,需要傳入對象,需要傳入?yún)?shù),此處無法完成調(diào)用,沒有把對象緩存起來,也沒有參數(shù)!!!!改造initHandlerMapping(); // method.invoke() //// 根據(jù)uri獲取到能夠處理當(dāng)前請求的hanlder(從handlermapping中(list))Handler handler = getHandler(req);if(handler == null) {resp.getWriter().write("404 not found");return;}// 安全認(rèn)證List<String> usernameList = securityMap.get(handler);// 不為空說明需要認(rèn)證,那么不包含的話就returnif(usernameList != null && !usernameList.contains(req.getParameter("username"))) {resp.getWriter().write("sorry,the request has been forbidden---security");return;}// 參數(shù)綁定// 獲取所有參數(shù)類型數(shù)組,這個(gè)數(shù)組的長度就是我們最后要傳入的args數(shù)組的長度Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();// 根據(jù)上述數(shù)組長度創(chuàng)建一個(gè)新的數(shù)組(參數(shù)數(shù)組,是要傳入反射調(diào)用的)Object[] paraValues = new Object[parameterTypes.length];// 以下就是為了向參數(shù)數(shù)組中塞值,而且還得保證參數(shù)的順序和方法中形參順序一致Map<String, String[]> parameterMap = req.getParameterMap();// 遍歷request中所有參數(shù) (填充除了request,response之外的參數(shù))for(Map.Entry<String,String[]> param: parameterMap.entrySet()) {// name=1&name=2 name [1,2]String value = StringUtils.join(param.getValue(), ","); // 如同 1,2// 如果參數(shù)和方法中的參數(shù)匹配上了,填充數(shù)據(jù)if(!handler.getParamIndexMapping().containsKey(param.getKey())) {continue;}// 方法形參確實(shí)有該參數(shù),找到它的索引位置,對應(yīng)的把參數(shù)值放入paraValuesInteger index = handler.getParamIndexMapping().get(param.getKey());//name在第 2 個(gè)位置paraValues[index] = value; // 把前臺傳遞過來的參數(shù)值填充到對應(yīng)的位置去}int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0paraValues[requestIndex] = req;int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1paraValues[responseIndex] = resp;// 最終調(diào)用handler的method屬性try {handler.getMethod().invoke(handler.getController(),paraValues);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}private Handler getHandler(HttpServletRequest req) {if(handlerMapping.isEmpty()){return null;}String url = req.getRequestURI();for(Handler handler: handlerMapping) {Matcher matcher = handler.getPattern().matcher(url);if(!matcher.matches()){continue;}return handler;}return null;}}3.源碼刨析
1.getHandler()
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);// public java.lang.String com.lagou.edu.controller.DemoController.handle01(java.lang.String,java.util.Map<java.lang.String, java.lang.Object>)if (handler != null) {return handler;}}}return null;}2.getHandlerAdapter()
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}} }3.handle()
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//執(zhí)行請求 參數(shù)Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);} //======================================================public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//獲取方法參數(shù)值Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);return doInvoke(args);} //=================getMethodArgumentValues=================================== protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {//獲取方法的參數(shù)MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);return args;}4.processDispatchResult()
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); //======================processDispatchResult試圖處理 private void processDispatchResult(){if (mv != null && !mv.wasCleared()) {render(mv, request, response);//試圖處理if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}if (mappedHandler != null) {//攔截器的第三次執(zhí)行的地方mappedHandler.triggerAfterCompletion(request, response, null);}} //======================render() protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {// 處理view = resolveViewName(viewName, mv.getModelInternal(), locale, request);} //======================resolveViewName() public View resolveViewName(String viewName, Locale locale) throws Exception {if (view == null) {synchronized (this.viewCreationCache) {view = this.viewCreationCache.get(cacheKey);if (view == null) {// Ask the subclass to create the View object.//創(chuàng)建視圖view = createView(viewName, locale);if (view == null && this.cacheUnresolved) {view = UNRESOLVED_VIEW;}if (view != null) {this.viewAccessCache.put(cacheKey, view);this.viewCreationCache.put(cacheKey, view);}}}}return (view != UNRESOLVED_VIEW ? view : null);}}4.什么是 RESTful
REST(英?:Representational State Transfer,簡稱 REST)描述了?個(gè)架構(gòu)樣式的?絡(luò)系統(tǒng),
1.RESTfu有哪些l區(qū)別
原有的url設(shè)計(jì)http://localhost:8080/user/queryUserById.action?id=3
restful設(shè)計(jì) http://localhost:8080/user/3
常規(guī)操作就是增刪改查
根據(jù)請求?式不同,代表要做不同的操作
get 查詢,獲取資源 /account/1 HTTP GET :得到 id = 1 的 account
post 增加,新建資源
put 更新 /account/1 HTTP PUT:更新 id = 1 的 account
delete 刪除資源 /account/1 HTTP DELETE:刪除 id = 1 的 account
5.自定義攔截器
1.接口實(shí)現(xiàn)HandlerInterceptor
/** * ?定義springmvc攔截器 */ public class MyIntercepter01 implements HandlerInterceptor {/*** 會在handler?法業(yè)務(wù)邏輯執(zhí)?之前執(zhí)?* 往往在這?完成權(quán)限校驗(yàn)?作* @param request* @param response* @param handler* @return 返回值boolean代表是否放?,true代表放?,false代表中?* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("MyIntercepter01 preHandle......");return true;}/*** 會在handler?法業(yè)務(wù)邏輯執(zhí)?之后尚未跳轉(zhuǎn)??時(shí)執(zhí)?* @param request* @param response* @param handler* @param modelAndView 封裝了視圖和數(shù)據(jù),此時(shí)尚未跳轉(zhuǎn)??呢,你可以在這?針對返回的 數(shù)據(jù)和視圖信息進(jìn)?修改* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("MyIntercepter01 postHandle......");}/*** ??已經(jīng)跳轉(zhuǎn)渲染完畢之后執(zhí)?* @param request* @param response* @param handler* @param ex 可以在這?捕獲異常* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("MyIntercepter01 afterCompletion......");} }2.注冊SpringMVC攔截器
<mvc:interceptors><!--攔截所有handler--><!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>--><mvc:interceptor><!--配置當(dāng)前攔截器的url攔截規(guī)則,**代表當(dāng)前?錄下及其??錄下的所有url--><mvc:mapping path="/**"/><!--exclude-mapping可以在mapping的基礎(chǔ)上排除?些url攔截--><!--<mvc:exclude-mapping path="/demo/**"/>--><bean class="com.lagou.edu.interceptor.MyIntercepter01"/></mvc:interceptor></mvc:interceptors>6.整合SSM
數(shù)據(jù)庫連接池以及事務(wù)管理都交給Spring容器來完成
SqlSessionFactory對象應(yīng)該放到Spring容器中作為單例對象管理
Mapper動態(tài)代理對象交給Spring管理,我們從Spring容器中直接獲得Mapper的代理對象
6.1web.xml
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:applicationContext*.xml</param-value></context-param><!--spring框架啟動--><listener><listenerclass>org.springframework.web.context.ContextLoaderListener</listenerclass></listener><!--springmvc啟動--><servlet><servlet-name>springmvc</servlet-name><servletclass>org.springframework.web.servlet.DispatcherServlet</servletclass><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> </web-app6.2整合springMvc(springmvc.xml)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/springcontext.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsd "><!--掃描controller--><context:component-scan base-package="com.lagou.edu.controller"/><mvc:annotation-driven/> </beans>6.3整合Dao(applicationContext-dao.xml)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd "><!--包掃描--><context:component-scan base-package="com.lagou.edu.mapper"/><!--數(shù)據(jù)庫連接池以及事務(wù)管理都交給Spring容器來完成--><!--引?外部資源?件--><context:property-placeholderlocation="classpath:jdbc.properties"/><!--第三?jar中的bean定義在xml中--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><!--SqlSessionFactory對象應(yīng)該放到Spring容器中作為單例對象管理原來mybaits中sqlSessionFactory的構(gòu)建是需要素材的:SqlMapConfig.xml中的內(nèi) 容--><bean id="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"><!--別名映射掃描--><property name="typeAliasesPackage" value="com.lagou.edu.pojo"/><!--數(shù)據(jù)源dataSource--><property name="dataSource" ref="dataSource"/></bean><!--Mapper動態(tài)代理對象交給Spring管理,我們從Spring容器中直接獲得Mapper的代理對 象--><!--掃描mapper接?,?成代理對象,?成的代理對象會存儲在ioc容器中--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!--mapper接?包路徑配置--><property name="basePackage" value="com.lagou.edu.mapper"/><property name="sqlSessionFactoryBeanName"value="sqlSessionFactory"/></bean> </beans>6.4applicationContext-service.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:lgContext="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd "><!--包掃描--><lgContext:component-scan base-package="com.lagou.edu.service"/><!--事務(wù)管理--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--事務(wù)管理注解驅(qū)動--><tx:annotation-driven transaction-manager="transactionManager"/> </beans>7.設(shè)計(jì)模式
1.策略模式
策略模式(Strategy),就是?個(gè)問題有多種解決?案,選擇其中的?種使?,這種情況下我們使?策略模式來實(shí)現(xiàn)靈活地選擇,也能夠?便地增加新的解決?。?如做數(shù)學(xué)題,?個(gè)問題的解法可能有多種;再?如商場的打折促銷活動,打折?案也有很多種,有些商品是不參與折扣活動要按照原價(jià)銷售,有些商品打8.5折,有些打6折,有些是返現(xiàn)5元等
//打折接口的父類 public abstract class AbstractDiscount {protected double finalPrice;//最終的價(jià)格protected String desc;//詳細(xì)信息public AbstractDiscount(String desc) {this.desc = desc;}public abstract double discount(double price);//打折的方案方法 } //打折的具體方法 public class Discount6 extends AbstractDiscount {public Discount6() {super("該商品可享受6折優(yōu)惠");}@Overridepublic double discount(double price) {finalPrice = price * 0.6;return finalPrice;} } //商品對象 public class BuyGoods {private String goods; //商品名稱private double price;//商品的價(jià)格private AbstractDiscount abstractDiscount;//商品的打折類public BuyGoods(String goods, double price, AbstractDiscountabstractDiscount) {this.goods = goods;this.price = price;this.abstractDiscount = abstractDiscount;}//打折后的價(jià)格public double calculate() {double finalPrice = abstractDiscount.discount(this.price);String desc = abstractDiscount.getDesc();System.out.println(MessageFormat.format("商品:{0},原價(jià):{1},{2},最終價(jià)格為:{3}", goods, price, desc, finalPrice));return finalPrice;} } //測試類public static void main(String[] args) {BuyGoods buyGoods1 = new BuyGoods("Java編程思想", 99.00, newDiscount85());buyGoods1.calculate();BuyGoods buyGoods2 = new BuyGoods("羅技?標(biāo)", 66, new Discount6());buyGoods2.calculate();} //商品:Java編程思想,原價(jià):99,該商品可享受8.5折優(yōu)惠,最終價(jià)格為:84.15 //商品:羅技?標(biāo),原價(jià):66,該商品可享受6折優(yōu)惠,最終價(jià)格為:39.62.模板方法
模板?法模式是指定義?個(gè)算法的?架,并允許?類為?個(gè)或者多個(gè)步驟提供實(shí)現(xiàn)。模板?法模式使得?類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法的某些步驟,屬于?為型設(shè)計(jì)模式。采?模板?法模式的核?思路是處理某個(gè)流程的代碼已經(jīng)具備,但其中某些節(jié)點(diǎn)的代碼暫時(shí)不能確定。此時(shí)可以使?模板?法。
//主類 public abstract class Interview {private final void register() {System.out.println("?試登記");}//不確定的方法protected abstract void communicate();private final void notifyResult() {System.out.println("HR?姐姐通知?試結(jié)果");}protected final void process() {this.register();this.communicate();this.notifyResult();} } //繼承調(diào)整方法 public class Interviewee1 extends Interview{public void communicate() {System.out.println("我是?試?員1,來?試Java?程師,我們聊的是Java相關(guān)內(nèi)容");} } //測試類public static void main(String[] args) {// ?試Java?程師Interview interviewee1 = new Interviewee1();interviewee1.process();} //?試登記 //我是?試?員1,來?試Java?程師,我們聊的是Java相關(guān)內(nèi)容 //HR?姐姐通知?試結(jié)果3.適配器模式
使得原本由于接?不兼容?不能?起?作、不能統(tǒng)?管理的那些類可以?起?作、可以進(jìn)?統(tǒng)?管理解決接?不兼容?不能?起?作問題,
看下??個(gè)?常經(jīng)典的案例
在中國,??電都是220v交流電,但是?機(jī)鋰電池?的都是5v直流電。因此,我們給?機(jī)充電時(shí)就需要使?電源適配器來進(jìn)?轉(zhuǎn)換。使?代碼還原這個(gè)?活場景
//220V類 public class AC220 {public int outputAC220V() {int output = 220;System.out.println("輸出交流電" + output + "V");return output;} } //5V接口 public interface DC5 {int outputDC5V(); }//5V適配 public class PowerAdapter implements DC5 {private AC220 ac220;public PowerAdapter(AC220 ac220) {this.ac220 = ac220;}public int outputDC5V() {int adapterInput = ac220.outputAC220V();// 變壓器...int adapterOutput = adapterInput / 44;System.out.println("使? PowerAdapter 輸?AC:" + adapterInput + "V 輸出DC:" + adapterOutput + "V");return adapterOutput;} } //測試類public static void main(String[] args) {DC5 dc5 = new PowerAdapter(new AC220());dc5.outputDC5V();}問題
1.攔截器,監(jiān)聽器,過濾器
過濾器(Filter):在web.xml中配置 用來配置Post請求的編碼過濾器
對Request請求起到過濾的作用,如果配置為/ 可以對所有的資源訪問進(jìn)行過濾
監(jiān)聽器(Listener):實(shí)現(xiàn)了javax.servlet.ServletContextListener 接?的服務(wù)器端組件,它隨
Web應(yīng)?的啟動?啟動,只初始化?次,然后會?直運(yùn)?監(jiān)視,隨Web應(yīng)?的停??銷毀
作??:做?些初始化?作,web應(yīng)?中spring容器啟動ContextLoaderListener
作??:監(jiān)聽web中的特定事件,?如HttpSession,ServletRequest的創(chuàng)建和銷毀;變量的創(chuàng)建、
銷毀和修改等。可以在某些動作前后增加處理,實(shí)現(xiàn)監(jiān)控,?如統(tǒng)計(jì)在線?數(shù),利?
HttpSessionLisener等。
public class MyHttpSessionListener implements HttpSessionListener {//在線人數(shù)public static int online = 0;@Overridepublic void sessionCreated(HttpSessionEvent se) {System.out.println("創(chuàng)建session");online ++;}@Overridepublic void sessionDestroyed(HttpSessionEvent se) {System.out.println("銷毀session");}} //controller@RequestMapping("/online")@ResponseBodypublic Object online() {return "當(dāng)前在線人數(shù):" + MyHttpSessionListener.online + "人";} <!--*****配置spring的核心監(jiān)聽器*****--><!--指定spring的配置文件的位置--><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/application*.xml</param-value></context-param><!--spring封裝的核心監(jiān)聽器ContextLoaderListener--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>攔截器(Interceptor):是SpringMVC、Struts等表現(xiàn)層框架??的,不會攔截jsp/html/css/image的訪問等,只會攔截訪問的控制器?法(Handler)。
注冊SpringMvc攔截器
<!--攔截所有handler--><!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>--><mvc:interceptors><mvc:interceptor><!--配置當(dāng)前攔截器的url攔截規(guī)則,**代表當(dāng)前?錄下及其??錄下的所有url--><mvc:mapping path="/**"/><!--exclude-mapping可以在mapping的基礎(chǔ)上排除?些url攔截--><!--<mvc:exclude-mapping path="/demo/**"/>--><bean class="com.lagou.edu.interceptor.MyIntercepter01"/></mvc:interceptor><mvc:interceptor><mvc:mapping path="/**"/><bean class="com.lagou.edu.interceptor.MyIntercepter02"/></mvc:interceptor></mvc:interceptors>配置的角度
從配置的?度也能夠總結(jié)發(fā)現(xiàn):serlvet、fifilter、listener是配置在web.xml中的,?interceptor是配置在表現(xiàn)層框架??的配置?件中的
2.Pattern和Matcher用法
Pattern pattern = Pattern.compile("Java"); String test="123Java456Java789Java"; String[] result = pattern.split(test); for(String s : result) System.out.println(s); } //123 456 789Pattern類也自帶一個(gè)靜態(tài)匹配方法matches(String regex, CharSequence input),但只能進(jìn)行全字符串匹配并且只能返回是否匹配上的boolean值
String test1 = "Java"; String test2 = "Java123456";System.out.println(Pattern.matches("Java",test1));//返回true System.out.println(Pattern.matches("Java",test2));//返回false3.亂碼問題解決
###3.1 Post亂碼
Post請求亂碼,web.xml中加?過濾器
3.2get亂碼
Get請求亂碼(Get請求亂碼需要修改tomcat下server.xml的配置)
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>4.自定義框架式 參數(shù)是arg2
在web.xml中配置參數(shù)
<!--編譯插件定義編譯細(xì)節(jié)--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>8</source><target>8</target><encoding>utf-8</encoding><!--告訴編譯器,編譯的時(shí)候記錄下形參的真實(shí)名稱--><compilerArgs><arg>-parameters</arg></compilerArgs></configuration></plugin>單詞
Flash 閃光
retrieve檢索
specific特殊的,具體的
delegates 代表
Determine 確定決定
Modified 修改
process 進(jìn)程,流程
theme 主題
previous 以前
Internal 內(nèi)部
discoverer 發(fā)現(xiàn)
Destruction 破壞
Discovery發(fā)現(xiàn)
Provided提供,假如 》findProvidedArgument
resolve 解析,決心》resolveArgument
總結(jié)
以上是生活随笔為你收集整理的springMvc源码刨析笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot源码笔记分析
- 下一篇: spring源码刨析总结