之前分析过SpringMVC中的DispatcherServlet,分析了SpringMVC处理请求的过程。但忽略了一些DispatcherServlet协助请求处理的组件,例如SpringMVC中的HandlerMappingHandlerAdapterViewResolvers等等。

HandlerMappings

HandlerMappingsDispathServlet中主要作用是为请求的urlpath匹配对应的Controller,建立一个映射关系,根据请求查找HandlerInterceptorHandlerMappings将请求传递到HandlerExecutionChain上,HandlerExecutionChain包含了一个能够处理该请求的处理器,还可以包含拦截改请求的拦截器。

在没有处理器映射相关配置情况下,DispatcherServlet会为你创建一个BeanNameUrlHandlerMapping作为默认映射的配置。在DispatchServlet.properties文件中对于HandlerMapping的默认配置是:

  1. org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
  2. org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

HandlerMapping的配置策略一般分为配置式BeanNameUrlHandlerMapping和注解式DefaultAnnotationHandlerMapping。不过DefaultAnnotationHandlerMapping已经被放弃了,取代它的是RequestMappingHandlerMapping,不知道为啥SpringMVC这个默认配置尚未做修改。

AbstractHandlerMapping

AbstractHandlerMappingHandlerMapping的抽象实现,是所有HandlerMapping实现类的父类。

AbstractHandlerMapping的作用是是为了初始化InterceptorsAbstractHandlerMapping重写了WebApplicationObjectSupportinitApplicationContext方法。

  1. protected void initApplicationContext() throws BeansException {
  2. extendInterceptors(this.interceptors);
  3. detectMappedInterceptors(this.adaptedInterceptors);
  4. initInterceptors();
  5. }
  • extendInterceptors方法,Springmvc并没有做出具体实现,这里留下一个拓展,子类可以重写这个模板方法,为子类添加或者修改Interceptors

  • detectMappedInterceptors方法将SpringMVC容器中所有MappedInterceptor类的bean添加到adaptedInterceptors中。

  • 最后调用initInterceptors初始化拦截器。遍历interceptorsWebRequestInterceptorHandlerInterceptor类型的拦截器添加到adaptedInterceptors中。

HandlerMapping通过getHandler方法来获取请求的处理器Handler和拦截器Interceptor。在getHandlerExecutionChain方法中将遍历之前初始化的adaptedInterceptors,为当前的请求选择对应的MappedInterceptorsadaptedInterceptors

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping

AbstractUrlHandlerMapping继承于AbstractHandlerMapping,它是通过URL来匹配具体的HandlerAbstractUrlHandlerMapping维护一个handlerMap来存储UrlHandler的映射关系。

AbstractUrlHandlerMapping重写了AbstractHandlerMapping类中的getHandlerInternal方法。HandlerMapping通过getHandler方法,就会调用这里的getHandlerInternal方法来获取HandlergetHandlerInternal方法中关键调用lookupHandler方法去获取handler

  1. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
  2. // Direct match?
  3. Object handler = this.handlerMap.get(urlPath);
  4. if (handler != null) {
  5. // Bean name or resolved handler?
  6. if (handler instanceof String) {
  7. String handlerName = (String) handler;
  8. handler = getApplicationContext().getBean(handlerName);
  9. }
  10. validateHandler(handler, request);
  11. return buildPathExposingHandler(handler, urlPath, urlPath, null);
  12. }
  13. // Pattern match?
  14. List<String> matchingPatterns = new ArrayList<String>();
  15. for (String registeredPattern : this.handlerMap.keySet()) {
  16. if (getPathMatcher().match(registeredPattern, urlPath)) {
  17. matchingPatterns.add(registeredPattern);
  18. }
  19. else if (useTrailingSlashMatch()) {
  20. if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
  21. matchingPatterns.add(registeredPattern +"/");
  22. }
  23. }
  24. }
  25. String bestMatch = null;
  26. Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
  27. if (!matchingPatterns.isEmpty()) {
  28. Collections.sort(matchingPatterns, patternComparator);
  29. if (logger.isDebugEnabled()) {
  30. logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
  31. }
  32. bestMatch = matchingPatterns.get(0);
  33. }
  34. if (bestMatch != null) {
  35. handler = this.handlerMap.get(bestMatch);
  36. if (handler == null) {
  37. if (bestMatch.endsWith("/")) {
  38. handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
  39. }
  40. if (handler == null) {
  41. throw new IllegalStateException(
  42. "Could not find handler for best pattern match [" + bestMatch + "]");
  43. }
  44. }
  45. // Bean name or resolved handler?
  46. if (handler instanceof String) {
  47. String handlerName = (String) handler;
  48. handler = getApplicationContext().getBean(handlerName);
  49. }
  50. validateHandler(handler, request);
  51. String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
  52. // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
  53. // for all of them
  54. Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
  55. for (String matchingPattern : matchingPatterns) {
  56. if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
  57. Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
  58. Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
  59. uriTemplateVariables.putAll(decodedVars);
  60. }
  61. }
  62. if (logger.isDebugEnabled()) {
  63. logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
  64. }
  65. return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
  66. }
  67. // No handler found...
  68. return null;
  69. }
  • 首先调用lookupHandler方法来获取handler。在lookupHandler方法中,先通过URLhandlerMap查找是否有合适的handler
  • 如果没有获取到handler,遍历handlerMap利用正则匹配的方法,找到符合要求的handlers(有可能是多个)。
  • 正则匹配是采用Ant风格,将会通过排序筛选出一个匹配程度最高的Handler
  • 最后调用buildPathExposingHandler方法构建一个handler,添加PathExposingHandlerInterceptorUriTemplateVariablesHandlerInterceptor两个拦截器并返回。

上面介绍获取handler的过程中,会先从handlerMap查找。下面看一下handlerMap是如何初始化的。AbstractUrlHandlerMapping是通过registerHandler初始化handlerMap的。AbstractUrlHandlerMapping共有两个registerHandler方法。分别是注册多个url到一个handler和注册一个url到一个handler。首先判断handlerMap是否有此handler。如果存在的话,判断是否一致,不一致则抛出异常,如果不存在的话,如果url//*,则,返回root handlerdefault handler,如果不是将添加到handlerMap中。

SimpleUrlHandlerMapping

SimpleUrlHandlerMapping继承于AbstractUrlHandlerMappingSimpleUrlHandlerMapping重写了父类AbstractHandlerMapping中的初始化方法initApplicationContext。在initApplicationContext方法中调用registerHandlers方法。

  1. protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
  2. if (urlMap.isEmpty()) {
  3. logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
  4. }
  5. else {
  6. for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
  7. String url = entry.getKey();
  8. Object handler = entry.getValue();
  9. // Prepend with slash if not already present.
  10. if (!url.startsWith("/")) {
  11. url = "/" + url;
  12. }
  13. // Remove whitespace from handler bean name.
  14. if (handler instanceof String) {
  15. handler = ((String) handler).trim();
  16. }
  17. registerHandler(url, handler);
  18. }
  19. }
  20. }

判断是url是否以/开头,如果不是,默认补齐/,确保所有的url都是以/开头,然后依次调用父类的registerHandler方法注册到AbstractUrlHandlerMapping中的handlerMap

在使用SimpleUrlHandlerMapping时,需要在注册的时候配置其urlmap否则会抛异常。

AbstractDetectingUrlHandlerMapping

AbstractDetectingUrlHandlerMapping类继承于AbstractUrlHandlerMapping类,重写了initApplicationContext方法,在initApplicationContext方法中调用了detectHandlers方法。

  1. protected void detectHandlers() throws BeansException {
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
  4. }
  5. String[] beanNames = (this.detectHandlersInAncestorContexts ?
  6. BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
  7. getApplicationContext().getBeanNamesForType(Object.class));
  8. // Take any bean name that we can determine URLs for.
  9. for (String beanName : beanNames) {
  10. String[] urls = determineUrlsForHandler(beanName);
  11. if (!ObjectUtils.isEmpty(urls)) {
  12. // URL paths found: Let's consider it a handler.
  13. registerHandler(urls, beanName);
  14. }
  15. else {
  16. if (logger.isDebugEnabled()) {
  17. logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
  18. }
  19. }
  20. }
  21. }

获取所有容器的beanNames,遍历所有的beanName,调用determineUrlsForHandler方法解析url,这里的determineUrlsForHandler也是运用了模板方法设计模式,具体的实现在其子类中,如果解析到子类,将注册到父类的handlerMap中。

BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping类的类图大致如下:

BeanNameUrlHandlerMapping类继承于AbstractDetectingUrlHandlerMapping类。重写了父类中的determineUrlsForHandler方法。

  1. protected String[] determineUrlsForHandler(String beanName) {
  2. List<String> urls = new ArrayList<String>();
  3. if (beanName.startsWith("/")) {
  4. urls.add(beanName);
  5. }
  6. String[] aliases = getApplicationContext().getAliases(beanName);
  7. for (String alias : aliases) {
  8. if (alias.startsWith("/")) {
  9. urls.add(alias);
  10. }
  11. }
  12. return StringUtils.toStringArray(urls);
  13. }

其通过beanName解析Url规则也很简单,判断beanName是否以/开头。

BeanNameUrlHandlerMappingSpringMVC的默认映射配置。

AbstractHandlerMethodMapping

通常我们也习惯于用@Controller@Re questMapping来定义HandlerAbstractHandlerMethodMapping可以将method作为Handler来使用。

AbstractHandlerMethodMapping实现了InitializingBean接口,实现了afterPropertiesSet方法。当容器启动的时候会调用initHandlerMethods注册委托handler中的方法。


  1. public void afterPropertiesSet() {
  2. initHandlerMethods();
  3. }
  4. protected void initHandlerMethods() {
  5. if (logger.isDebugEnabled()) {
  6. logger.debug("Looking for request mappings in application context: " + getApplicationContext());
  7. }
  8. String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
  9. BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
  10. getApplicationContext().getBeanNamesForType(Object.class));
  11. for (String beanName : beanNames) {
  12. if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
  13. Class<?> beanType = null;
  14. try {
  15. beanType = getApplicationContext().getType(beanName);
  16. }
  17. catch (Throwable ex) {
  18. // An unresolvable bean type, probably from a lazy bean - let's ignore it.
  19. if (logger.isDebugEnabled()) {
  20. logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
  21. }
  22. }
  23. if (beanType != null && isHandler(beanType)) {
  24. detectHandlerMethods(beanName);
  25. }
  26. }
  27. }
  28. handlerMethodsInitialized(getHandlerMethods());
  29. }

initHandlerMethods方法中,做了以下工作:

  • 首先通过BeanFactoryUtils扫描应用上下文,获取所有的bean
  • 遍历所有的beanName,调用isHandler方法判断是目标bean是否包含@Controller@RequestMapping注解。
  • 对于带有@Controller@RequestMapping注解的类,调用detectHandlerMethods委托处理,获取所有的method,并调用registerHandlerMethod注册所有的方法。

detectHandlerMethods方法负责将Handler保存到Map中。

  1. protected void detectHandlerMethods(final Object handler) {
  2. // 获取handler的类型
  3. Class<?> handlerType = (handler instanceof String ?
  4. getApplicationContext().getType((String) handler) : handler.getClass());
  5. final Class<?> userType = ClassUtils.getUserClass(handlerType);
  6. Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
  7. new MethodIntrospector.MetadataLookup<T>() {
  8. @Override
  9. public T inspect(Method method) {
  10. try {
  11. return getMappingForMethod(method, userType);
  12. }
  13. catch (Throwable ex) {
  14. throw new IllegalStateException("Invalid mapping on handler class [" +
  15. userType.getName() + "]: " + method, ex);
  16. }
  17. }
  18. });
  19. if (logger.isDebugEnabled()) {
  20. logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
  21. }
  22. for (Map.Entry<Method, T> entry : methods.entrySet()) {
  23. Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
  24. T mapping = entry.getValue();
  25. registerHandlerMethod(handler, invocableMethod, mapping);
  26. }
  27. }

selectMethods方法中重写了MetadataLookup中的inspect方法,inspect方法中调用了子类RequestMappingHandlerMapping实现了getMappingForMethod模板方法,用于构建RequestMappingInfo

  1. public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
  2. final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();
  3. Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
  4. Class<?> specificHandlerType = null;
  5. if (!Proxy.isProxyClass(targetType)) {
  6. handlerTypes.add(targetType);
  7. specificHandlerType = targetType;
  8. }
  9. handlerTypes.addAll(Arrays.asList(targetType.getInterfaces()));
  10. for (Class<?> currentHandlerType : handlerTypes) {
  11. final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
  12. ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
  13. @Override
  14. public void doWith(Method method) {
  15. Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
  16. T result = metadataLookup.inspect(specificMethod);
  17. if (result != null) {
  18. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
  19. if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
  20. methodMap.put(specificMethod, result);
  21. }
  22. }
  23. }
  24. }, ReflectionUtils.USER_DECLARED_METHODS);
  25. }
  26. return methodMap;
  27. }

selectMethods通过反射获取所有的方法,重写了doWith方法,将handler中的method和请求对应的RequestMappingInfo保存到methodMap中。

最终detectHandlerMethods将遍历这个methodMap,调用registerHandlerMethod注册HandlerMethodMappingRegistry

AbstractHandlerMethodMapping类中,有个内部类MappingRegistry,用来存储mapping和 handler methods注册关系,并提供了并发访问方法。

AbstractHandlerMethodMapping通过getHandlerInternal来为一个请求选择对应的handler

  1. protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  2. // 根据request获取对应的urlpath
  3. String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Looking up handler method for path " + lookupPath);
  6. }
  7. // 获取读锁
  8. this.mappingRegistry.acquireReadLock();
  9. try {
  10. // 调用lookupHandlerMethod方法获取请求对应的HandlerMethod
  11. HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
  12. if (logger.isDebugEnabled()) {
  13. if (handlerMethod != null) {
  14. logger.debug("Returning handler method [" + handlerMethod + "]");
  15. }
  16. else {
  17. logger.debug("Did not find handler method for [" + lookupPath + "]");
  18. }
  19. }
  20. return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
  21. }
  22. finally {
  23. this.mappingRegistry.releaseReadLock();
  24. }
  25. }

lookupHandlerMethod的具体实现如下:

  1. protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  2. List<Match> matches = new ArrayList<Match>();
  3. // 通过lookupPath获取所有匹配到的path
  4. List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
  5. if (directPathMatches != null) {
  6. // 将匹配条件添加到matches
  7. addMatchingMappings(directPathMatches, matches, request);
  8. }
  9. if (matches.isEmpty()) {
  10. // 如果没有匹配条件,将所有的匹配条件都加入matches
  11. addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
  12. }
  13. if (!matches.isEmpty()) {
  14. Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
  15. Collections.sort(matches, comparator);
  16. if (logger.isTraceEnabled()) {
  17. logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
  18. lookupPath + "] : " + matches);
  19. }
  20. // 选取排序后的第一个作为最近排序条件
  21. Match bestMatch = matches.get(0);
  22. if (matches.size() > 1) {
  23. if (CorsUtils.isPreFlightRequest(request)) {
  24. return PREFLIGHT_AMBIGUOUS_MATCH;
  25. }
  26. Match secondBestMatch = matches.get(1);
  27. // 前两个匹配条件排序一样抛出异常
  28. if (comparator.compare(bestMatch, secondBestMatch) == 0) {
  29. Method m1 = bestMatch.handlerMethod.getMethod();
  30. Method m2 = secondBestMatch.handlerMethod.getMethod();
  31. throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
  32. request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
  33. }
  34. }
  35. // 将lookupPath设为请求request的PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE属性
  36. handleMatch(bestMatch.mapping, lookupPath, request);
  37. return bestMatch.handlerMethod;
  38. }
  39. else {
  40. return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
  41. }
  42. }

整个过程以Match作为载体,Match是个内部类,封装了匹配条件和handlerMethod两个属性,默认的实现是将lookupPath设置为请求的属性。

总结

本文从源码角度上分析了HandlerMapping的各种实现。主要功能是为请求找到合适的handlerinterceptors,并组合成HandlerExecutionChain。查找handler的过程通过getHandlerInternal方法实现,每个子类都其不同的实现。

所有的HandlerMapping的实现都继承于AbstarctHandlerMappingAbstarctHandlerMapping主要作用是完成拦截器的初始化工作。而通过AbstarctHandlerMapping又衍生出两个系列,AbstractUrlHandlerMappingAbstractHandlerMethodMapping

AbstractUrlHandlerMapping也有很多子类的实现,如SimpleUrlHandlerMappingAbstractDetectingUrlHandlerMapping。总体来说,AbstractUrlHandlerMapping需要用到一个保存urlhandler的对应关系的mapmap的初始化工作由子类实现。不同的子类会有自己的策略,可以在配置文件中注册,也可以在spring容器中找。

AbstractHandlerMethodMapping系列则通常用于注解的方法,解析包含@Controller或者@RequestMapping注解的类,建立urlmethod的直接对应关系,这也是目前使用最多的一种方式。

SpringMVC源码分析--HandlerMappings的更多相关文章

  1. springMVC源码分析--HandlerMapping(一)

    HandlerMapping的工作就是为每个请求找到合适的请求找到一个处理器handler,其实现机制简单来说就是维持了一个url到Controller关系的Map结构,其提供的实际功能也是根据req ...

  2. SpringMVC源码分析--容器初始化(五)DispatcherServlet

    上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...

  3. [心得体会]SpringMVC源码分析

    1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...

  4. 8、SpringMVC源码分析(3):分析ModelAndView的形成过程

    首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...

  5. 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解

    从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...

  6. springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)

    之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...

  7. springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)

    在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...

  8. springMVC源码分析--RequestParamMethodArgumentResolver参数解析器(三)

    之前两篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)和springMVC源码解析--HandlerMethodArgumentResol ...

  9. springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod

    在之前一篇博客中springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestMa ...

随机推荐

  1. redis3.2 aof重写

    redis关闭aof,缩容,redis实例一直在重写. 原因也是redis3.2的bug,aof重写是没有判断aof是否开启. redis缩容后改变的是redis重写的min_size,缩容之前,实例 ...

  2. kvm虚拟化环境的搭建

    首先搭建kvm的虚拟化环境,我选择的环境是在vmvare上的Centos 7的虚拟机,在该环境上搭建kvm的虚拟化环境 1:安装虚拟机(该过程自行安装) 2:操作系统环境的设置 (1)修改内核模式为兼 ...

  3. centos7 修改内核文件 网卡名称为标准名称eth0

    在开机安装系统之前按TAB键后输入标记信息后安装系统就可以变成标准网卡接口eth0 或eth1

  4. SegNet网络的Pytorch实现

    1.文章原文地址 SegNet: A Deep Convolutional Encoder-Decoder Architecture for Image Segmentation 2.文章摘要 语义分 ...

  5. CentOS环境部署(Nginx+Mariadb+Java+Tomcat)

    1.安装nginx 安装 yum install nginx 启动 yum install nginx 开机自启 sudo systemctl enable nginx 2.安装mariadb 安装 ...

  6. Selenium常用API的使用java语言之4-环境安装之Selenium

    1.通过jar包安装 点击Selenium下载 链接 你会看到Selenium Standalone Server的介绍: The Selenium Server is needed in order ...

  7. CSP模拟赛 Lost My Music(二分,可回退化栈)

    题面 题解 发现是斜率的形式,答案的相反数可以看做一条直线的斜率.那么我们要答案最小,斜率最大.维护下凸壳就行了. 考试时写了直接dfsdfsdfs+暴力弹栈拿了808080分(还以为自己是O(n)正 ...

  8. 使用AJAX传输不了值时

    当时候AJAX往后台传递值时  传不到后台  这时我们就要检查程序是否有问题了 一般AJAX程序 $.ajax( { type: "POST", url: "Login. ...

  9. Oracle 绑定变量窥视

    绑定变量窥视功能是数据库的一个特性,自ORACLE9i版本开始引入,默认是开启的. “绑定变量窥视”表示,查询优化器在第一次调用游标时,会观察用户定义的绑定变量的值,允许优化器来确认过滤条件的选择性, ...

  10. 第12章、乐活人生的ABCDE

    目录 第12章.乐活人生的ABCDE 什么时候该乐观 让自己乐观的ABC 确认ABC 你的ABC记录 反驳和转移注意 转移注意 反驳 保持距离 学习与自己争辩 证据 其他可能性 暗示 用处 你的反驳记 ...