springMvc源码刨析笔记

tech2023-02-16  87

springMvc源码刨析笔记

MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式。

1.执行流程

2.九大组件

//文件上传组件 initMultipartResolver(context); //初始化本地语言环境 initLocaleResolver(context); //初始化模板处理器 initThemeResolver(context); //初始化HandleMapping initHandlerMappings(context); //初始化参数适配器 initHandlerAdapters(context); //初始化异常拦截器 initHandlerExceptionResolvers(context); //初始化试视图预处理器 initRequestToViewNameTranslator(context); //试图转换器 initViewResolvers(context); //falshMap管理器 initFlashMapManager(context);

2.自定义springMvc框架

1.声明创建(LgDispatcherServlet)并继承Servlet

springmvc.properties

scanPackage=com.lagou.demo

LgDispatcherServlet 实现了servlet对象的init() doGet() doPost()方法

public class LgDispatcherServlet extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected 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.properties String 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 扫描相关的类,扫描注解 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缓存的类的全限定类名,以及反射技术,完成对象创建和管理 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); // 区分controller,区分service' if(aClass.isAnnotationPresent(LagouController.class)) { // controller的id此处不做过多处理,不取value了,就拿类的首字母小写作为id,保存到ioc中 String simpleName = aClass.getSimpleName();// DemoController String lowerFirstSimpleName = lowerFirst(simpleName); // demoController Object 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,就以指定的为准 if(!"".equals(beanName.trim())) { ioc.put(beanName,aClass.newInstance()); }else{ // 如果没有指定,就以类名首字母小写 beanName = lowerFirst(aClass.getSimpleName()); ioc.put(beanName,aClass.newInstance()); } // service层往往是有接口的,面向接口开发,此时再以接口名为id,放入一份对象到ioc中,便于后期根据接口类型注入 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.实例化对象(AutoWired)

// 实现依赖注入 doAutoWired(); // 实现依赖注入 private void doAutoWired() { if(ioc.isEmpty()) {return;} // 有对象,再进行依赖注入处理 // 遍历ioc中所有对象,查看对象中的字段,是否有@LagouAutowired注解,如果有需要维护依赖注入关系 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的id if("".equals(beanName.trim())) { // 没有配置具体的bean id,那就需要根据当前字段类型注入(接口注入) IDemoService beanName = 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之间的映射关系 private List<Handler> handlerMapping = new ArrayList<>(); =============================================================== private void initHandlerMapping() { if(ioc.isEmpty()) {return;} for(Map.Entry<String,Object> entry: ioc.entrySet()) { // 获取ioc中当前遍历的对象的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]; // 方法没有标识LagouRequestMapping,就不处理 if(!method.isAnnotationPresent(LagouRequestMapping.class)) {continue;} // 如果标识,就处理 LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class); String methodUrl = annotation.value(); // /query String url = baseUrl + methodUrl; // 计算出来的url /demo/query // 把method所有信息及url封装为一个Handler Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url)); // 计算方法的参数位置信息 // 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对象,那么参数名称写HttpServletRequest和HttpServletResponse handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j); }else{ handler.getParamIndexMapping().put(parameter.getName(),j); // <name,2> } } // 建立url和method之间的映射关系(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; // 参数顺序,是为了进行参数绑定,key是参数名,value代表是第几个参数 <name,2> }

8.post请求参数处理

@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理请求:根据url,找到对应的Method方法,进行调用 // 获取uri // String requestURI = req.getRequestURI(); // Method method = handlerMapping.get(requestURI);// 获取到一个反射的方法 // 反射调用,需要传入对象,需要传入参数,此处无法完成调用,没有把对象缓存起来,也没有参数!!!!改造initHandlerMapping(); // method.invoke() // // 根据uri获取到能够处理当前请求的hanlder(从handlermapping中(list)) Handler handler = getHandler(req); if(handler == null) { resp.getWriter().write("404 not found"); return; } // 参数绑定 // 获取所有参数类型数组,这个数组的长度就是我们最后要传入的args数组的长度 Class<?>[] parameterTypes = handler.getMethod().getParameterTypes(); // 根据上述数组长度创建一个新的数组(参数数组,是要传入反射调用的) Object[] paraValues = new Object[parameterTypes.length]; // 以下就是为了向参数数组中塞值,而且还得保证参数的顺序和方法中形参顺序一致 Map<String, String[]> parameterMap = req.getParameterMap(); // 遍历request中所有参数 (填充除了request,response之外的参数) for(Map.Entry<String,String[]> param: parameterMap.entrySet()) { // name=1&name=2 name [1,2] String value = StringUtils.join(param.getValue(), ","); // 如同 1,2 // 如果参数和方法中的参数匹配上了,填充数据 if(!handler.getParamIndexMapping().containsKey(param.getKey())) {continue;} // 方法形参确实有该参数,找到它的索引位置,对应的把参数值放入paraValues Integer index = handler.getParamIndexMapping().get(param.getKey());//name在第 2 个位置 paraValues[index] = value; // 把前台传递过来的参数值填充到对应的位置去 } int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0 paraValues[requestIndex] = req; int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1 paraValues[responseIndex] = resp; // 最终调用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/query String 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之间的映射关系 private List<Handler> handlerMapping = new ArrayList<>(); @Override public void init(ServletConfig config) throws ServletException { // 1 加载配置文件 springmvc.properties String contextConfigLocation = config.getInitParameter("contextConfigLocation"); doLoadConfig(contextConfigLocation); // 2 扫描相关的类,扫描注解 doScan(properties.getProperty("scanPackage")); // 3 初始化bean对象(实现ioc容器,基于注解) doInstance(); // 4 实现依赖注入 doAutoWired(); // 5 构造一个HandlerMapping处理器映射器,将配置好的url和Method建立映射关系 initHandlerMapping(); System.out.println("lagou mvc 初始化完成...."); // 等待请求进入,处理请求 } /* 构造一个HandlerMapping处理器映射器 最关键的环节 目的:将url和method建立关联 */ private void initHandlerMapping() { if(ioc.isEmpty()) {return;} for(Map.Entry<String,Object> entry: ioc.entrySet()) { // 获取ioc中当前遍历的对象的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]; // 方法没有标识LagouRequestMapping,就不处理 if(!method.isAnnotationPresent(LagouRequestMapping.class)) {continue;} // 如果标识,就处理 LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class); String methodUrl = annotation.value(); // /query String url = baseUrl + methodUrl; // 计算出来的url /demo/query // 把method所有信息及url封装为一个Handler Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url)); // 计算方法的参数位置信息 // 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对象,那么参数名称写HttpServletRequest和HttpServletResponse handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j); }else{ handler.getParamIndexMapping().put(parameter.getName(),j); // <name,2> } } //*******************针对Security的处理,start************************ // 必须先有Controller的权限,然后才能有Handler方法的权限,所以有如下情况 // 1 如果Controller和Handler都配置了@Security(取交集) // 2 如果仅仅Controller配置了@Security(以Controller为准) // 3 如果仅仅Handler方法配置了@Security(以Handler方法为准) 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之间的映射关系(map缓存起来) handlerMapping.add(handler); } } } // 实现依赖注入 private void doAutoWired() { if(ioc.isEmpty()) {return;} // 有对象,再进行依赖注入处理 // 遍历ioc中所有对象,查看对象中的字段,是否有@LagouAutowired注解,如果有需要维护依赖注入关系 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的id if("".equals(beanName.trim())) { // 没有配置具体的bean id,那就需要根据当前字段类型注入(接口注入) IDemoService beanName = declaredField.getType().getName(); } // 开启赋值 declaredField.setAccessible(true); try { declaredField.set(entry.getValue(),ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } // ioc容器 // 基于classNames缓存的类的全限定类名,以及反射技术,完成对象创建和管理 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); // 区分controller,区分service' if(aClass.isAnnotationPresent(LagouController.class)) { // controller的id此处不做过多处理,不取value了,就拿类的首字母小写作为id,保存到ioc中 String simpleName = aClass.getSimpleName();// DemoController String lowerFirstSimpleName = lowerFirst(simpleName); // demoController Object 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,就以指定的为准 if(!"".equals(beanName.trim())) { ioc.put(beanName,aClass.newInstance()); }else{ // 如果没有指定,就以类名首字母小写 beanName = lowerFirst(aClass.getSimpleName()); ioc.put(beanName,aClass.newInstance()); } // service层往往是有接口的,面向接口开发,此时再以接口名为id,放入一份对象到ioc中,便于后期根据接口类型注入 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/demo 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); } } } // 加载配置文件 private void doLoadConfig(String contextConfigLocation) { InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try { properties.load(resourceAsStream); } catch (IOException e) { e.printStackTrace(); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 处理请求:根据url,找到对应的Method方法,进行调用 // 获取uri // String requestURI = req.getRequestURI(); // Method method = handlerMapping.get(requestURI);// 获取到一个反射的方法 // 反射调用,需要传入对象,需要传入参数,此处无法完成调用,没有把对象缓存起来,也没有参数!!!!改造initHandlerMapping(); // method.invoke() // // 根据uri获取到能够处理当前请求的hanlder(从handlermapping中(list)) Handler handler = getHandler(req); if(handler == null) { resp.getWriter().write("404 not found"); return; } // 安全认证 List<String> usernameList = securityMap.get(handler); // 不为空说明需要认证,那么不包含的话就return if(usernameList != null && !usernameList.contains(req.getParameter("username"))) { resp.getWriter().write("sorry,the request has been forbidden---security"); return; } // 参数绑定 // 获取所有参数类型数组,这个数组的长度就是我们最后要传入的args数组的长度 Class<?>[] parameterTypes = handler.getMethod().getParameterTypes(); // 根据上述数组长度创建一个新的数组(参数数组,是要传入反射调用的) Object[] paraValues = new Object[parameterTypes.length]; // 以下就是为了向参数数组中塞值,而且还得保证参数的顺序和方法中形参顺序一致 Map<String, String[]> parameterMap = req.getParameterMap(); // 遍历request中所有参数 (填充除了request,response之外的参数) for(Map.Entry<String,String[]> param: parameterMap.entrySet()) { // name=1&name=2 name [1,2] String value = StringUtils.join(param.getValue(), ","); // 如同 1,2 // 如果参数和方法中的参数匹配上了,填充数据 if(!handler.getParamIndexMapping().containsKey(param.getKey())) {continue;} // 方法形参确实有该参数,找到它的索引位置,对应的把参数值放入paraValues Integer index = handler.getParamIndexMapping().get(param.getKey());//name在第 2 个位置 paraValues[index] = value; // 把前台传递过来的参数值填充到对应的位置去 } int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); // 0 paraValues[requestIndex] = req; int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); // 1 paraValues[responseIndex] = resp; // 最终调用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 { //执行请求 参数 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); } //====================================================== public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //获取方法参数值 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); return doInvoke(args); } //=================getMethodArgumentValues=================================== protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //获取方法的参数 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) { //拦截器的第三次执行的地方 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. //创建视图 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)描述了⼀个架构样式的⽹络系统,

1.RESTfu有哪些l区别

原有的url设计http://localhost:8080/user/queryUserById.action?id=3 restful设计 http://localhost:8080/user/3 常规操作就是增删改查 根据请求⽅式不同,代表要做不同的操作 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

<form method="post" action="/demo/handle"> <input type="text" name="username"/> <input type="submit" value="提交rest_post请求"/> </form> <form method="post" action="/demo/handle/15/lisi"> <input type="hidden" name="_method" value="put"/> <input type="submit" value="提交rest_put请求"/> </form> @RequestMapping(value = "/handle/{id}/{name}",method = {RequestMethod.PUT}) public ModelAndView handlePut(@PathVariable("id") Integer id,@PathVariable("name") String username) { Date date = new Date(); ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("date",date); modelAndView.setViewName("success"); return modelAndView; } <!--配置springmvc请求⽅式转换过滤器,会检查请求参数中是否有_method参数,如果有就 按照指定的请求⽅式进⾏转换--> <filter> <filter-name>hiddenHttpMethodFilter</filter-name> <filterclass>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>hiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

5.自定义拦截器

1.接口实现HandlerInterceptor

/** * ⾃定义springmvc拦截器 */ public class MyIntercepter01 implements HandlerInterceptor { /** * 会在handler⽅法业务逻辑执⾏之前执⾏ * 往往在这⾥完成权限校验⼯作 * @param request * @param response * @param handler * @return 返回值boolean代表是否放⾏,true代表放⾏,false代表中⽌ * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyIntercepter01 preHandle......"); return true; } /** * 会在handler⽅法业务逻辑执⾏之后尚未跳转⻚⾯时执⾏ * @param request * @param response * @param handler * @param modelAndView 封装了视图和数据,此时尚未跳转⻚⾯呢,你可以在这⾥针对返回的 数据和视图信息进⾏修改 * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("MyIntercepter01 postHandle......"); } /** * ⻚⾯已经跳转渲染完毕之后执⾏ * @param request * @param response * @param handler * @param ex 可以在这⾥捕获异常 * @throws Exception */ @Override public 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> <!--配置当前拦截器的url拦截规则,**代表当前⽬录下及其⼦⽬录下的所有url--> <mvc:mapping path="/**"/> <!--exclude-mapping可以在mapping的基础上排除⼀些url拦截--> <!--<mvc:exclude-mapping path="/demo/**"/>--> <bean class="com.lagou.edu.interceptor.MyIntercepter01"/> </mvc:interceptor> </mvc:interceptors>

6.整合SSM

数据库连接池以及事务管理都交给Spring容器来完成

SqlSessionFactory对象应该放到Spring容器中作为单例对象管理

Mapper动态代理对象交给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-app

6.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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/springcontext.xsd http://www.springframework.org/schema/mvc http://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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> <!--包扫描--> <context:component-scan base-package="com.lagou.edu.mapper"/> <!--数据库连接池以及事务管理都交给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对象应该放到Spring容器中作为单例对象管理 原来mybaits中sqlSessionFactory的构建是需要素材的:SqlMapConfig.xml中的内 容 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--别名映射扫描--> <property name="typeAliasesPackage" value="com.lagou.edu.pojo"/> <!--数据源dataSource--> <property name="dataSource" ref="dataSource"/> </bean> <!--Mapper动态代理对象交给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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> <!--包扫描--> <lgContext:component-scan base-package="com.lagou.edu.service"/> <!--事务管理--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--事务管理注解驱动--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>

7.设计模式

1.策略模式

策略模式(Strategy),就是⼀个问题有多种解决⽅案,选择其中的⼀种使⽤,这种情况下我们使⽤策略模式来实现灵活地选择,也能够⽅便地增加新的解决⽅。⽐如做数学题,⼀个问题的解法可能有多种;再⽐如商场的打折促销活动,打折⽅案也有很多种,有些商品是不参与折扣活动要按照原价销售,有些商品打8.5折,有些打6折,有些是返现5元等

//打折接口的父类 public abstract class AbstractDiscount { protected double finalPrice;//最终的价格 protected String desc;//详细信息 public AbstractDiscount(String desc) { this.desc = desc; } public abstract double discount(double price);//打折的方案方法 } //打折的具体方法 public class Discount6 extends AbstractDiscount { public Discount6() { super("该商品可享受6折优惠"); } @Override public double discount(double price) { finalPrice = price * 0.6; return finalPrice; } } //商品对象 public class BuyGoods { private String goods; //商品名称 private double price;//商品的价格 private AbstractDiscount abstractDiscount;//商品的打折类 public BuyGoods(String goods, double price, AbstractDiscount abstractDiscount) { this.goods = goods; this.price = price; this.abstractDiscount = abstractDiscount; } //打折后的价格 public double calculate() { double finalPrice = abstractDiscount.discount(this.price); String desc = abstractDiscount.getDesc(); System.out.println(MessageFormat.format("商品:{0},原价:{1},{2},最终价格为:{3}", goods, price, desc, finalPrice)); return finalPrice; } } //测试类 public static void main(String[] args) { BuyGoods buyGoods1 = new BuyGoods("Java编程思想", 99.00, new Discount85()); buyGoods1.calculate(); BuyGoods buyGoods2 = new BuyGoods("罗技⿏标", 66, new Discount6()); buyGoods2.calculate(); } //商品:Java编程思想,原价:99,该商品可享受8.5折优惠,最终价格为:84.15 //商品:罗技⿏标,原价:66,该商品可享受6折优惠,最终价格为:39.6

2.模板方法

模板⽅法模式是指定义⼀个算法的⻣架,并允许⼦类为⼀个或者多个步骤提供实现。模板⽅法模式使得⼦类可以在不改变算法结构的情况下,重新定义算法的某些步骤,属于⾏为型设计模式。采⽤模板⽅法模式的核⼼思路是处理某个流程的代码已经具备,但其中某些节点的代码暂时不能确定。此时可以使⽤模板⽅法。

//主类 public abstract class Interview { private final void register() { System.out.println("⾯试登记"); } //不确定的方法 protected abstract void communicate(); private final void notifyResult() { System.out.println("HR⼩姐姐通知⾯试结果"); } protected final void process() { this.register(); this.communicate(); this.notifyResult(); } } //继承调整方法 public class Interviewee1 extends Interview{ public void communicate() { System.out.println("我是⾯试⼈员1,来⾯试Java⼯程师,我们聊的是Java相关内容"); } } //测试类 public static void main(String[] args) { // ⾯试Java⼯程师 Interview interviewee1 = new Interviewee1(); interviewee1.process(); } //⾯试登记 //我是⾯试⼈员1,来⾯试Java⼯程师,我们聊的是Java相关内容 //HR⼩姐姐通知⾯试结果

3.适配器模式

使得原本由于接⼝不兼容⽽不能⼀起⼯作、不能统⼀管理的那些类可以⼀起⼯作、可以进⾏统⼀管理解决接⼝不兼容⽽不能⼀起⼯作问题,

看下⾯⼀个⾮常经典的案例

在中国,⺠⽤电都是220v交流电,但是⼿机锂电池⽤的都是5v直流电。因此,我们给⼿机充电时就需要使⽤电源适配器来进⾏转换。使⽤代码还原这个⽣活场景

//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.拦截器,监听器,过滤器

过滤器(Filter):在web.xml中配置 用来配置Post请求的编码过滤器

对Request请求起到过滤的作用,如果配置为/ 可以对所有的资源访问进行过滤

<filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

监听器(Listener):实现了javax.servlet.ServletContextListener 接⼝的服务器端组件,它随

Web应⽤的启动⽽启动,只初始化⼀次,然后会⼀直运⾏监视,随Web应⽤的停⽌⽽销毁

作⽤⼀:做⼀些初始化⼯作,web应⽤中spring容器启动ContextLoaderListener

作⽤⼆:监听web中的特定事件,⽐如HttpSession,ServletRequest的创建和销毁;变量的创建、

销毁和修改等。可以在某些动作前后增加处理,实现监控,⽐如统计在线⼈数,利⽤

HttpSessionLisener等。

public class MyHttpSessionListener implements HttpSessionListener { //在线人数 public static int online = 0; @Override public void sessionCreated(HttpSessionEvent se) { System.out.println("创建session"); online ++; } @Override public void sessionDestroyed(HttpSessionEvent se) { System.out.println("销毁session"); } } //controller @RequestMapping("/online") @ResponseBody public Object online() { return "当前在线人数:" + MyHttpSessionListener.online + "人"; } <!--*****配置spring的核心监听器*****--> <!--指定spring的配置文件的位置--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/application*.xml</param-value> </context-param> <!--spring封装的核心监听器ContextLoaderListener--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

拦截器(Interceptor):是SpringMVC、Struts等表现层框架⾃⼰的,不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器⽅法(Handler)。

/** * ⾃定义springmvc拦截器 */ public class MyIntercepter01 implements HandlerInterceptor { /** 往往在这⾥完成权限校验⼯作 * @param request * @param response * @param handler * @return 返回值boolean代表是否放⾏,true代表放⾏,false代表中⽌ * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("MyIntercepter01 preHandle......"); return true; } /** * 会在handler⽅法业务逻辑执⾏之后尚未跳转⻚⾯时执⾏ * @param request * @param response * @param handler * @param modelAndView 封装了视图和数据,此时尚未跳转⻚⾯呢,你可以在这⾥针对返回的 数据和视图信息进⾏修改 * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("MyIntercepter01 postHandle......"); } /** * ⻚⾯已经跳转渲染完毕之后执⾏ * @param request * @param response * @param handler * @param ex 可以在这⾥捕获异常 * @throws Exception */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyIntercepter01 afterCompletion......"); } }

注册SpringMvc拦截器

<!--拦截所有handler--> <!--<bean class="com.lagou.edu.interceptor.MyIntercepter01"/>--> <mvc:interceptors> <mvc:interceptor> <!--配置当前拦截器的url拦截规则,**代表当前⽬录下及其⼦⽬录下的所有url--> <mvc:mapping path="/**"/> <!--exclude-mapping可以在mapping的基础上排除⼀些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>

配置的角度

从配置的⻆度也能够总结发现:serlvet、fifilter、listener是配置在web.xml中的,⽽interceptor是配置在表现层框架⾃⼰的配置⽂件中的

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 789

Pattern类也自带一个静态匹配方法matches(String regex, CharSequence input),但只能进行全字符串匹配并且只能返回是否匹配上的boolean值

String test1 = "Java"; String test2 = "Java123456"; System.out.println(Pattern.matches("Java",test1));//返回true System.out.println(Pattern.matches("Java",test2));//返回false

3.乱码问题解决

###3.1 Post乱码 Post请求乱码,web.xml中加⼊过滤器

<!-- 解决post乱码问题 --> <filter> <filter-name>encoding</filter-name> <filter-class> org.springframework.web.filter.CharacterEncodingFilter </filter-class> <!-- 设置编码参是UTF8 --> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

3.2get乱码

Get请求乱码(Get请求乱码需要修改tomcat下server.xml的配置)

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

4.自定义框架式 参数是arg2

在web.xml中配置参数

<!--编译插件定义编译细节--> <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> <!--告诉编译器,编译的时候记录下形参的真实名称--> <compilerArgs> <arg>-parameters</arg> </compilerArgs> </configuration> </plugin>

单词

Flash 闪光 retrieve检索 specific特殊的,具体的 delegates 代表 Determine 确定决定 Modified 修改 process 进程,流程 theme 主题 previous 以前 Internal 内部 discoverer 发现 Destruction 破坏 Discovery发现 Provided提供,假如 》findProvidedArgument resolve 解析,决心》resolveArgument

最新回复(0)