Spring MVC架构

  Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。传统的模型层被拆分为了业务层(Service)和数据访问层(DAO,Data Access Object)。 在 Service 下可以通过 Spring 的声明式事务操作数据访问层,而在业务层上还允许我们访问 NoSQL ,这样就能够满足异军突起的 NoSQL 的使用了,它可以大大提高互联网系统的性能。

  M 代表 模型(Model),V 代表 视图(View),C 代表 控制器(controller):

  • 模型封装了应用程序数据,并且通常它们由 POJO 组成。

  • 视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。

  • 控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。

一些优点:

1.在Spring MVC中,控制器可以处理所有HTTP方法的请求,这是RESTful Web服务的基础。例如,可以处理GET方法来执行读取操作,POST方法来创建资源,PUT方法来更新资源以及DELETE方法来从服务器中删除资源。从Spring 3.2开始,还可以处理PATCH请求。

2.对于REST,数据的表示非常重要,这就是为什么Spring MVC允许您通过使用 @ResponseBody 批注和各种 HttpMessgeConverter 实现方式完全绕过基于View的呈现  。通过使用此功能,您可以直接向客户端发送响应,例如,客户端所需的资源以及客户端所需的格式。

3. Spring 4.0版本添加了专用注释,@RestController使RESTful Web服务的开发更加容易。

如果使用@RestController 而不是 @Controller注释控制器类  ,则 Spring 会将消息对话应用于控制器中的所有处理程序方法。

这意味着您不需要为每个方法添加 @ResponseBody 注释。这也使您的代码更加简洁。

4. REST Web服务和普通Web应用程序之间的主要区别之一是,REST传递资源标识URI本身中的数据,例如  /messages/101,  而Web应用程序通常使用查询参数,例如 /messages?Id=101。

如果您还记得的话,我们使用 @RequestParam 获取这些查询参数的值,但是不用担心,Spring MVC还提供了一个@PathVariable批注,可以从URL提取数据。它允许控制器处理对参数化URL的请求。

5. RESTful Web服务的另一个关键方面是表示,表示同一资源可以以不同的格式表示,例如JSON,XML,HTML等。值得庆幸的是,Spring提供了几种视图实现和视图解析器,以将数据呈现为JSON,XML,和HTML。

例如,ContentNegotiatingViewResolver 可以查看请求的文件扩展名或“接受”标头,以找出客户端资源的正确表示形式。

6.与 @ResponseBody 用于将响应转换为客户端想要的格式(通过使用 HttpMessageConverts)的注释  类似  ,Spring MVC还提供  @RequestBody 注释,该注释使用  HttpMethodConverter 实现将入站HTTP数据转换为传递到控制器的处理程序方法中的Java对象。

7.在 Spring框架还提供了一个模板类,RestTemplate,这是类似的 JdbcTemplate,并且 JmsTemplate,它可以消耗REST资源。您可以使用此类测试RESTful Web服务或开发REST客户端。

DispatchServlet

DispatchServlet 是 HTTP 请求处理程序/控制器的中央调度程序。Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的。

下面是对应于 DispatcherServlet 传入 HTTP 请求的事件序列:

  • 收到一个 HTTP 请求后,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器

  • 控制器接受请求(取决于使用@Controller和@RequestMapping注释请求的URL),并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到 DispatcherServlet 中。

  • DispatcherServlet 会从视图解析器 ViewResolver 获取帮助,为请求检取定义视图。

  • 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器中。

  上面所提到的所有组件,即 HandlerMappingController 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是带有一些对 web 应用程序必要的额外特性的 ApplicationContext 的扩展。

DispatcherServlet 初始化流程

DispatcherServlet在MVC子容器初始化时会初始化这9大组件:

  1. MultipartResolver:用来处理上传请求的。其处理方式就是将 request 包装成 MultipartHttpServletRequest。然后就可以用 MultipartHttpServletRequest 这个直接调用 getFile 获取的文件了。

  2. LocalResolver:作用是从 request 中解析出 local,关于local大多数情况下都是用来做国际化处理的。

  3. ThemeResolver:解析主题的,例如配一套properties文件供系统在不同的时候读取切换。

  4. HandlerMapping:其作用就是根据 request 找到相应的处理器 Handler 和 Intecepter 拦截器。

  5. HandlerAdapter:其接口的 handle 方法就是使用 handler 来处理逻辑的。

  6. HandlerExceptionResolver:Spring MVC 中的异常处理组件,根据异常设置 ModelAndView,然后再将处理结果交给 render方法进行渲染。在渲染之前工作的,因此渲染过程中发生异常,HandlerExceptionResolver 是不会处理的。

  7. RequestToViewNameTranslator:将request请求转换为视图名称。

  8. ViewResolver:作用是将 String 类型的逻辑视图根据 local 解析为 View 视图的。

  9. FlashMapManager:在redirect是进行参数传递需要用到。

1     /**
2 * This implementation calls {@link #initStrategies}.
3 */
4 @Override
5 protected void onRefresh(ApplicationContext context) {
6 initStrategies(context);
7 }
 1     /**
2 * Initialize the strategy objects that this servlet uses.
3 * <p>May be overridden in subclasses in order to initialize further strategy objects.
4 */
5 protected void initStrategies(ApplicationContext context) {
6 initMultipartResolver(context);
7 initLocaleResolver(context);
8 initThemeResolver(context);
9 initHandlerMappings(context);
10 initHandlerAdapters(context);
11 initHandlerExceptionResolvers(context);
12 initRequestToViewNameTranslator(context);
13 initViewResolvers(context);
14 initFlashMapManager(context);
15 }

DispatcherServlet 执行流程

  Http请求通过 HttpServlet 读取请求的内容,生成一个 HttpServletRequest 和一个 HttpServletResponse。下面这段方法将客户端请求分派到受保护的 service 方法。

 1     @Override
2 public void service(ServletRequest req, ServletResponse res)
3 throws ServletException, IOException {
4
5 HttpServletRequest request;
6 HttpServletResponse response;
7
8 try {
9 request = (HttpServletRequest) req;
10 response = (HttpServletResponse) res;
11 } catch (ClassCastException e) {
12 throw new ServletException(lStrings.getString("http.non_http"));
13 }
14 service(request, response);
15 }

  接着来到 FrameworkServlet 的 service 方法,若是 patch 类型的方法,则单独拿出来进行处理。

 1     /**
2 * Override the parent class implementation in order to intercept PATCH requests.
3 */
4 @Override
5 protected void service(HttpServletRequest request, HttpServletResponse response)
6 throws ServletException, IOException {
7
8 HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
9 if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
10 processRequest(request, response);
11 }
12 else {
13 super.service(request, response);
14 }
15 }

  最后回到 HttpServlet 的 service 方法中,这里使用到的设计模式是模板方法。

 1     protected void service(HttpServletRequest req, HttpServletResponse resp)
2 throws ServletException, IOException {
3
4 String method = req.getMethod();
5
6 if (method.equals(METHOD_GET)) {
7 long lastModified = getLastModified(req);
8 if (lastModified == -1) {
9 // servlet doesn't support if-modified-since, no reason
10 // to go through further expensive logic
11 doGet(req, resp);
12 } else {
13 long ifModifiedSince;
14 try {
15 ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
16 } catch (IllegalArgumentException iae) {
17 // Invalid date header - proceed as if none was set
18 ifModifiedSince = -1;
19 }
20 if (ifModifiedSince < (lastModified / 1000 * 1000)) {
21 // If the servlet mod time is later, call doGet()
22 // Round down to the nearest second for a proper compare
23 // A ifModifiedSince of -1 will always be less
24 maybeSetLastModified(resp, lastModified);
25 doGet(req, resp);
26 } else {
27 resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
28 }
29 }
30
31 } else if (method.equals(METHOD_HEAD)) {
32 long lastModified = getLastModified(req);
33 maybeSetLastModified(resp, lastModified);
34 doHead(req, resp);
35
36 } else if (method.equals(METHOD_POST)) {
37 doPost(req, resp);
38
39 } else if (method.equals(METHOD_PUT)) {
40 doPut(req, resp);
41
42 } else if (method.equals(METHOD_DELETE)) {
43 doDelete(req, resp);
44
45 } else if (method.equals(METHOD_OPTIONS)) {
46 doOptions(req,resp);
47
48 } else if (method.equals(METHOD_TRACE)) {
49 doTrace(req,resp);
50
51 } else {
52 //
53 // Note that this means NO servlet supports whatever
54 // method was requested, anywhere on this server.
55 //
56
57 String errMsg = lStrings.getString("http.method_not_implemented");
58 Object[] errArgs = new Object[1];
59 errArgs[0] = method;
60 errMsg = MessageFormat.format(errMsg, errArgs);
61
62 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
63 }
64 }

  然后在跳到 FrameworkServlet 的 doGet 方法中,doGet方法中用到 processRequest 方法,再看看上面对 patch 进行单独处理,可见是把将不同类型的请求合并到了processRequest 这个方法中来处理。

1     @Override
2 protected final void doGet(HttpServletRequest request, HttpServletResponse response)
3 throws ServletException, IOException {
4
5 processRequest(request, response);
6 }

  processRequest 方法中做了三件事:

  • 调用了 doService 模板方法具体处理请求。

  • 将当前请求的 LocaleContext 和 ServletRequestAttributes 在处理请求前设置到了 LocaleContextHolder 和 RequestContextHolder,并在请求处理完成后恢复

  • 请求处理完后发布了 ServletRequestHandledEvent 消息。

 1     protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
2 throws ServletException, IOException {
3
4 long startTime = System.currentTimeMillis();
5 Throwable failureCause = null;
6
7 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
8 LocaleContext localeContext = buildLocaleContext(request);
9
10 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
11 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
12
13 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
14 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
15
16 initContextHolders(request, localeContext, requestAttributes);
17
18 try {
19 doService(request, response);
20 }
21 catch (ServletException | IOException ex) {
22 failureCause = ex;
23 throw ex;
24 }
25 catch (Throwable ex) {
26 failureCause = ex;
27 throw new NestedServletException("Request processing failed", ex);
28 }
29
30 finally {
31 resetContextHolders(request, previousLocaleContext, previousAttributes);
32 if (requestAttributes != null) {
33 requestAttributes.requestCompleted();
34 }
35 logResult(request, response, failureCause, asyncManager);
36 publishRequestHandledEvent(request, response, startTime, failureCause);
37 }
38 }

  上面这段代码调用了 doService 的模板方法,接着走到了 DispatcherServlet 类中的 doService方法。

 1     @Override
2 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
3 logRequest(request);
4
5 // Keep a snapshot of the request attributes in case of an include,
6 // to be able to restore the original attributes after the include.
7 Map<String, Object> attributesSnapshot = null;
8 if (WebUtils.isIncludeRequest(request)) {
9 attributesSnapshot = new HashMap<>();
10 Enumeration<?> attrNames = request.getAttributeNames();
11 while (attrNames.hasMoreElements()) {
12 String attrName = (String) attrNames.nextElement();
13 if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
14 attributesSnapshot.put(attrName, request.getAttribute(attrName));
15 }
16 }
17 }
18
19 // Make framework objects available to handlers and view objects.
20 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
21 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
22 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
23 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
24
25 if (this.flashMapManager != null) {
26 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
27 if (inputFlashMap != null) {
28 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
29 }
30 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
31 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
32 }
33
34 try {
35 doDispatch(request, response);
36 }
37 finally {
38 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
39 // Restore the original attribute snapshot, in case of an include.
40 if (attributesSnapshot != null) {
41 restoreAttributesAfterInclude(request, attributesSnapshot);
42 }
43 }
44 }
45 }

  然后进行请求的分发 DispatcherServlet#doDispatch

 1     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 HttpServletRequest processedRequest = request;
3 HandlerExecutionChain mappedHandler = null;
4 boolean multipartRequestParsed = false;
5
6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
7
8 try {
9 ModelAndView mv = null;
10 Exception dispatchException = null;
11
12 try {
13 processedRequest = checkMultipart(request);
14 multipartRequestParsed = (processedRequest != request);
15
16 // 确定当前请求的handler
17 mappedHandler = getHandler(processedRequest);
18 if (mappedHandler == null) {
19 noHandlerFound(processedRequest, response);  // 没有找到则执行此方法
20 return;
21 }
22
23 // 确定当前请求的处理程序适配器
24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
25
26 // Process last-modified header, if supported by the handler.
27 String method = request.getMethod();
28 boolean isGet = "GET".equals(method);
29 if (isGet || "HEAD".equals(method)) {
30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
31 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
32 return;
33 }
34 }
35
36 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
37 return;
38 }
39
40 // 调用对应handler方法
41 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
42
43 if (asyncManager.isConcurrentHandlingStarted()) {
44 return;
45 }
46
47 applyDefaultViewName(processedRequest, mv);
48 mappedHandler.applyPostHandle(processedRequest, response, mv);
49 }
50 catch (Exception ex) {
51 dispatchException = ex;
52 }
53 catch (Throwable err) {
54 // As of 4.3, we're processing Errors thrown from handler methods as well,
55 // making them available for @ExceptionHandler methods and other scenarios.
56 dispatchException = new NestedServletException("Handler dispatch failed", err);
57 }
58 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
59 }
60 catch (Exception ex) {
61 triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
62 }
63 catch (Throwable err) {
64 triggerAfterCompletion(processedRequest, response, mappedHandler,
65 new NestedServletException("Handler processing failed", err));
66 }
67 finally {
68 if (asyncManager.isConcurrentHandlingStarted()) {
69 // Instead of postHandle and afterCompletion
70 if (mappedHandler != null) {
71 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
72 }
73 }
74 else {
75 // Clean up any resources used by a multipart request.
76 if (multipartRequestParsed) {
77 cleanupMultipart(processedRequest);
78 }
79 }
80 }
81 }

  看看 getHandler 方法,遍历所有配置的 HandlerMapping 类,可以看出是支持用户配置多个 HandlerMapping 类的,

 1     @Nullable
2 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
3 if (this.handlerMappings != null) {
4 for (HandlerMapping mapping : this.handlerMappings) {
5 HandlerExecutionChain handler = mapping.getHandler(request);
6 if (handler != null) {
7 return handler;
8 }
9 }
10 }
11 return null;
12 }

  如果没有找到对应的 HandlerExecutionChain 对象,则会执行 noHandlerFound 方法返回异常。

 1     protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 if (pageNotFoundLogger.isWarnEnabled()) {
3 pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
4 }
5 if (this.throwExceptionIfNoHandlerFound) {
6 throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
7 new ServletServerHttpRequest(request).getHeaders());
8 }
9 else {
10 response.sendError(HttpServletResponse.SC_NOT_FOUND);  // 404错误
11 }
12 }

  返回到 DispatcherServlet#doDispatch,找到了对应的 handler 后,就去寻找对应的 HandlerAdapter。与 HandlerMapping 类似,查找能够处理具体 Handler 的 HandlerAdapter 时同样会遍历所有配置了的 HandlerAdapter,没有找到对应的 adapter 则会抛出异常。

 1     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
2 if (this.handlerAdapters != null) {
3 for (HandlerAdapter adapter : this.handlerAdapters) {
4 if (adapter.supports(handler)) {
5 return adapter;
6 }
7 }
8 }
9 throw new ServletException("No adapter for handler [" + handler +
10 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
11 }

  前没有什么问题发生,则按照顺序依次调用 HandlerInterceptor的 preHandle 方法,但当任一 HandlerInterceptor 的 preHandle方法返回了 false 就不再继续执行其他HandlerInterceptor 的 preHandle方法,而是直接跳转执行 triggerAfterCompletion 方法。

 1     boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 HandlerInterceptor[] interceptors = getInterceptors();
3 if (!ObjectUtils.isEmpty(interceptors)) {
4 for (int i = 0; i < interceptors.length; i++) {
5 HandlerInterceptor interceptor = interceptors[i];
6 if (!interceptor.preHandle(request, response, this.handler)) {
7 triggerAfterCompletion(request, response, null);
8 return false;
9 }
10 this.interceptorIndex = i;
11 }
12 }
13 return true;
14 }

  然后执行 mapperHandler 的 applyPostHandle 方法,应用注册拦截器的 postHandle 方法。

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception { HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

  最后执行 processDispatchResult 方法

 1     /**
2 * Handle the result of handler selection and handler invocation, which is
3 * either a ModelAndView or an Exception to be resolved to a ModelAndView.
4 */
5 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
6 HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
7
8 boolean errorView = false;
9 //判断 HandlerMapping、HandlerAdapter 处理时的异常是否有异常,有异常则进行异常处理
10 if (exception != null) {
11 //上述两个组件处理时的异常不为空
12 //如果为 ModelAndViewDefiningException 异常,则获取一个异常视图
13 if (exception instanceof ModelAndViewDefiningException) {
14 logger.debug("ModelAndViewDefiningException encountered", exception);
15 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
16 }
17 //如果不为 ModelAndViewDefiningException 异常,进行异常视图的获取
18 else {
19 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
20 mv = processHandlerException(request, response, handler, exception);
21 errorView = (mv != null);
22 }
23 }
24
25 // Did the handler return a view to render?
26 //判断 mv 是否为空,不管是正常的 ModelAndView 还是异常的 ModelAndView,只要存在 mv 就进行视图渲染
27 if (mv != null && !mv.wasCleared()) {
28 render(mv, request, response); // 交给rander渲染
29 if (errorView) {
30 WebUtils.clearErrorRequestAttributes(request);
31 }
32 }
33 //否则记录无视图
34 else {
35 if (logger.isDebugEnabled()) {
36 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
37 "': assuming HandlerAdapter completed request handling");
38 }
39 }
40
41 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
42 // Concurrent handling started during a forward
43 return;
44 }
45 //执行相关HandlerInterceptor的afterCompletion 方法
46 if (mappedHandler != null) {
47 mappedHandler.triggerAfterCompletion(request, response, null);
48 }
49 }

  到这里之后则按着栈帧一步步弹出,DispatcherServlet 处理完 Http 请求的流程。

参考:

Spring MVC源码初探(一) FrameworkServlet

SpringMVC源码系列:九大组件小记

servlet之模板方法和多线程

Spring MVC:DispatchServlet类的更多相关文章

  1. Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)

    当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...

  2. Spring MVC:控制器类名称处理映射

    控制器类名称处理映射的好好处是: 如果项目是hello,WelcomeController是控制器,那么访问地址是: http://localhost:8080/hello/welcome http: ...

  3. Spring MVC 配置类 WebMvcConfigurerAdapter

    WebMvcConfigurerAdapter配置类是spring提供的一种配置方式,采用JavaBean的方式替代传统的基于xml的配置来对spring框架进行自定义的配置.因此,在spring b ...

  4. Spring MVC控制器类名称处理映射

    以下示例显示如何使用Spring Web MVC框架使用控制器类名称处理程序映射. ControllerClassNameHandlerMapping类是基于约定的处理程序映射类,它将URL请求映射到 ...

  5. 1.2(Spring MVC学习笔记) Spring MVC核心类及注解

    一.DispatcherServlet DispatcherServlet在程序中充当着前端控制器的作用,使用时只需在web.xml下配置即可. 配置格式如下: <?xml version=&q ...

  6. spring MVC请求处理类注解属性详解

  7. Spring MVC小结

    Spring MVC项目搭建 添加依赖 (省略) Spring MVC配置类 @Configuration @EnableWebMvc @ComponentScan("com.sjx.spr ...

  8. 就是这么简单(续)!使用 RestAssuredMockMvc 测试 Spring MVC Controllers

    就是这么简单(续)!使用 RestAssuredMockMvc 测试 Spring MVC Controllers 转载注明出处:http://www.cnblogs.com/wade-xu/p/43 ...

  9. Spring MVC 教程,快速入门,深入分析

    http://elf8848.iteye.com/blog/875830/ Spring MVC 教程,快速入门,深入分析 博客分类: SPRING Spring MVC 教程快速入门  资源下载: ...

  10. 使用Maven创建一个Spring MVC Web 项目

    使用Maven创建java web 项目(Spring MVC)用到如下工具: 1.Maven 3.2 2.IntelliJ IDEA 13 3.JDK 1.7 4.Spring 4.1.1 rele ...

随机推荐

  1. Typeora 图床设置

    Typeora 文章中的图片 使用 Github 作为图床. 使用 PicGo 上传图片到 Github 并获取图片链接. 设置 Typeora 的上传服务. 一.Github 作为图床 创建 Rep ...

  2. Java中HashCode()和equals()的作用

    引言 我们知道Java中的集合(Collection)大致可以分为两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重复:后者元素无序,但元素不可重复. 这里就引出一个问题: ...

  3. Maven专题4——Maven测试

    Java世界的主流测试框架是JUnit和TestNG,Maven在构建执行到特定生命周期阶段的时候,通过插件执行JUnit和TestNG的测试用例. Maven执行测试的插件是maven-surefi ...

  4. 关于vue-cli的安装

    (一):*安装 vue-cli 参考: https://cn.vuejs.org/v2/guide/installation.html https://github.com/vuejs/vue-cli ...

  5. 多线程run()方法是不能够被直接调用的

    操作系统线程的五种状态: 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于"可运行 ...

  6. 【C++周报】第一期2021-8-1

    [C++周报]第一期 2021-8-1 这一期我们来看这道题目:https://vijos.org/p/1058 这道题是一道非常好的模拟题.题目如下: 描述 我们用文本处理器来处理一个特殊的文本文件 ...

  7. Faster RCNN 改进论文及资料

    1,面向小目标的多尺度Faster RCNN检测算法 黄继鹏等 对高分辨率图像进行下采样和上采样,使得网上获取的数据与实际测试数据分布接近. 下采样:最大池化和平均池化 上采样:线性插值,区域插值,最 ...

  8. P3643-[APIO2016]划艇【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P3643 题目大意 求有多少个\(n\)个数的序列\(x\)满足,\(x_i\in \{0\}\cup[a_i,b_ ...

  9. 51nod1355-斐波那契的最小公倍数【min-max容斥】

    正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1355 题目大意 定义\(f_i\)表示斐波那契的第\(i\)项,给出一个 ...

  10. AT4996-[AGC034F]RNG and XOR【FWT,生成函数】

    正题 题目链接:https://www.luogu.com.cn/problem/AT4996 题目大意 给出一个\(0\sim 2^n-1\)下标的数组\(p\),\(p_i\)表示有\(p_i\) ...