到本篇文章spring的IOC DI AOP原理就分析完了!但是仅仅是容器内的的调用。
今天继续来分析下spring MVC框架的源码,看下是怎么实现发送一个请求返回数据或者页面的。
MVC原理分析
预先准备
- 看过手写的应该都知道,MVC基于tomcat容器,依赖servlet包。这里直接采用springboot,引入web包就好了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 准备一个Controller
@RequestMapping("/v1")
public class HelloController{
@RequestMapping("/index")
@ResponseBody
public String hello() {
return "hello world";
}
@RequestMapping("/home.html")
@ResponseBody
public String home() {
return "home";
}
}
MVC handleMapping初始化时序图
- 入口在IOC容器的回调
MVC初始化时序图
- HttpServlet包下init方法
MVC调用阶段时序图
本章源码比较开散,请自备好晕车药!
MVC源码分析
1.HandlerMapping 初始化阶段
- 上篇文章有提到过MVC的入口,找到
AbstractAutowireCapableBeanFactory.doCreateBean
依赖注入完之后的这行代码
exposedObject = initializeBean(beanName, exposedObject, mbd);
//初始化之后
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
......
try {
//mvc在这里实现 接下来走这里
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//aop在这里实现
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
//初始化完bean之后对实现了InitializingBean的类进行通知
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
......
//多余的去掉
//重点看这里
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { //发送通知
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//发送回调通知
((InitializingBean) bean).afterPropertiesSet();
}
}
}
- 既然是MVC,那么就去MVC包里找实现了
InitializingBean
接口的类,于是在spring-mvc包下找到org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
这个类,因为现在是基于springboot架子分析,为什么到这个类就不多说了,看完springboot自动注入篇就懂了
@Override
public void afterPropertiesSet() {
//初始化一些配置 RequestMappingInfo.BuilderConfiguration config
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
//调用父类的方法
super.afterPropertiesSet();
}
- 接下来到
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
这个类
@Override
public void afterPropertiesSet() {
initHandlerMethods(); //初始化mapping的入口
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
//beanName 不是已 ‘scopedTarget.’ 开头的
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//走这里
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
//是否满足解析的条件
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//通过getType去容器中取出实例对象
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
//取出类型并是handler的才走下面 先看isHandler
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
//先看下 isHandler 只有加了这两个注解的才解析
protected boolean isHandler(Class<?> beanType) {
//@Controller || @RequestMapping
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
//接下来detectHandlerMethods 找到对应关系并注册
protected void detectHandlerMethods(Object handler) {
//用beanName 通过type获取class
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
//获的class方法与路径的对应关系 存储的是class类方法和url请求路径
//详细Debugger图在下面
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
//这里面的代码就不贴了 获取注解里的值组成请求路径
//封装成 RequestMappingInfo
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
//遍历map进行注册 注意此时的handler还是beanName
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//内部类 保存在Map<T, MappingRegistration<T>> registry 里面
this.mappingRegistry.register(mapping, handler, method);
}
-
MethodIntrospector.selectMethods()
断点调试图
-
接下来来到内部类
MappingRegistry.register()
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
//加写锁
this.readWriteLock.writeLock().lock();
try {
//进行封装,跟到底就是new HandlerMethod对象,里面保存类beanName和方法相关
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
//保存到map
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//保存到list 其实是一个map List
//Map<K, List<V>> targetMap key是请求的url 值是bean 方法 容器的封装
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
//加到 name -> list
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
//存储注册信息
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
- 内部成员变量
mappingRegistry
,里面有ReentrantReadWriteLock
读写锁,是保证线程安全的
private final MappingRegistry mappingRegistry = new MappingRegistry();
class MappingRegistry {
//保存注册信息的MAP
//this.registry.put(mapping,
//new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//this.pathLookup.add(path, mapping); 路径对应mapping
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
//this.nameLookup.put(name, newList); List<HandlerMethod> newList
//名称 #(方法名中的大写字符) list
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
//this.corsLookup.put(handlerMethod, corsConfig);
//跨域配置Map
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
//读写锁
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}
至此,MVC的AbstractHandlerMethodMapping
类已经通过Bean的初始化回调接口InitializingBean#afterPropertiesSet()
将所有符合条件的类解析,并将方法和请求路径的对应关系存储到成员变量MappingRegistry
中,接下来就开始MVC的初始化了
但是注意此时的handler还是beanName
2.MVC初始化阶段
- 看过前面手写的MVC版本,入口应不用多说了,这里还是贴下
<servlet>
<servlet-name>mvc</servlet-name>
//调用哪个类
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
//init方法 传参
<init-param>
//参数名称
<param-name>contextConfigLocation</param-name>
//参数值
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
- 接下来就直接找到
DispatcherServlet
类里找init方法,结果没找到,那就只能去父类了。最终在父类找到GenericServlet#init()
public void init(ServletConfig config) throws ServletException {
//保存了配置文件
this.config = config;
//当前类的init 子类实现
this.init();
}
- 接下来到
HttpServletBean.init()
public final void init() throws ServletException {
// Set bean properties from init parameters.
//把init参数封装一下
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
//接下来走这个 又是子类实现的方法
initServletBean();
}
- 接下来到
FrameworkServlet.initServletBean()
//找到这行
this.webApplicationContext = initWebApplicationContext();
//创建web容器 实际上也是继承了Application 这个容器用来存储一些web的东西
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
//配置 & 刷新容器 接下来先看这个,创建容器
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
//容器初始完成走这里 分析完 配置 & 刷新容器 等下分析这里
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
- 配置 & 刷新容器
configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
//web容器的一些参数
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
//在任何情况下,当上下文要先刷新;
//在这里这样做的原因,以确保servlet属性源已准备就绪
//在以下刷新之前发生的任何后处理或初始化中使用
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//回调后置处理
postProcessWebApplicationContext(wac);
//保存web容器
applyInitializers(wac);
//重点 点进去到了 AbstractApplicationContext.refresh
wac.refresh();
}
wac.refresh()
接下来到了AbstractApplicationContext.refresh()
,是不是很熟悉了,这不是SpringIOC容器的入口吗
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
......
}
- 无缝整合!创建web容器的同时,也创建好了IOC容器。容器创建完成,接下来我们再回到前面继续看
onRefresh
方法,到DispatcherServlet.onRefresh()
方法
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
//初始化九大组件
protected void initStrategies(ApplicationContext context) {
//图片上传组件
initMultipartResolver(context);
//初始化本地语言环境
initLocaleResolver(context);
//初始化模板处理器
initThemeResolver(context);
//handlerMapping
initHandlerMappings(context);
//初始化参数适配器
initHandlerAdapters(context);
//初始化异常拦截器
initHandlerExceptionResolvers(context);
//初始化视图预处理器
initRequestToViewNameTranslator(context);
//初始化视图转换器
initViewResolvers(context);
//初始化参数缓存管理
initFlashMapManager(context);
}
- 9大组件只挑几个我们关注的分析,先分析
initHandlerMappings()
private void initHandlerMappings(ApplicationContext context) {
//保存HandlerMapping的list private List<HandlerMapping> handlerMappings;
this.handlerMappings = null;
//找到所有的映射器 默认为true
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//翻译过来就是找到所有的HandleMapping 映射器
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
//然后保存到本地的handlerMapping List
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
.....
}
- 经过断点调试,springboot中映射器截图就这么多。然后保存到本地变量
List<HandlerMapping> handlerMappings
其他组件都是去容器中取出对应类型的bean,代码就不贴了。
初始化阶段至此结束,接下来开始调用阶段!
3.MVC调用阶段
用过servlet都知道,需要自己实现doGet/doPost方法,那么直接从DispatcherServlet
类开始找,最终在父类FrameworkServlet
找到
//还有其他的例如 doPut、doDelete这里就不贴了
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
FrameworkServlet
的doGet/doPost
方法下processRequest() -> DispatcherServlet#doService() -> DispatcherServlet#doDispatch()
一直到这里,中间初始化一些配置和本地线程变量。
DispatcherServlet#doDispatch这个方法有四个关注点,一个一个来
3.1 getHandler()根据当前请求找到对应的请求Bean和管道
DispatcherServlet#doDispatch
找到这段代码
//根据当前请求找到对应的请求地址管道
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
//没找到路径 返回404
noHandlerFound(processedRequest, response);
return;
}
- getHandler一直往下,到AbstractHandlerMapping
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//拿到HandlerMethod对象 我们接下来看一下这个方法
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//可能会存在多个请求地址 组成封装好的管道链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
......
//返回管道链
return executionChain;
}
接下来到AbstractHandlerMethodMapping#getHandlerInternal
,这里分为两步,第一步是从注册的信息中获取对应的HandlerMethod bean,先看方法initLookupPath()
3.1.1 initLookupPath()从注册的信息中获取对应的HandlerMethod
//getHandlerInternal到抽象类 AbstractHandlerMethodMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//获取请求的url路径
String lookupPath = initLookupPath(request);
//加读锁
this.mappingRegistry.acquireReadLock();
try {
//根据请求路径取出对应的handlerMethod beanName和Method
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//如果不等于null 就去容器中根据beanName获取bean
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
- 获取请求的url路径
AbstractHandlerMapping#initLookupPath()->UrlPathHelper#resolveAndCacheLookupPath()->getLookupPathForRequest()->getPathWithinApplication()
,这个代码应该很熟悉,返回请求路径
public String getPathWithinApplication(HttpServletRequest request) {
//获取项目路径
String contextPath = getContextPath(request);
//获取请求路径
String requestUri = getRequestUri(request);
//合并 下面有注释 正常情况下 URI会包含项目路径
String path = getRemainingPath(requestUri, contextPath, true);
if (path != null) {
// Normal case: URI contains context path.
return (StringUtils.hasText(path) ? path : "/");
}
else {
return requestUri;
}
}
上面已经拿到请求路径了,再回到AbstractHandlerMapping#getHandlerInternal
根据请求路径取出对应的handlerMethod,在lookupHandlerMethod()
方法里
3.1.2 lookupHandlerMethod()
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
//去本地变量中拿到对应的mapping
//this.pathLookup.get(urlPath); 上面有提过的一个封装的Map
//key是路径,value是List RequestMappingInfo
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
//用上面的mapping 校验request,最终封装成RequestMappingInfo添加到matches list
//继续往下看
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
//不关注的去掉了
。。。
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
//返回bean的封装信息
return bestMatch.getHandlerMethod();
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
AbstractHandlerMethodMapping#addMatchingMappings()
添加有效的mapping
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
//通过mapping中的一下本地变量校验request
T match = getMatchingMapping(mapping, request);
if (match != null) {
//添加到list
matches.add(new Match(match,
this.mappingRegistry.getRegistrations().get(mapping)));
}
}
}
- 获取mapping的注册信息,
this.mappingRegistry.getRegistrations().get(mapping);
public Map<T, MappingRegistration<T>> getRegistrations() {
return this.registry;
}
//前面添加 this.mappingRegistry.register(mapping, handler, method);
//上面有提过此时的handler还是beanName
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
- 再次回到
AbstractHandlerMethodMapping#getHandlerInternal()
此时已经拿到对应的handlerMethod了。但此时的handler还是beanName
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
//根据请求路径取出对应的handlerMethod beanName和Method
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//如果不等于null 就去容器中根据beanName获取bean
//先看createWithResolvedBean方法
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
handlerMethod.createWithResolvedBean()
到HandlerMethod#createWithResolvedBean()
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
String beanName = (String) this.bean;
//还是熟悉的味道 getBean就不用多说了吧
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
至此,handler才从真正的实例化,得到的就是我们要调用的bean了
3.2 getHandlerAdapter()获取处理器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//返回此处理程序对象的HandlerAdapter。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
//类型匹配 满足就返回
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
//随便看了一个 AbstractHandlerMethodAdapter 其实就是类型匹配
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
3.3 handle()进行调用
- 上面通过校验已经拿到请求的bean了,回到
DispatcherServlet#doDispatch
//通过封装的bean 拿到执行器 Adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Actually invoke the handler.
//开始执行 往这里走
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- 接下来最终会到
RequestMappingHandlerAdapter#handle()
//父类抽象类 AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//注意这里 返回的是一个ModelAndView
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
//服务器session存在需要加锁 现在能分清session和cookie的区别了吧
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
//直接看调用吧 走这里
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
- invokeHandlerMethod()方法关注两行
//调用 先看调用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//封装成ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
3.4 invokeAndHandle()调用
ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//调用以及获取返回值 先看这个
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
//无返回值直接return
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
//设置还没有处理返回结果
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//调用返回值的实现 例如返回页面回到ModelAndViewMethodReturnValueHandler
//返回字符串数据到RequestResponseBodyMethodProcessor
//最后一点返回值时分析
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
InvocableHandlerMethod#invokeForRequest()
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取参数 重点看这个
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//这里就不多说了 后面代码return method.invoke(getBean(), args);
return doInvoke(args);
}
getMethodArgumentValues()
看过手写的MVC,这个代码应该不陌生,这里简单过一下
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);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//通过转换器赋值
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
- 再回到
ServletInvocableHandlerMethod#invokeAndHandle
分析下返回参数的封装
//挑了页面的返回 ModelAndViewMethodReturnValueHandler
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
//空的设置返回已处理
mavContainer.setRequestHandled(true);
return;
}
//返回值是modelAndView的 里面是html路径和参数
ModelAndView mav = (ModelAndView) returnValue;
if (mav.isReference()) {
String viewName = mav.getViewName();
//保存到mavContainer容器里面来
mavContainer.setViewName(viewName);
if (viewName != null && isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
View view = mav.getView();
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
mavContainer.setStatus(mav.getStatus());
mavContainer.addAllAttributes(mav.getModel());
}
至此调用业务类已经完成,并且拿到了返回值。接下来分析返回
3.5 返回值处理
3.5.1 非页面返回值分析
- 找到这里
ServletInvocableHandlerMethod#invokeAndHandle
,返回值这里也分情况,加了@ResponseBody的才能找到
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
//handleReturnValue一直往下到RequestResponseBodyMethodProcessor
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//设置返回值已处理
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
//即使返回值为空,也要尝试。ResponseBodyAdvice可能会参与进来
//到最后通过 response.write写出去
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
3.5.2 如果是页面返回的
-
非页面的在这里其实已经写出去了,这里看到是返回页面的,我们以127.0.0.1/error页面为例
-
再次回到
DispatcherServlet#doDispatch
,找到这段代码
//处理返回结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
...
// Did the handler return a view to render?
//返回去的页面是否要显示视图
if (mv != null && !mv.wasCleared()) {
//渲染 多余的去掉 这里重点关注
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
....
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
......
//不关注的判断先去掉
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//视图的渲染 请求/error 断点看到接下来走ErrorMvcAutoConfiguration.render
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
- 接下来到
ErrorMvcAutoConfiguration.render
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (response.isCommitted()) {
String message = getMessage(model);
logger.error(message);
return;
}
response.setContentType(TEXT_HTML_UTF8.toString());
StringBuilder builder = new StringBuilder();
Date timestamp = (Date) model.get("timestamp");
Object message = model.get("message");
Object trace = model.get("trace");
if (response.getContentType() == null) {
response.setContentType(getContentType());
}
builder.append("<html><body><h1>Whitelabel Error Page</h1>").append(
"<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>")
.append("<div id='created'>").append(timestamp).append("</div>")
.append("<div>There was an unexpected error (type=").append(htmlEscape(model.get("error")))
.append(", status=").append(htmlEscape(model.get("status"))).append(").</div>");
if (message != null) {
builder.append("<div>").append(htmlEscape(message)).append("</div>");
}
if (trace != null) {
builder.append("<div style='white-space:pre-wrap;'>").append(htmlEscape(trace)).append("</div>");
}
builder.append("</body></html>");
//通过write写到页面上
response.getWriter().append(builder.toString());
}
- 在页面上看到就是这样的
至此,整个MVC的源码分析结束了。多看多断点就会发生一切很简单,只是想的太复杂
以上就是本章的全部内容了。
上一篇:Spring源码分析第三弹 - AOP切面编程分析
下一篇:Spring源码分析第五弹 - 神级的spring还有其他什么功效?
问渠那得清如许,为有源头活水来