`

tomcat源码—redirect和forward的实现

阅读更多

全文转载:http://luckaway.iteye.com/blog/557980

作者:luckaway [from javaeye]

 

网上已经有很多关于redirect和forward区别的文章,更多的都是只是一些概念上的描述,虽然在大多情况下,知道这些就已经足够了。但也有例外:forward not working for struts2,why?我也是在工作中碰到了这个问题,才特意看了下tomcat有关这部分的源代码。深刻的了解下也无妨。
redirect和forward都是属于servlet规范的,不同的servlet容器的实现可能会有一些区别,但原理都是类似的。

redirect和forward的定义:
1. redirect(重定向):服务端发送给客户端一个重定向的临时响应头,这个响应头包含重定向之后的URL,客户端用新的URL重新向服务器发送一个请求。
2. forward(请求转向):服务器程序内部请求转向,这个特性允许前一个程序用于处理请求,而后一个程序用来返回响应。


Redirect的原理比较简单,它的定义也已经描述的很清楚了,我也不想多讲什么,就贴一段简单的代码吧!

org.apache.catalina.connector.Response#sendRedirect(String): 
  

void sendRedirect(String location)    
        throws IOException {   
  
        if (isCommitted())   
            throw new IllegalStateException   
                (sm.getString("coyoteResponse.sendRedirect.ise"));   
  
        // Ignore any call from an included servlet   
        if (included)   
            return;    
  
        // Clear any data content that has been buffered   
        resetBuffer();   
  
        // Generate a temporary redirect to the specified location   
        try {   
            String absolute = toAbsolute(location);   
            setStatus(SC_FOUND);   
            setHeader("Location", absolute);   
        } catch (IllegalArgumentException e) {   
            setStatus(SC_NOT_FOUND);   
        }   
  
        // Cause the response to be finished (from the application perspective)   
        setSuspended(true);   
}  

 
方法行为:先把相对路径转换成绝对路径,再包装一个包含有新的URL的临时响应头,“SC_FOUND”的值是302,就是重定向临时响应头的状态码。如果传入的“location”值不合法,就包装一个404的响应头。

下面就来看看tomcat是如何实现forward的,forward为什么在struts2下会无效(注解:其实是可以设置的)。

先看下程序是如何调用forward的:

req.getRequestDispatcher("testForward").forward(req, resp);  

 

整个过程分两个步骤来执行
1. 得到一个请求调度器
2. 通过调度器把请求转发过去。


第一步骤,获取请求调度器。
org.apache.catalina.connector.Request#getRequestDispatcher(String)
public RequestDispatcher getRequestDispatcher(String path) {   
  
        if (request == null) {   
            throw new IllegalStateException(   
                            sm.getString("requestFacade.nullRequest"));   
        }   
  
        if (Globals.IS_SECURITY_ENABLED){   
            return (RequestDispatcher)AccessController.doPrivileged(   
                new GetRequestDispatcherPrivilegedAction(path));   
        } else {   
             return request.getRequestDispatcher(path);     
        }  
 
方法行为:把获取RequestDispatcher的任务交个内部的request。它们之间的关系如下所示 


org.apache.catalina.connector.RequestFacade和类org.apache.catalina.connector.Request都是实现了javax.servlet.http.HttpServletRequest接口,而RequestFacade内部有包装了个Request,对Request的访问做了些控制,应该是代理模式

org.apache.catalina.connector.Request#getRequestDispatcher(String)
public RequestDispatcher getRequestDispatcher(String path) {   
         if (path.startsWith("/"))   
           return (context.getServletContext().getRequestDispatcher(path));   
  
       //省略了部分代码   
       return (context.getServletContext().getRequestDispatcher(relative));    
  
   }  
 
方法行为:把绝对路径转换成相对路径,最终的格式如“/testForward”。若已经是这种格式的相对路径,就无需再转换了。
接下来就转交给ServletContext来处理,ServletContext是web项目的一个上下文,包含所有的Servlet集合,还定义了一些Servlet与容器之间交互的接口。
org.apache.catalina.core.ApplicationContext#getRequestDispatcher(String)
public RequestDispatcher getRequestDispatcher(String path) {   
          //省去部分代码   
          context.getMapper().map(uriMB, mappingData);   
          //省去部分代码   
      Wrapper wrapper = (Wrapper) mappingData.wrapper;   
      String wrapperPath = mappingData.wrapperPath.toString();   
      String pathInfo = mappingData.pathInfo.toString();   
  
      mappingData.recycle();   
         
      // Construct a RequestDispatcher to process this request   
      return new ApplicationDispatcher   
          (wrapper, uriCC.toString(), wrapperPath, pathInfo,    
           queryString, null);    
  }  
 
方法行为:根据路径名“path”找到一个包含有Servlet的Wrapper,最后实例化一个ApplicationDispatcher,并且返回该ApplicationDispatcher。

该方法里非常关键的一行:context.getMapper().map(uriMB, mappingData)。
Mapper的类定义我不知道如何描述,就贴上原文吧:Mapper, which implements the servlet API mapping rules (which are derived from the HTTP rules)。
不过只想了解forward的原理,熟悉map函数就够了。

org.apache.tomcat.util.http.mapper.Mapper#map(org.apache.tomcat.util.buf.MessageBytes, org.apache.tomcat.util.http.mapper.MappingData):
public void map(MessageBytes uri, MappingData mappingData)   
    throws Exception {   
  
    uri.toChars();   
    CharChunk uricc = uri.getCharChunk();   
    uricc.setLimit(-1);   
    internalMapWrapper(context, uricc, mappingData);   
  
}  
 
方法行为:。。。。。。。就介绍下参数吧,uri可以理解是path(“/testforward”)的一个变形,而mappingData用于存储当前线程用到的部分数据。该函数是没有返回值的,处理之后的结果就是存放到mappingData里的。

org.apache.tomcat.util.http.mapper.Mapper#internalMapWrapper(Mapper$Context,org.apache.tomcat.util.buf.CharChunk, org.apache.tomcat.util.http.mapper.MappingData):
private final void internalMapWrapper(Context context, CharChunk path,   
                                         MappingData mappingData)   
       throws Exception {   
  
       int pathOffset = path.getOffset();   
       int pathEnd = path.getEnd();   
       int servletPath = pathOffset;   
       boolean noServletPath = false;   
  
       int length = context.name.length();   
       if (length != (pathEnd - pathOffset)) {   
           servletPath = pathOffset + length;   
       } else {   
           noServletPath = true;   
           path.append('/');   
           pathOffset = path.getOffset();   
           pathEnd = path.getEnd();   
           servletPath = pathOffset+length;   
       }   
  
       path.setOffset(servletPath);   
  
       // Rule 1 -- Exact Match   
       Wrapper[] exactWrappers = context.exactWrappers;   
       internalMapExactWrapper(exactWrappers, path, mappingData);   
  
       // Rule 2 -- Prefix Match   
       boolean checkJspWelcomeFiles = false;   
       Wrapper[] wildcardWrappers = context.wildcardWrappers;   
       if (mappingData.wrapper == null) {   
           internalMapWildcardWrapper(wildcardWrappers, context.nesting,    
                                      path, mappingData);   
           if (mappingData.wrapper != null && mappingData.jspWildCard) {   
               char[] buf = path.getBuffer();   
               if (buf[pathEnd - 1] == '/') {   
                   /*  
                    * Path ending in '/' was mapped to JSP servlet based on  
                    * wildcard match (e.g., as specified in url-pattern of a  
                    * jsp-property-group.  
                    * Force the context's welcome files, which are interpreted  
                    * as JSP files (since they match the url-pattern), to be  
                    * considered. See Bugzilla 27664.  
                    */    
                   mappingData.wrapper = null;   
                   checkJspWelcomeFiles = true;   
               } else {   
                   // See Bugzilla 27704   
                   mappingData.wrapperPath.setChars(buf, path.getStart(),   
                                                    path.getLength());   
                   mappingData.pathInfo.recycle();   
               }   
           }   
       }   
  
       if(mappingData.wrapper == null && noServletPath) {   
           // The path is empty, redirect to "/"   
           mappingData.redirectPath.setChars   
               (path.getBuffer(), pathOffset, pathEnd);   
           path.setEnd(pathEnd - 1);   
           return;   
       }   
  
       // Rule 3 -- Extension Match   
       Wrapper[] extensionWrappers = context.extensionWrappers;   
       if (mappingData.wrapper == null && !checkJspWelcomeFiles) {   
           internalMapExtensionWrapper(extensionWrappers, path, mappingData);   
       }   
  
       // Rule 4 -- Welcome resources processing for servlets   
       if (mappingData.wrapper == null) {   
           boolean checkWelcomeFiles = checkJspWelcomeFiles;   
           if (!checkWelcomeFiles) {   
               char[] buf = path.getBuffer();   
               checkWelcomeFiles = (buf[pathEnd - 1] == '/');   
           }   
           if (checkWelcomeFiles) {   
               for (int i = 0; (i < context.welcomeResources.length)   
                        && (mappingData.wrapper == null); i++) {   
                   path.setOffset(pathOffset);   
                   path.setEnd(pathEnd);   
                   path.append(context.welcomeResources[i], 0,   
                               context.welcomeResources[i].length());   
                   path.setOffset(servletPath);   
  
                   // Rule 4a -- Welcome resources processing for exact macth   
                   internalMapExactWrapper(exactWrappers, path, mappingData);   
  
                   // Rule 4b -- Welcome resources processing for prefix match   
                   if (mappingData.wrapper == null) {   
                       internalMapWildcardWrapper   
                           (wildcardWrappers, context.nesting,    
                            path, mappingData);   
                   }   
  
                   // Rule 4c -- Welcome resources processing   
                   //            for physical folder   
                   if (mappingData.wrapper == null  
                       && context.resources != null) {   
                       Object file = null;   
                       String pathStr = path.toString();   
                       try {   
                           file = context.resources.lookup(pathStr);   
                       } catch(NamingException nex) {   
                           // Swallow not found, since this is normal   
                       }   
                       if (file != null && !(file instanceof DirContext) ) {   
                           internalMapExtensionWrapper(extensionWrappers,   
                                                       path, mappingData);   
                           if (mappingData.wrapper == null  
                               && context.defaultWrapper != null) {   
                               mappingData.wrapper =   
                                   context.defaultWrapper.object;   
                               mappingData.requestPath.setChars   
                                   (path.getBuffer(), path.getStart(),    
                                    path.getLength());   
                               mappingData.wrapperPath.setChars   
                                   (path.getBuffer(), path.getStart(),    
                                    path.getLength());   
                               mappingData.requestPath.setString(pathStr);   
                               mappingData.wrapperPath.setString(pathStr);   
                           }   
                       }   
                   }   
               }   
  
               path.setOffset(servletPath);   
               path.setEnd(pathEnd);   
           }   
                                          
       }  
 
方法行为:通过“path”从“context”里找到对应的Servlet,存放到“mappingData”里。
可以看到这里有7个匹配Servlet规则:
1. Rule 1 -- Exact Match:精确匹配,匹配web.xml配置的格式如“<url-pattern>/testQiu</url-pattern>”的Servlet
2. Rule 2 -- Prefix Matcha:前缀匹配,匹配的Servlet格式如“<url-pattern>/testQiu/*</url-pattern>”
3. Rule 3 -- Extension Match:扩展匹配,匹配jsp或者jspx
4. ---Rule 4a -- Welcome resources processing for exact macth:
5. ---Rule 4b -- Welcome resources processing for prefix match:
6. ---Rule 4c -- Welcome resources processing for physical folder:
7. Rule 7 --如果前面6条都没匹配到,那就返回org.apache.catalina.servlets.DefaultServlet。

其实这里真正的匹配的是Wapper,而不是Servlet,因为Wapper最重要的一个属性就是Servlet,说成“匹配Servlet”是为了更容易的表达。

至此返回RequestDispatcher就结束了。

接下来就是讲解RequestDispatcher.forward了。Forward的就不贴出全部的源代码,只贴一些重要的片段,绝大部分的逻辑都在org.apache.catalina.core.ApplicationDispatcher类里。
先描述下过程:
1. 设置request里的部分属性值,如:请求的路径、参数等。
2. 组装一个FilterChain链,调用doFilter方法。
3. 最后根据实际情况调用Filter的doFilter函数或者Servlet的service函数。

注:FilterChain和Filter是两个不同的接口,两个接口的UML 


org.apache.catalina.core.ApplicationDispatcher#doForward(ServletRequest,ServletResponse):
private void doForward(ServletRequest request, ServletResponse response)   
        throws ServletException, IOException   
         //省略了部分代码   
        // Handle an HTTP named dispatcher forward   
        if ((servletPath == null) && (pathInfo == null)) {   
//省略了部分代码   
        } else {// Handle an HTTP path-based forward   
            ApplicationHttpRequest wrequest =   
                (ApplicationHttpRequest) wrapRequest(state);   
            String contextPath = context.getPath();   
            HttpServletRequest hrequest = state.hrequest;   
            if (hrequest.getAttribute(Globals.FORWARD_REQUEST_URI_ATTR) == null) {   
                wrequest.setAttribute(Globals.FORWARD_REQUEST_URI_ATTR,   
                                      hrequest.getRequestURI());   
                wrequest.setAttribute(Globals.FORWARD_CONTEXT_PATH_ATTR,   
                                      hrequest.getContextPath());   
                wrequest.setAttribute(Globals.FORWARD_SERVLET_PATH_ATTR,   
                                      hrequest.getServletPath());   
                wrequest.setAttribute(Globals.FORWARD_PATH_INFO_ATTR,   
                                      hrequest.getPathInfo());   
                wrequest.setAttribute(Globals.FORWARD_QUERY_STRING_ATTR,   
                                      hrequest.getQueryString());   
            }   
    
            wrequest.setContextPath(contextPath);   
            wrequest.setRequestURI(requestURI);   
            wrequest.setServletPath(servletPath);   
            wrequest.setPathInfo(pathInfo);   
            if (queryString != null) {   
                wrequest.setQueryString(queryString);   
                wrequest.setQueryParams(queryString);   
            }   
  
            processRequest(request,response,state);   
        }   
        }   
//省略了部分代码   
    }  
 
第1步:设置新的request的属性: 
wrequest.setContextPath(contextPath);   
          wrequest.setRequestURI(requestURI);   
          wrequest.setServletPath(servletPath);   
          wrequest.setPathInfo(pathInfo);   
          if (queryString != null) {   
              wrequest.setQueryString(queryString);   
              wrequest.setQueryParams(queryString);   
          }  
 
第2步:组装FitlerChain链,根据web.xml配置信息,是否决定添加Filter----
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>


org.apache.catalina.core.ApplicationFilterFactory#createFilterChain(ServletRequest, Wrapper, Servlet):

public ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {   
        //省略部分代码   
            filterChain = new ApplicationFilterChain();   
        }   
  
        filterChain.setServlet(servlet);   
  
        filterChain.setSupport   
            (((StandardWrapper)wrapper).getInstanceSupport());   
  
        // Acquire the filter mappings for this Context   
        StandardContext context = (StandardContext) wrapper.getParent();   
        FilterMap filterMaps[] = context.findFilterMaps();   
  
        // If there are no filter mappings, we are done   
        if ((filterMaps == null) || (filterMaps.length == 0))   
            return (filterChain);   
  
        // Acquire the information we will need to match filter mappings   
        String servletName = wrapper.getName();   
  
        // Add the relevant path-mapped filters to this filter chain   
        for (int i = 0; i < filterMaps.length; i++) {   
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {   
                continue;   
            }   
            if (!matchFiltersURL(filterMaps[i], requestPath))   
                continue;   
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)   
                context.findFilterConfig(filterMaps[i].getFilterName());   
            if (filterConfig == null) {   
                ;       // FIXME - log configuration problem   
                continue;   
            }   
            boolean isCometFilter = false;   
            if (comet) {   
                try {   
                    isCometFilter = filterConfig.getFilter() instanceof CometFilter;   
                } catch (Exception e) {   
                    // Note: The try catch is there because getFilter has a lot of    
                    // declared exceptions. However, the filter is allocated much   
                    // earlier   
                }   
                if (isCometFilter) {   
                    filterChain.addFilter(filterConfig);   
                }   
            } else {   
                filterChain.addFilter(filterConfig);   
            }   
        }   
  
       //省略部分代码   
  
        // Return the completed filter chain   
        return (filterChain);   
  
}  
 
如果是<dispatcher>REQUEST</dispatcher>,那就不添加Filter,默认设置是REQUEST
如果是<dispatcher>FORWARD</dispatcher>,添加Filter到FilterChain。

第3步:调用doFilter或者service,代码删减了很多。

org.apache.catalina.core.ApplicationFilterChain#doFilter(ServletRequest, ServletResponse):

  public void doFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {   
            internalDoFilter(request,response);   
  }   
  
  
org.apache.catalina.core.ApplicationFilterChain#internalDoFilter(ServletRequest, ServletResponse)   
private void internalDoFilter(ServletRequest request,    
                                  ServletResponse response)   
        throws IOException, ServletException {   
  
        // Call the next filter if there is one   
        if (pos < n) {   
                    filter.doFilter(request, response, this);   
            return;   
        }   
       servlet.service((HttpServletRequest) request,(HttpServletResponse) response);               
}  
 
如果我对Filter非常了解的,根本就不需要花那么多时间去查看tomcat源代码。只要在web.xml增加一点配置就OK了。
<filter-mapping>   
        <filter-name>struts2</filter-name>   
        <url-pattern>/*</url-pattern>   
        <dispatcher>REQUEST</dispatcher>   
        <dispatcher>FORWARD</dispatcher>   
</filter-mapping>  
 
分享到:
评论

相关推荐

    基于Django花卉商城系统的设计与实现-2885fb37-.zip

    基于Django花卉商城系统的设计与实现_2885fb37--.zip

    102页-智慧农业解决方案.pdf

    智慧农业,作为现代农业的新篇章,正引领着农业生产的革命性变革。本解决方案以物联网、云计算、大数据等先进技术为核心,为农业生产打造了一套全面、智能的管理系统。 想象一下,从温室大棚到广袤田野,智能传感器遍布每个角落,它们能实时感知空气温湿度、土壤水分、光照强度等环境参数,仿佛为农作物装上了“眼睛”和“耳朵”。这些数据通过物联网技术传输到云端,经过大数据分析,为农民提供精准的种植建议,如何时灌溉、施肥、防虫,让农业生产变得更加科学、高效。 更有趣的是,通过智慧农业平台,农民可以远程监控作物生长情况,甚至用手机就能控制温室大棚的遮阳板、通风设备等,实现“指尖上的农业”。此外,方案还包含了农产品可追溯系统,从田间到餐桌,每一步都可追溯,让消费者吃得放心。而智慧农业电商平台,则让农产品销售更加便捷,农民直接对接市场,收益倍增。 总之,这套智慧农业解决方案不仅让农业生产变得更加智能、高效,还提升了农产品的质量和安全,为农民带来了实实在在的收益,开启了农业现代化的新篇章。 对于想要投身智慧农业领域的你来说,这不仅仅是一套解决方案,更是一把开启现代农业大门的钥匙,引领你走向更加辉煌的未来。

    【人工智能模型部署】DeepSeek本地部署与WebUI可视化:环境搭建、模型启动及界面开发指南文档的主要内容

    内容概要:本文档详细介绍了DeepSeek本地部署与WebUI可视化的一般步骤。本地部署方面,涵盖了环境准备(硬件要求如多核CPU、8GB以上内存或带适当显存的NVIDIA GPU,软件环境涵盖操作系统如Ubuntu 20.04及以上版本、Python环境及依赖库如PyTorch或TensorFlow)、获取DeepSeek模型代码和权重(通过官方仓库克隆代码,从指定渠道下载权重)、模型配置与启动(配置模型参数,运行启动脚本以初始化模型和服务)。WebUI可视化部分则推荐了Streamlit和Gradio两种框架,介绍了它们的安装、使用方法(通过编写脚本调用DeepSeek API构建交互界面),以及集成与部署(确保WebUI与模型服务之间的数据正确传递,在本地运行后可通过浏览器访问)。 适合人群:对深度学习模型部署有一定了解的技术人员,尤其是那些希望将DeepSeek模型应用于本地环境并提供用户友好界面的研发人员。 使用场景及目标:①为希望在本地环境中运行DeepSeek模型的研究者或开发者提供详细的部署指南;②帮助用户快速搭建一个带有图形化操作界面的DeepSeek应用,降低使用门槛,提高用户体验。 阅读建议:在阅读时,应根据自己的操作系统环境和硬件条件调整相应的配置要求,同时注意按照官方文档的具体指引操作,确保各组件版本兼容,以便顺利完成部署和可视化工作。

    MISRA C 2014和MISRA CPP 2008版本

    MISRA C 2014和MISRA CPP 2008版本

    Revit2024二次开发之安装Addin

    Revit2024二次开发之安装Addin

    ai应用文生视频大模型及AI人应用方案设计.docx### 文生视频大模型及AI人应用方案总结

    内容概要:本文详细介绍了文生视频大模型及AI人应用方案的设计与实现。文章首先阐述了文生视频大模型的技术基础,包括深度生成模型、自然语言处理(NLP)和计算机视觉(CV)的深度融合,以及相关技术的发展趋势。接着,文章深入分析了需求,包括用户需求、市场现状和技术需求,明确了高效性、个性化和成本控制等关键点。系统架构设计部分涵盖了数据层、模型层、服务层和应用层的分层架构,确保系统的可扩展性和高效性。在关键技术实现方面,文章详细描述了文本解析与理解、视频生成技术、AI人交互技术和实时处理与反馈机制。此外,还探讨了数据管理与安全、系统测试与验证、部署与维护等重要环节。最后,文章展示了文生视频大模型在教育、娱乐和商业领域的应用场景,并对其未来的技术改进方向和市场前景进行了展望。 适用人群:具备一定技术背景的研发人员、产品经理、数据科学家以及对AI视频生成技术感兴趣的从业者。 使用场景及目标:①帮助研发人员理解文生视频大模型的技术实现和应用场景;②指导产品经理在实际项目中应用文生视频大模型;③为数据科学家提供技术优化和模型改进的思路;④让从业者了解AI视频生成技术的市场潜力和发展趋势。 阅读建议:本文内容详尽,涉及多个技术细节和应用场景,建议读者结合自身的专业背景和技术需求,重点阅读与自己工作相关的章节,并结合实际项目进行实践和验证。

    黑板风格毕业答辩模板25个

    黑板风格毕业答辩模板是一系列富有创意和趣味性的答辩文档模板,专为追求独特表达的大学生设计。这25个模板模拟了传统黑板的效果,结合了手绘风格与现代设计理念,使得内容呈现既生动又具学术感。每个模板都强调清晰的结构和易于理解的布局,适用于各类学科和研究领域,帮助学生有效地展示研究成果和核心观点。 黑板风格不仅带来亲切感,还能唤起人们对课堂学习的回忆,为答辩增添了轻松而专业的氛围。这些模板配备了丰富的图标、示意图和配色,既美观又实用,能够帮助学生在答辩中更好地吸引评审的注意力,增强信息的传达效果。无论是科技、艺术还是人文社科,黑板风格毕业答辩模板都能够为你的演示增添一份独特的魅力,提升你的表现,助力你在毕业答辩中取得成功。

    delphi-ACCESS宠物医院

    delphi_ACCESS宠物医院

    社会心理学:MATLAB复杂系统仿真在群体行为预测中的跨学科研究.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 你是否渴望高效解决复杂的数学计算、数据分析难题?MATLAB 就是你的得力助手!作为一款强大的技术计算软件,MATLAB 集数值分析、矩阵运算、信号处理等多功能于一身,广泛应用于工程、科学研究等众多领域。 其简洁直观的编程环境,让代码编写如同行云流水。丰富的函数库和工具箱,为你节省大量时间和精力。无论是新手入门,还是资深专家,都能借助 MATLAB 挖掘数据背后的价值,创新科技成果。别再犹豫,拥抱 MATLAB,开启你的科技探索之旅!

    DDS Accepted Assessment Instruments DDS 认可的评估工具.doc

    DDS Accepted Assessment Instruments DDS 认可的评估工具.doc

    Windows下MySQL安装与配置教程.markdown

    mysql安装配置教程 本教程将指导您在Windows操作系统上安装和配置MySQL数据库,适用于MySQL 8.0及以上版本。本教程以清晰的步骤说明,确保初学者也能顺利完成安装和基本配置。

    体育科学突破:MATLAB生物力学仿真优化短跑运动员起跑姿态.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 你是否渴望高效解决复杂的数学计算、数据分析难题?MATLAB 就是你的得力助手!作为一款强大的技术计算软件,MATLAB 集数值分析、矩阵运算、信号处理等多功能于一身,广泛应用于工程、科学研究等众多领域。 其简洁直观的编程环境,让代码编写如同行云流水。丰富的函数库和工具箱,为你节省大量时间和精力。无论是新手入门,还是资深专家,都能借助 MATLAB 挖掘数据背后的价值,创新科技成果。别再犹豫,拥抱 MATLAB,开启你的科技探索之旅!

    ### 智慧教育应用发展研究报告(2025年)总结

    内容概要:《智慧教育应用发展研究报告(2025年)》由中国信息通信研究院发布,全面梳理了全球及我国智慧教育的发展现状和趋势。报告指出,智慧教育通过多种数字技术促进教育模式、管理模式和资源生成等方面的变革。国外经济体如欧盟、美国、韩国和日本纷纷通过顶层设计推动智慧教育发展,而我国则通过政策支持、基础设施建设、技术融合等多方面努力,推动智慧教育进入“快车道”。智慧教育应用场景分为智慧校园和校外教育两类,涵盖教学、考试、评价、管理和服务等多个方面。报告还详细分析了支撑智慧教育发展的技术、产业、基础设施和安全能力的发展趋势,并指出了当前面临的挑战及建议。 适用人群:教育领域的政策制定者、教育管理者、教育技术从业者、研究人员和关心教育发展的社会各界人士。 使用场景及目标:①了解全球及我国智慧教育的最新进展和趋势;②为政策制定者提供决策参考;③为教育管理者和技术从业者提供实施智慧教育的具体指导;④促进教育技术的研发和应用。 其他说明:报告强调了智慧教育在促进教育公平、提升教育质量、推动教育模式创新等方面的重要性,并呼吁加强跨领域协同攻关、缩小教育数字化差距、强化网络信息安全和提升教师数字素养,以应对当前面临的挑战。

    AC6003V200R008C10SPC300.cc华为AC6003-8固件系统下载

    华为AC6003-8固件系统 网上确实 不好找啊

    教育领域基于Word模板的实习证明文档设计:学生实习信息规范化记录与管理

    内容概要:这是一份实习证明模板,用于证明学生在指定单位完成实习经历。主要内容包括学生的学校、年级、专业以及姓名,明确标注了实习开始日期、实习单位名称、具体岗位、薪资待遇、单位地址及联系方式等信息,还列出了实习期间的指导教师及其联系方式。文件最后设有单位公章、单位负责人签字及联系电话的位置,并标明开具证明的日期。; 适合人群:即将或正在实习的大学生、大专生以及其他需要开具实习证明的学生群体。; 使用场景及目标:①为学生提供规范的实习证明文件,方便学校、企业或其他相关机构核实实习情况;②作为实习经历的正式书面记录,可用于求职、升学等场合。; 其他说明:此模板可根据不同学校和单位的具体要求进行适当调整,确保信息完整性和准确性。在填写时应注意核实各项信息的真实性,确保与实际情况相符。

    IMG_20250416_154832.jpg

    IMG_20250416_154832.jpg

    红色警戒95版(RA95)

    游戏亲测无毒可用,可在Win10、Win11等系统直接运行(执行ra95.exe,无需虚拟机) #初代经典红警,#红警95,#RTS,#电脑游戏,#怀旧游戏

    【路径规划】基于matlab改进的RRT算法移动机器人路径规划【含Matlab源码 13175期】.zip

    Matlab领域上传的视频是由对应的完整代码运行得来的,完整代码皆可运行,亲测可用,适合小白; 1、从视频里可见完整代码的内容 主函数:main.m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开main.m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主; 4.1 博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作

    教育技术领域JavaWeb技术在网络考试系统设计与实现中的应用【毕业论文+数据库+项目辅导视频+源代码】

    内容概要:本文介绍了网络考试系统的设计与实现,旨在通过浏览器作为界面,利用B/S模式解决传统考试流程复杂、耗时的问题。系统主要采用JavaWeb技术和MySql数据库,设计了用户管理、功能管理、角色权限管理、学生网络考试、试题管理、错题管理、自动组卷等功能模块。文章详细描述了系统的可行性分析、需求分析、总体设计、详细设计、数据库设计以及系统测试等内容。通过功能测试和兼容性测试,确保系统的稳定性和实用性。该系统基本可以满足简单的在线考试需求,运行良好,基本达到了设计要求。 适合人群:计算机科学与技术、软件工程等相关专业的本科生、研究生,以及对网络考试系统感兴趣的教育工作者和开发人员。 使用场景及目标:①适用于高校、培训机构等教育机构,用于组织和管理在线考试;②帮助教师减少出卷、阅卷和统计的时间,提高工作效率;③为学生提供便捷的在线考试平台,支持错题解析,促进自主学习。 阅读建议:本文不仅介绍了系统的具体实现细节,还涵盖了相关技术的应用和开发流程,建议读者在阅读时重点关注系统设计思路和关键技术的应用,同时结合实际操作,理解系统的工作原理和实现方法。

    毕节市乡镇边界,矢量边界,shp格式

    矢量边界,行政区域边界,精确到乡镇街道,可直接导入arcgis使用

Global site tag (gtag.js) - Google Analytics