首先看下doDispatch()方法如何找到适合的适配器来执行方法的:

     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator i$ = this.handlerAdapters.iterator(); HandlerAdapter ha;
do {
if (!i$.hasNext()) {
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
} ha = (HandlerAdapter)i$.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler adapter [" + ha + "]");
}
} while(!ha.supports(handler)); //遍历初始化时候保存好的适配器,通过执行每个适配器的supports方法,如果支持就是他了。 return ha;
}

注: 这块也有点责任链模式的意思...

下面看下RequestMappingHandlerAdapter是怎么实现support方法的,看之前先上类图。

实际上support方法是在AbstractHandlerMethodAdapter这个父类实现的,然后给自己留个钩子方法,让子类实现

     public final boolean supports(Object handler) {
return handler instanceof HandlerMethod && this.supportsInternal((HandlerMethod)handler);
} protected abstract boolean supportsInternal(HandlerMethod var1); //钩子方法

这里RequestMappingHandlerAdapter的supportInternal直接是返回的true,估计后续扩展其他子类可能会复杂些,这就是设计模式的好处。

这样就找到了合适的适配器,下面来看执行:

     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
try {
ModelAndView mv = null;
Exception dispatchException = null; try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest, false); //获取处理器
if (mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
} HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); //获取适配器
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
this.logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
} if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
} //这一块是处理重复请求??? 有大神知道请留言.... if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} //执行拦截器的preHandle方法 try {
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //执行真正的Controller方法,就是我们的方法
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
} } this.applyDefaultViewName(request, mv); //设置视图名称
mappedHandler.applyPostHandle(processedRequest, response, mv); //执行拦截器的postHandle方法
} catch (Exception var28) {
dispatchException = var28;
} this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);//渲染视图
} catch (Exception var29) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var29);
} catch (Error var30) {
this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var30);
} } finally {
if (asyncManager.isConcurrentHandlingStarted()) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); //执行拦截器的afterConcurrentHandlingStarted
return;
} else {
if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
} }
}
}

拦截器这里就不在多说,这块就是返回false就不在往下执行。下面我们重点满ha.handle()方法,是如果映射参数,找到我们的方法,封装结果的。

类图前面已经展示了,实际上handle是在父类AbstractHandlerMethodAdapter实现的

     public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return this.handleInternal(request, response, (HandlerMethod)handler); //子类实现这个方法
} protected abstract ModelAndView handleInternal(HttpServletRequest var1, HttpServletResponse var2, HandlerMethod var3) throws Exception;
     protected final ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
this.checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
} else {
this.checkAndPrepare(request, response, true);
} //看代码应该是从session中获取一些信息,然后初始化header等信息,不知道准确不?请大家指正!
//这块就是根据需要是否进行同步操作
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized(mutex) {
return this.invokeHandleMethod(request, response, handlerMethod);
}
}
}
//正式进入执行环节
return this.invokeHandleMethod(request, response, handlerMethod);
}

下面这个方法非常重要,将重点讲解:

     private ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod); //创建@InitBinder注解的方法的工厂类,进行缓存
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);//创建@ModelAttribute@ControllerAdvice注解方法工厂并缓存
ServletInvocableHandlerMethod requestMappingMethod = this.createRequestMappingMethod(handlerMethod, binderFactory);
ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //创建结果容器并初始化一些参数,
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);//执行@ModelAttribute注解的方法,将结果放到结果容器中
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); //下面异步这一块不太明白,等后续在慢慢分析
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[];
asyncManager.clearConcurrentResult();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Found concurrent result value [" + result + "]");
} requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
} requestMappingMethod.invokeAndHandle(webRequest, mavContainer, new Object[]); //继续执行方法
return asyncManager.isConcurrentHandlingStarted() ? null : this.getModelAndView(mavContainer, modelFactory, webRequest); //返回值了,两种情况
}
     public final void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs); //执行方法,获取返回值
this.setResponseStatus(webRequest);
if (returnValue == null) {
if (this.isRequestNotModified(webRequest) || this.hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
} mavContainer.setRequestHandled(false); try { //处理返回值 ,封装结果集
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.getReturnValueHandlingErrorMessage("Error handling return value", returnValue), var6);
} throw var6;
}
}
     public final Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs); //处理 参数
if (this.logger.isTraceEnabled()) {
StringBuilder builder = new StringBuilder("Invoking [");
builder.append(this.getMethod().getName()).append("] method with arguments ");
builder.append(Arrays.asList(args));
this.logger.trace(builder.toString());
} Object returnValue = this.invoke(args); //反射执行方法
if (this.logger.isTraceEnabled()) {
this.logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
} return returnValue;
}
     private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
MethodParameter[] parameters = this.getMethodParameters();
Object[] args = new Object[parameters.length]; for(int i = ; i < parameters.length; ++i) { //遍历方法的所有参数
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(parameter, this.getBean().getClass()); //获取设置参数类型
args[i] = this.resolveProvidedArgument(parameter, providedArgs);
if (args[i] == null) {
if (this.argumentResolvers.supportsParameter(parameter)) { //这块是遍历预置的参数解析器,就是前面说的责任链模式,**composite负责查找和执行
try { //由找到的参数解析器,来解析参数
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception var9) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.getArgumentResolutionErrorMessage("Error resolving argument", i), var9);
} throw var9;
}
} else if (args[i] == null) {
String msg = this.getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
throw new IllegalStateException(msg);
}
}
} return args;
}

这块以,没有任何注解,参数为javaBean的解析器为例:ModelAttributeMethodProcessor

     public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
return true;
} else if (this.annotationNotRequired) {
return !BeanUtils.isSimpleProperty(parameter.getParameterType());
} else {
return false;
}
} public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter); //如果当前参数用@ModelAttribute修饰了,返回value值或者参数类型第一个字母小写
// 获取需要绑定的表单对象,看参数容器包含name为key的对象不,有的话就用它,没有创建个新的。
      Object attribute = mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : this.createAttribute(name, parameter, binderFactory, request);
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
this.bindRequestParameters(binder, request);
this.validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && this.isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
     //以上就是参数绑定, 这块领开一篇文章详细说
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return binder.getTarget();
}

参数就这样遍历处理,然后就开始通过反射 invoke执行了。接下来我们看是怎么封装换回结果的

         try {
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.getReturnValueHandlingErrorMessage("Error handling return value", returnValue), var6);
} throw var6;
}

this.returnValuehandlers. 就是那个返回结果的包装类,初始化的结果解析器就保存这里,处理思路和参数解析器一样的,

     public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = this.getReturnValueHandler(returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
} private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
Iterator i$ = this.returnValueHandlers.iterator(); HandlerMethodReturnValueHandler returnValueHandler;
do {
if (!i$.hasNext()) {
return null;
} returnValueHandler = (HandlerMethodReturnValueHandler)i$.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" + returnType.getGenericParameterType() + "]");
}
} while(!returnValueHandler.supportsReturnType(returnType)); return returnValueHandler;
}

遍历预置的所有结果解析器,结果解析器统一实现HandlerMethodReturnValueHandler 接口,实现supportReturnType方法:

这里我们距离用@ResponseBody注解的结果解析器RequestResponseBodyMethodProcessor 前面说了,参数和结果集他都实现了

     public boolean supportsReturnType(MethodParameter returnType) {
return returnType.getMethodAnnotation(ResponseBody.class) != null; //判断是否有@ResponseBody注解
}
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
mavContainer.setRequestHandled(true);
if (returnValue != null) {
this.writeWithMessageConverters(returnValue, returnType, webRequest); //用内置的消息转换器来转换结果集
} }

这里可能有人会问,消息转换器什么时候加载的?是在RequestMappingHandlerAdapter这个bean实例化的时候加载的,同时加载参数和结果解析器时候注入到解析器当中的

     public RequestMappingHandlerAdapter() { //无参构造函数中初始化
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false);
this.messageConverters = new ArrayList();
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { //构造参数解析器时候,注入进去
List<HandlerMethodReturnValueHandler> handlers = new ArrayList();
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(this.getMessageConverters(), this.contentNegotiationManager));
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.contentNegotiationManager));
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
if (this.getCustomReturnValueHandlers() != null) {
handlers.addAll(this.getCustomReturnValueHandlers());
} if (!CollectionUtils.isEmpty(this.getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(this.getModelAndViewResolvers()));
} else {
handlers.add(new ModelAttributeMethodProcessor(true));
} return handlers;
}

下面来看是怎么寻找可以合适的消息转换器的

     protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException {
ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
} protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException {
Class<?> returnValueClass = returnValue.getClass();
HttpServletRequest servletRequest = inputMessage.getServletRequest();
List<MediaType> requestedMediaTypes = this.getAcceptableMediaTypes(servletRequest); //获取请求的MediaType,eg:"application/json"
List<MediaType> producibleMediaTypes = this.getProducibleMediaTypes(servletRequest, returnValueClass); //寻找支持这个返回类型的转换器支持的MediaTyoe
Set<MediaType> compatibleMediaTypes = new LinkedHashSet();
Iterator i$ = requestedMediaTypes.iterator();
     //双循环两个list,进行匹配,把复核条件的MediaType放到compatibleMediaTypes中 //TODO有些不懂得是为啥这块要过滤一遍, 后面实现了 父类也做了判断每个字类是否支持MediaType了??
MediaType selectedMediaType;
Iterator i$;
MediaType mediaType;
while(i$.hasNext()) {
selectedMediaType = (MediaType)i$.next();
i$ = producibleMediaTypes.iterator(); while(i$.hasNext()) {
mediaType = (MediaType)i$.next();
if (selectedMediaType.isCompatibleWith(mediaType)) {
compatibleMediaTypes.add(this.getMostSpecificMediaType(selectedMediaType, mediaType));
}
}
} if (compatibleMediaTypes.isEmpty()) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
} else {
List<MediaType> mediaTypes = new ArrayList(compatibleMediaTypes);
MediaType.sortBySpecificityAndQuality(mediaTypes);
selectedMediaType = null;
i$ = mediaTypes.iterator();
//排序之后,选择适合的MediaType
while(i$.hasNext()) {
mediaType = (MediaType)i$.next();
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
} if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
} if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
i$ = this.messageConverters.iterator();
//遍历所有消息转换器,canWrite是接口方法,相当于前面的support等,模式都是一个。然后满足的进行write。输出结果。
while(i$.hasNext()) {
HttpMessageConverter<?> messageConverter = (HttpMessageConverter)i$.next();
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
messageConverter.write(returnValue, selectedMediaType, outputMessage);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]");
} return;
}
}
} throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}

下面介绍下,@ResponseBode标签用的消息转换器是MappingJacksonHttpMessageConverter;先看下类图吧

MappingJacksonHttpMessageConverter重写了父类的write方法:

     public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return this.objectMapper.canSerialize(clazz) && this.canWrite(mediaType); //canWrite(mediaType)是父类实现的
}
     protected boolean canWrite(MediaType mediaType) {
if (mediaType != null && !MediaType.ALL.equals(mediaType)) {
Iterator i$ = this.getSupportedMediaTypes().iterator(); //获取子类解析器支持的MediaType,看下是否支持 MediaType supportedMediaType;
do {
if (!i$.hasNext()) {
return false;
} supportedMediaType = (MediaType)i$.next();
} while(!supportedMediaType.isCompatibleWith(mediaType)); return true;
} else {
return true;
}
}

write方法 父类也帮着实现了,父类具体做了如输出,拼凑输出流头等信息

     public final void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
HttpHeaders headers = outputMessage.getHeaders();
if (headers.getContentType() == null) {
if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) {
contentType = this.getDefaultContentType(t);
} if (contentType != null) {
headers.setContentType(contentType);
}
} if (headers.getContentLength() == -1L) {
Long contentLength = this.getContentLength(t, headers.getContentType());
if (contentLength != null) {
headers.setContentLength(contentLength);
}
} this.writeInternal(t, outputMessage); //钩子方法,让子类去实现
outputMessage.getBody().flush();
}
     protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
if (this.objectMapper.getSerializationConfig().isEnabled(Feature.INDENT_OUTPUT)) {
jsonGenerator.useDefaultPrettyPrinter();
}
//这块就是默认用Jackson进行翻译结果集了
try {
if (this.prefixJson) {
jsonGenerator.writeRaw("{} && ");
} this.objectMapper.writeValue(jsonGenerator, object);
} catch (JsonProcessingException var6) {
throw new HttpMessageNotWritableException("Could not write JSON: " + var6.getMessage(), var6);
}
}

因为用@ResponseBody不需要返回视图,所以视图那块就返回Null,不需要渲染视图了

Spring MVC之适配器的获取及执行(RequestMappingHandlerAdapter)的更多相关文章

  1. 转载:如何让spring mvc web应用启动时就执行

    转载:如何让spring mvc web应用启动时就执行特定处理 http://www.cnblogs.com/yjmyzz/p/4747251.html# Spring-MVC的应用中 一.Appl ...

  2. spring mvc在Controller中获取ApplicationContext

    spring mvc在Controller中获取ApplicationContext web.xml中进行正常的beans.xml和spring-mvc.xml的配置: 需要在beans.xml中进行 ...

  3. spring mvc web应用启动时就执行特定处理(线程启动)

    package com.sdt.platform.index.controller; import java.net.URL; import java.util.List; import java.u ...

  4. Spring MVC(十三)--保存并获取属性参数

    这里的属性参数主要是指通过request.session.cookie等设置的属性,有时候我们需要将一些请求的参数保存到HTTP的request或者session对象中去,在控制器中也会进行设置和获取 ...

  5. Spring Mvc 前台数据的获取、SpringMvc 表单数据的获取

    首先在web.xml 里面配置一个编码过滤器 <!-- springmvc框架本身没有处理请求编码,我们自己配置一个请求编码过滤器 --> <filter> <filte ...

  6. @Spring MVC 中几种获取request和response的方式

    1.最简单方式:处理方法入参 例如: @RequestMapping("/test") @ResponseBody public void saveTest(HttpServlet ...

  7. spring mvc中几种获取request对象的方式

    在使用spring进行web开发的时候,优势会用到request对象,用来获取访问ip.请求头信息等 这里收集几种获取request对象的方式 方法一:在controller里面的加参数 public ...

  8. Spring MVC模式下,获取WebApplicationContext的工具类 方法

    在已有的注解类型下,获取WebApplicationContext的工具类 通过  WebApplicationContextUtils.getRequiredWebApplicationContex ...

  9. 如何让spring mvc web应用启动时就执行特定处理

    Asp.Net的应用中通过根目录下的Global.asax,在Application_Start方法中做一些初始化操作,比如:预先加载缓存项对网站热点数据进行预热,获取一些远程的配置信息等等. Spr ...

随机推荐

  1. ASP.NET 页面双向静态化

    而我们预期的结果应该如下图,实际只请求两次. 用301重定向可以解决该循环请求产生的问题. OK, let's begin. 本文的Demo和Source是基于上一篇的,如果下面的一些文件或文件夹没有 ...

  2. Dijkstra算法与Bellman - Ford算法示例(源自网上大牛的博客)【图论】

    题意:题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 poj2387 Description Bessie is out in the field and ...

  3. Web原理

    在日常学代码的过程中,总会多多少少遇到点瓶颈.但是别人又听得懂,这是为什么? 我觉得,就是在原理上都不去思考,为什么这样写?为什么一定要这样写?那到底该怎么写?写完了是怎么运行的? 就以上几个问题,只 ...

  4. mysql下如何删除本节点下的所有子节点小记

    在开发过程中,经常会遇到树形结构的数据,在删除某个节点时候其所有的子节点都要被删除,可以使用如下方法: 1.添加记录该节点所有父节点的ID的字段(parent_ids),并用逗号隔开(一定是逗号),如 ...

  5. mybatis快速入门(四)

    mybatis动态标签<where><if><foreach>以及sql片段 1.创建一个包装类UserQueryVo.java package cn.my.myb ...

  6. 历史记录 history

    设置显示行数:HISTSISE=5 或 export HISTSIZE=5 永久生效,生效,检查,同118. 储存历史记录文件:cat ~/.bash_history 控制文件:HISTFILESIZ ...

  7. Python 2.7版本与3.6的不同

    初学python,暂时就记一点. 1.print 2.7:print "123" #正常 3.6:print "123" #报错,正常应该是print在输出任何 ...

  8. web前端UI框架

    分类:WEB前端 时间:2016年1月13日 目前,众多互联网公司APP都嵌入了大量的HTML5,移动端的开发越来越重视,HTML5的运用场景也越来越多了.在移动WEB开发的过程中,使用合适的移动WE ...

  9. IIS前端页面不显示详细错误解决方法

    要想解决这个问题,有三种方法可以考虑: 1.Internet信息服务(IIS)管理器 2.Web.config文件 3. 命令行 在IIS的"错误页"右边的"编辑功能设置 ...

  10. ubuntu字符界面怎么设置中文显示和中文输入

    在ubuntu的字符登陆界面,中文显示默认是乱码的,而且也不支持中文输入,解决方法有很多, 1)安装zhcon--解决中文显示乱码的问题. sudo apt-get install zhcon 然后c ...