SpringMVC请求处理流程源码

SpringMVC的请求处理流程图

springmvc请求处理流程.png

总结

1、请求进入DispatcherServlet,由DispatcherServlet 从HandlerMappings中提取对应的Handler

2、根据Handle,然后去寻找对应的处理器适配器(HandlerAdapter),拿到对应HandlerAdapter后,这时候开始调用对应的Handler处理业务逻辑

3、执行完成之后返回一个ModeAndView,将结果交给我们的ViewResolver通过视图名称查找出对应的视图然后返回

4、渲染视图:返回渲染后的视图并响应请求

请求处理器的初始化

1、DispacterServlet中的静态块

static {
    /**
     * static静态块,在类加载的时候执行
     * 加载springWvc的默认系统配置文件 DispatcherServlet.properties
     * 配置了 HandlerAdapter HandlerMapping ThemeResolver LocaleResolver ....
     * 放入到 defaultStrategies 对象中
     */
    ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
    defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}

DispatcherServlet.properties文件

# 仅列出部分
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

2、当Servlet容器启动的时候,tomcat会调用这个DispatcherServlet的init方法

// HttpServletBean#init
public final void init() throws ServletException {
    // 执行子类 FrameServlet的initServletBean方法
    initServletBean();
}
// FrameworkServlet#initServletBean
protected final void initServletBean() throws ServletException {
    // 初始化web环境 WebApplicationContext  ==child==> ConfigurableWebApplicationContext
    this.webApplicationContext = initWebApplicationContext();
    // 调用了initFrameworkServlet方法,这是一个空方法
    initFrameworkServlet();
}
// FrameworkServlet#initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
    WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.webApplicationContext;
    // 配置并且刷新容器
    configureAndRefreshWebApplicationContext(cwac);
}
// FrameworkServlet#configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    // 添加了一个容器刷新的监听器  =>  ContextRefreshListener
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
    // 空方法
    postProcessWebApplicationContext(wac);
    // 容器的初始化共组
    applyInitializers(wac);
    // 刷新容器,走到AbstractApplicationContext中的refresh方法
    wac.refresh();
}

3、容器刷新时触发的监听器ContextRefreshListener

// FrameworkServlet.ContextRefreshListener
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        FrameworkServlet.this.onApplicationEvent(event);
    }
}
public void onApplicationEvent(ContextRefreshedEvent event) {
    this.refreshEventReceived = true;
    synchronized (this.onRefreshMonitor) {
        onRefresh(event.getApplicationContext());
    }
}
// 调用子类的onRefresh方法
// DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
    // 初始化策略组件
    initStrategies(context);
}

4、初始化组件

// DispatcherServlet#initStrategies
protected void initStrategies(ApplicationContext context) {
    // 初始化文件上传组件 beanName="multipartResolver"
    initMultipartResolver(context);
    // 初始化国际化组件 beanName="localeResolver"
    initLocaleResolver(context);
    // 初始化主题解析器,利用spring来做 beanName="themeResolver"
    initThemeResolver(context);
    // 初始化处理映射器 beanName="handlerMapping"
    initHandlerMappings(context);
    // 初始化处理器适配器 beanName="handlerAdapter"
    initHandlerAdapters(context);
    // 初始化异常处理器 beanName="handlerExceptionResolver"
    initHandlerExceptionResolvers(context);
    // 视图解析器,在view为空时,根据请求来获取视图名称 beanName="viewNameTranslator"
    initRequestToViewNameTranslator(context);
    // 初始化视图解析器 beanName="viewResolver"
    initViewResolvers(context);
    // 重定向数据管理器 beanName="flashMapManager"
    initFlashMapManager(context);
}

初始化的组件中MultipartResolver、LocaleResolver、ThemeResolver、RequestToViewNameTranslator和FlashMapManager的初始化是直接去容器中获取对应的bean对象。

其他的组件默认会去容器中寻找所有的 对应类型 的对象。如果在容器中没有找到对应的对象,那么就会向容器中注册 DispatcherServlet.properties 文件中的对应的类。

// 去DispatcherServlet.properties文件中获取 strategyInterface 对应的组件
// 找到之后会将组件注册到DispatcherServlet的对应属性上
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
    // 传入的class全名,就对应了DispatcherServlet.properties中的key
    String key = strategyInterface.getName();
    // 从配置文件中拿出配置
    String value = defaultStrategies.getProperty(key);
    if (value != null) {
        // 截取value中的 , 返回一个className 的数组
        String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
        List<T> strategies = new ArrayList<>(classNames.length);
        for (String className : classNames) {
            // 根据反射创建出这个class对象
            Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
            // 根据这个class 去createBean,这里就可以将 HandlerMapping 的组件放入容器中
            Object strategy = createDefaultStrategy(context, clazz);
            // 最后把策略对象返回
            strategies.add((T) strategy);
        }
        return strategies;
    } else {
        return new LinkedList<>();
    }
}

请求处理器处理请求

SpringMVC的核心处理器:DispatcherServlet,它本质上也是一个HttpServlet,在配置SpringMVC的时候,我们会让这个servlet随着servlet容器的启动而启动(load-on-startup),并且拦截所有对当前应用程序的请求。

所以当请求来了之后,首先会调用HttpServlet#service方法。具体会调用父类FrameworkServlet#service

// FrameworkServlet#service
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    // // 增加对HttpMethod.PATCH的支持
    if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
        processRequest(request, response);
    }  else {
        // HttpServlet#service 进行请求类型的分发 -> 走到doGet/doPost/doPut
        // 最终都会和上面的一致调用 processRequest(request, response);
        super.service(request, response);
    }
}

处理请求之前的一些准备

org.springframework.web.servlet.FrameworkServlet#processRequest
 -> org.springframework.web.servlet.DispatcherServlet#doService
    -> org.springframework.web.servlet.DispatcherServlet#doDispatch

处理请求的核心方法:DispatcherServlet#doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 检查是否存在文件上传
    HttpServletRequest processedRequest = checkMultipart(request);
    boolean multipartRequestParsed = (processedRequest != request);
    // 确定当前请求是由哪一种handlerMapping来处理
    // 一种是选择一个bean来处理,另一种是映射到一个方法上
    HandlerExecutionChain mappedHandler = getHandler(processedRequest);
    // 根据当前的请求的 handlerMapping 来决定使用哪一种处理器适配器
    // HttpRequestHandlerAdapter
    // SimpleControllerHandlerAdapter
    // RequestMappingHandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    // 执行所有配置这个uri的拦截器的 preHandle 方法
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        // 只要存在一个拦截器的 preHandle 方法返回false,这里就会直接return
        return;
    }
   /** 调用实际的处理器来处理请求
     如果是匹配一个类的话,这里会直接把这个映射对象传入 => mappedHandler.getHandler()
    HttpRequestHandlerAdapter => handle() => handler.handleRequest(request, response)
    SimpleControllerHandlerAdapter => handle() => handler.handleRequest(request, response);
    RequestMappingHandlerAdapter
        => AbstractHandlerMethodAdapter#handle
        => RequestMappingHandlerAdapter#handleInternal
          mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    */
    // 是否是异步请求
    if (asyncManager.isConcurrentHandlingStarted()) {
        return;
    }
    // 如果存在mv对象,但是在mv中不存在view,这里就会应用一个默认的视图对象
    applyDefaultViewName(processedRequest, mv);
    // 执行所有配置这个uri的拦截器的 postHandle 方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    // 处理返回值,渲染页面,返回响应...
    // 在这里面的最后一个执行所有配置这个uri的拦截器的 afterCompletion 方法
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

doDispatch方法就是SpringMVC中请求处理流程的具体代码实现。

核心组件 - HandlerMapping

在DispatcherServlet.properties中HandlerMapping组件存在两种实现BeanNameUrlHandlerMapping和RequestMappingHandlerMapping

根据HandlerMapping中的getHandler方法来确定使用哪一种HandlerMapping。

// DispatcherServlet#getHandler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings == null)  return null;
    // 遍历所有的handlerMappings
    for (HandlerMapping mapping : this.handlerMappings) {
        // 这里返回这个请求对应的拦截器链
        HandlerExecutionChain handler = mapping.getHandler(request);
        if (handler != null) return handler;
    }
}

这里的getHandler方法非常关键,它不仅找出了需要使用的HandlerMapping还会去构建好拦截器链。

public final HandlerExecutionChain getHandler(HttpServletRequest request) {
    // 确定使用哪一类的请求处理器
    // AbstractHandlerMethodMapping.getHandlerInternal  -> RequestMappingHandlerMapping
    // AbstractUrlHandlerMapping.getHandlerInternal     -> BeanNameUrlHandlerMapping
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // 构建拦截器链
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
    return executionChain;
}

HandlerMapping

1、AbstractHandlerMethodMapping.getHandlerInternal

// @RequestMapping这种servlet
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // uri localhost:9090/index => /index
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // 读锁
    this.mappingRegistry.acquireReadLock();
    try {
        // 根据uri获取对应的处理方法
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    } finally {
        this.mappingRegistry.releaseReadLock();
    }
}

2、AbstractUrlHandlerMapping.getHandlerInternal

// @Component("/index/2") 这种controller
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // handlerMap<String, Object>中找可以找到
    Object handler = lookupHandler(lookupPath, request);
    if (handler != null) {
        return handler;
    }
    // 省略部分代码
}

构建拦截器链

只有在找出对应的HandlerMapping后才会构建拦截器链

// AbstractHandlerMapping#getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                                    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    // uri
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    // 遍历所有的拦截器 我们自己实现了HandlerInterceptor会被springMVC包装为MappedInterceptor
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        } else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

调用MappedInterceptor#matches进行拦截器匹配

// addPathPatterns -> includePatterns
// excludePathPatterns -> excludePatterns
public boolean matches(String lookupPath, PathMatcher pathMatcher) {
    PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
    if (!ObjectUtils.isEmpty(this.excludePatterns)) {
        for (String pattern : this.excludePatterns) {
            if (pathMatcherToUse.match(pattern, lookupPath)) {
                return false;
            }
        }
    }
    if (ObjectUtils.isEmpty(this.includePatterns)) {
        return true;
    }
    for (String pattern : this.includePatterns) {
        if (pathMatcherToUse.match(pattern, lookupPath)) {
            return true;
        }
    }
    return false;
}

获取处理器适配器

这里的handlerAdapters默认就是DispatcherServlet.properties中配置的三种拦截器适配器

  • HttpRequestHandlerAdapter 匹配HttpRequestHandler处理器
  • SimpleControllerHandlerAdapter 匹配实现了Controller接口或继承了AbstractController的类
  • RequestMappingHandlerAdapter 匹配HandlerMethod,@Controller注解
// HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            // 核心的supports方法
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
}
// HttpRequestHandlerAdapter#supports
public boolean supports(Object handler) {
    return (handler instanceof HttpRequestHandler);
}
// SimpleControllerHandlerAdapter#supports
public boolean supports(Object handler) {
    return (handler instanceof Controller);
}
// AbstractHandlerMethodAdapter#supports
public final boolean supports(Object handler) {
    return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
// RequestMappingHandlerAdapter#supportsInternal
protected boolean supportsInternal(HandlerMethod handlerMethod) {
    return true;
}

拦截器回调 - preHandle

// if (!mappedHandler.applyPreHandle(processedRequest, response)) {}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            // 每一个拦截器的 preHandle 需要返回true,整个请求处理才能继续向下执行
            if (!interceptor.preHandle(request, response, this.handler)) {
                // 如果有一个拦截器的 preHandle 方法返回了false,就会直接执行直接拦截器 afterCompletion 方法,并返回false
                triggerAfterCompletion(request, response, null);
                // 返回false意味着这个请求的处理就到此结束了
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}

处理器处理请求

以处理@Controller注解为例:AbstractHandlerMethodAdapter#handle

// mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) {
   /** 调用子类的 handleInternal 方法
     * 不同的处理器适配器实现了不同功能的 handleInternal 方法
     * RequestMappingHandlerAdapter#handleInternal  处理@RequestMapping方法映射
     */
    return handleInternal(request, response, (HandlerMethod) handler);
}
// RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
                                      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ModelAndView mav;
    checkRequest(request);
    // 在这个同步块中执行controller回调方法
    // Execute invokeHandlerMethod in synchronized block if required.
    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);
    }
    // json 数据格式的响应在上面就完成了 invokeHandlerMethod 方法中
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        }
        else {
            prepareResponse(response);
        }
    }
    return mav;
}

回调拦截器 - postHandle

检查返回的ModelAndView和回调拦截器 - postHandle

// 如果存在mv对象,但是在mv中不存在view,这里就会应用一个默认的视图对象
applyDefaultViewName(processedRequest, mv);
// 执行所有配置这个uri的拦截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 是否需要创建默认的视图对象
private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) {
    if (mv != null && !mv.hasView()) {
        // 存在mv对象,但是不存在view对象才会执行到这里:DefaultRequestToViewNameTranslator
        // DefaultRequestToViewNameTranslator#getViewName 方法
        // 会根据请求的URI返回视图名称,去除后缀,前后/,如果完全匹配,就返回请求的URI
        String defaultViewName = getDefaultViewName(request);
        if (defaultViewName != null) {
            mv.setViewName(defaultViewName);
        }
    }
}

调用拦截器 - afterCompletion

// processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler,
                                   ModelAndView mv,Exception exception) {
    boolean errorView = false;
    if (exception != null) {
        // 如果存在异常,就把异常交给HandlerExceptionResolver来处理
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        } else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            // 调用 HandlerExceptionResolver#resolveException
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }
    // 是否返回了一个需要渲染的视图
    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        // 渲染视图
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }
    // 在视图渲染等一切工作完成之后执行拦截器的 afterCompletion 方法
    // interceptor.afterCompletion(request, response, this.handler, ex);
    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

SpringMVC对于beanName请求的处理

如果一个请求可以匹配到一个实现了Controller接口或者继承了AbstractController的bean对象。那么可以直接调用接口或者抽象方法去处理请求。

// SimpleControllerHandlerAdapter  ->  直接调用Controller接口的方法 
// AbstractController会在handleRequest中调用子类的handleRequestInternal方法
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return ((Controller) handler).handleRequest(request, response);
}
// HttpRequestHandlerAdapter
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ((HttpRequestHandler) handler).handleRequest(request, response);
    return null;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 0 系列目录# WEB请求处理 WEB请求处理一:浏览器请求发起处理 WEB请求处理二:Nginx请求反向代理 W...
    七寸知架构阅读 4,261评论 3 55
  • 对于java中的思考的方向,1必须要看前端的页面,对于前端的页面基本的逻辑,如果能理解最好,不理解也要知道几点。 ...
    神尤鲁道夫阅读 802评论 0 0
  • 1.Spring整体架构 1)核心容器(Core Container) Core模块,主要包含了Spring框架基...
    Sponge1128阅读 1,048评论 0 1
  • SpringMVC的笔记 MVC M 代表 模型(Model)模型就是数据,如:dao,bean V 代表 视图(...
    JasonChen8888阅读 407评论 0 1
  • SpringMVC的工作原理图: SpringMVC的工作原理图: SpringMVC流程 1、 用户发送请求至前...
    我不饿我不想吃东西阅读 840评论 0 1