首先看下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. 应对不同格式 轻松转换PDF、WORD、PPT、TXT常用文件

    PDF.WORD.PPT.TXT,不同格式的文件是不是弄得你眼花缭乱?如何巧妙地将它们相互转换?你不会还在键盘上傻傻地一个字一个字敲吧?教你不同文件格式间的转换方式,轻松几键便能大功告成.职场之上,你 ...

  2. 异常-----freemarker.core.ParseException: Encountered

    1.错误描述 freemarker.core.ParseException: Encountered " " at line 14, column 12 in myself.ftl ...

  3. 从零一起学Spring Boot之LayIM项目长成记(三) 数据库的简单设计和JPA的简单使用。

    前言 今天是第三篇了,上一篇简单模拟了数据,实现了LayIM页面的数据加载.那么今天呢就要用数据库的数据了.闲言少叙,书归正传,让我们开始吧. 数据库 之前有好多小伙伴问我数据库是怎么设计的.我个人用 ...

  4. jquery的各种插件调用(有些已经过时,以备注,其他的一些可以闲的时候用作拆解)

    jquery的valiteDate验证插件应用 <!DOCTYPE html> <html>        < head>            < meta ...

  5. luogu【P2753】[USACO4.3]字母游戏Letter Game

    这个题...一开始看了很久题目(并且问了机房几个大佬)才明白题意.. (原题入口) 题意 大概是一开始给你一些字母出现的次数 你之后组成的单词(最多两个单词)每个字母出现次数 必须小于或等于标准(st ...

  6. 【Luogu1879】玉米田(状态压缩,动态规划)

    懒得搞题目了 哦对了,这题双倍经验 题解 装压DP 利用位运算很容易解决相邻位的问题 其实我的还是太复杂了 具体的,更加好的位运算的写法可以参考YL大佬,但是我也搞不到他代码,因为他太强了. 然而他博 ...

  7. LuoguP3701 「伪模板」主席树

    题面 这个题很有意思啊... 其实是道最大流板子题,只连byx会赢的边,S向byx连,另一个连T... 注意有长者时连的边加上同方mogician的个数... 还要注意mogician可以无限续命,也 ...

  8. 检测flash是否安装及版本号

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. 如何实现类似Oracle中的家族树功能

    先来设计一下表结构: create table city(id varchar(3) , pid varchar(3) , name varchar(10)); 下面我们造几条测试数据: INSERT ...

  10. AOP面向切面编程在Android中的使用

    GitHub地址(欢迎下载完整Demo) https://github.com/ganchuanpu/AOPDemo 项目需求描述 我想类似于这样的个人中心的界面,大家都不会陌生吧.那几个有箭头的地方 ...