算命先生的网站怎么做,如何购买大量客户电话号码,昆明网站优化,网站自己可以做么前文Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架#xff0c;通过把Model#xff0c;View#xff0c;Controller分离#xff0c;将web层进行职责解耦#xff0c;把复杂的web应用分成逻辑清晰的几部分#xff0c;简化开发#xff0c;减少出…前文Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架通过把ModelViewController分离将web层进行职责解耦把复杂的web应用分成逻辑清晰的几部分简化开发减少出错方便组内开发人员之间的配合。Springmvc的优点:可以支持各种视图技术,而不仅仅局限于JSP与Spring框架集成如IoC容器、AOP等清晰的角色分配前端控制器(dispatcherServlet) , 请求到处理器映射handlerMapping), 处理器适配器HandlerAdapter), 视图解析器ViewResolver。支持各种请求资源的映射策略。请求映射器源码解析这些优秀的特性使得它在企业级开发中使用率超过98%如此优秀的框架你是否疑惑过在一个请求到达后是如何被SpringMvc拦截到并处理的相信大家对上面的流程图都很熟悉或多或少无论是在准备面试的时候还是自己学习的时候都会接触到这个流程图我见过很多的人对着这个图死记硬背我也面试过一些技术人员问到这块知识仰着头闭着眼夸张一下把这块知识说出来再往深了问一点就懵逼归根到底就是对框架理解不够深刻。SpringMVC是如何感知到每个方法对应的url路径的org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 实现 org.springframework.beans.factory.InitializingBean 覆盖 afterPropertiesSet方法这个方法会在Spring容器初始化的时候回调该方法该方法类定义为Override
public void afterPropertiesSet() {initHandlerMethods();
}
复制代码调用initHandlerMethods方法那么initHandlerMethods里面干了什么事情呢对该方法逐步分析/*** Scan beans in the ApplicationContext, detect and register handler methods.* see #getCandidateBeanNames()* see #processCandidateBean* see #handlerMethodsInitialized*/
protected void initHandlerMethods() {for (String beanName : getCandidateBeanNames()) {if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {processCandidateBean(beanName);}}handlerMethodsInitialized(getHandlerMethods());
}首先 getCandidateBeanNames() 方法我们看它的定义/*** Determine the names of candidate beans in the application context.* since 5.1* see #setDetectHandlerMethodsInAncestorContexts* see BeanFactoryUtils#beanNamesForTypeIncludingAncestors*/
protected String[] getCandidateBeanNames() {return (this.detectHandlerMethodsInAncestorContexts ?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :obtainApplicationContext().getBeanNamesForType(Object.class));
}这个方法本质的目的就是为了从bean容器中获取所有的bean,为什么是获取全部的 因为它是基于Object.class类型来获取的类故而是全部的类但是这个方法其实深究起来知识点很多因为它涉及到Spring父子容器的知识点所以我决定后面花一篇文档单独去讲这里你只需要知道这个方法可以获取Spring容器里面所有的bean然后返回initHandlerMethods() 获取到所有的bean之后然后循环遍历我们将目光聚集在循环体内部的processCandidateBean方法protected void processCandidateBean(String beanName) {Class? beanType null;try {beanType obtainApplicationContext().getType(beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - lets ignore it.if (logger.isTraceEnabled()) {logger.trace(Could not resolve type for bean beanName , ex);}}if (beanType ! null isHandler(beanType)) {detectHandlerMethods(beanName);}
}beanType obtainApplicationContext().getType(beanName); 这个方法是基于bean名称获取该类的Class对象isHandler(beanType) 这个方法是判断该类是是加注了Controller注解或者RequestMappingOverride
protected boolean isHandler(Class? beanType) {return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}detectHandlerMethods(Object handler)MapMethod, T methods MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookupT) method - {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException(Invalid mapping on handler class [ userType.getName() ]: method, ex);}});内部该段逻辑可以遍历某个类下所有的方法getMappingForMethod(method, userType); 这个方法的内部做了什么呢 该i方内部读取所有的映射方法的所有定义具体的逻辑如下设置了该方法 的映射路径方法对象方法参数设置的方法请求头消费类型可接受类型映射名称等信息封装成RequestMappingInfo对象返回getPathPrefix(handlerType); 该方法是处理方法前缀如果存在和前者方法级别的合并最终返回一个方法与方法描述信息的map映射集合(MapMethod, RequestMappingInfo),循环遍历该集合 Method invocableMethod AopUtils.selectInvocableMethod(method, userType);找到该方法的代理方法 registerHandlerMethod(handler, invocableMethod, mapping);注册该方法 我们深入该方法摒弃其他与本文无关的代码会发现这么一段代码会发现我们方法上标注的 url会和前面读取的该方法的定义绑定在一个叫做 urlLookup的方法里面请大家记住这个方法这个方法对我们理解SpringMvc的处理逻辑有大用处3.请求获取逻辑源码解析现在整个工程所有对应的requestMapping的方法已经被缓存以该方法为例子RestController
public class TestController {RequestMapping(test)public String test(){return success;}
}现在在urlLookup属性里面就有一个 key为test,value为test()方法详细定义的 k:v键值对:v:我们看下下面这个类图DispatcherServlet这个关键的中央类实际上是Servlet的子类熟悉Servlet的同学都知道之前在做Servlet开发的时候所有的请求经过配置后都会被内部的doget和dopost方法拦截至此SpringMvc为什么能够拦截URL也就不难分析了拦截到url后进入如下的流程调用链请求经由 org.springframework.web.servlet.FrameworkServlet#doGet捕获委托给org.springframework.web.servlet.FrameworkServlet#processRequest方法最后在调用org.springframework.web.servlet.DispatcherServlet#doService来处理真正的逻辑我们看一下这个方法里面的一些主要逻辑吧org.springframework.web.servlet.DispatcherServlet#doDispatch调用org.springframework.web.servlet.DispatcherServlet#getHandler方法再次调用org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler经由org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法的org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod的org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl讲过这么长的调用链是不是懵了此时我们终于看到了正主/*** Return matches for the given URL path. Not thread-safe.* see #acquireReadLock()*/
Nullable
public ListT getMappingsByUrl(String urlPath) {return this.urlLookup.get(urlPath);
}这段代码是不是熟悉这就是我们Spring容器在初始化的时候将url和方法定义放置的那个属性现在Spring容器经由DispatcherServlet拦截请求后又重新找到该方法并且返回此时就完成了MVC流程图里面的HandlerMapping处理映射器的部分本章关于请求映射器的源码分析到这也就结束了后续作者会将处理适配器处理器,视图解析器一一讲明白其实后续的逻辑也就很简单了简单来说拿到方法后反射执行该方法不一定一般场景是这样然后拿到返回值判断是否有responseBody注解判断是否需要转换成json再通过write写回到页面大致流程就是这样详细过程作者后续会写经过今天的流程分析你能否基于Servlet写一个属于自己的SpringMvc呢