Spring Boot嵌入式Tomcat启动源码解析:从自动配置到端口绑定
嵌入式Servlet容器是Spring Boot和传统Spring应用最直观的区别之一。以前做Java Web开发要先装一个外置Tomcat把WAR包丢进去再启动容器现在一个java -jar搞定应用自己把Tomcat拉起来端口一监听就能访问。这篇源码笔记就围绕这个“自己拉起Tomcat”的过程展开讲清楚Spring Boot是怎么把嵌入式Tomcat实例化、配置、启动起来的从自动配置入口到Connector真正绑定端口再到常见的定制姿势和排查经验。适合对Spring Boot自动配置有一定了解、想深入源码但不知从哪下手的开发者。1. 嵌入式容器的设计思路与自动配置核心看源码之前得先建立一个整体认知Spring Boot并不是在Spring Framework之上打补丁而是把“容器生命周期”整个接管了。传统Servlet容器外置Tomcat由外部脚本启动应用只负责提供WAR包里的ServletContext初始化逻辑而嵌入式模式下容器变成一个普通Bean由应用上下文统一创建、启动、销毁。这个转变带来了一个核心设计问题Spring Boot如何在不知道你选哪个容器的情况下把Tomcat、Jetty、Undertow统一抽象成一套启动流程答案就是两层抽象一层是ServletWebServerFactory负责创建WebServer另一层是WebServer负责管理容器的生命周期。源码分析的主线就是顺着这两层抽象走完一个完整的启动链路。1.1 自动配置入口条件装配如何选中Tomcat先看ServletWebServerFactoryAutoConfiguration这是所有嵌入式Servlet容器的总入口。它的头上挂了一长串条件注解核心逻辑是当当前classpath里存在Servlet类且WebApplicationType是Servlet类型时才会生效。AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) Configuration(proxyBeanMethods false) ConditionalOnClass(ServletRequest.class) EnableConfigurationProperties(ServerProperties.class) Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { ... }关键在Import进来的EmbeddedTomcat。这个配置类内部又通过ConditionalOnClass判断classpath里有没有Tomcat.class有就实例化TomcatServletWebServerFactory。Configuration(proxyBeanMethods false) ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) ConditionalOnMissingBean(value ServletWebServerFactory.class, search SearchStrategy.CURRENT) public static class EmbeddedTomcat { Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { return new TomcatServletWebServerFactory(); } }这里有一个容易忽略又极其重要的点ConditionalOnMissingBean。意思是只要用户自己定义了ServletWebServerFactory类型的Bean自动配置的工厂就整体让位。这就是为什么网上很多教程让你自定义WebServerFactoryCustomizer或者直接声明TomcatServletWebServerFactory——不是覆盖某个属性而是直接把整个默认工厂替换掉。从条件装配的设计可以看出Spring Boot对第三方依赖的处理逻辑是一致的你依赖里有什么我就装配什么你自己显式配置了我就退位让贤。理解了这个前提后面看任何自动配置类都不会懵。1.2 ServerProperties所有server.*配置的归宿自动配置类上有个EnableConfigurationProperties(ServerProperties.class)这行代码把所有server.*配置项绑定到了一个配置属性类上。端口、地址、Servlet上下文路径、压缩、SSL、优雅停机这些配置最终都会汇聚到这里。ConfigurationProperties(prefix server, ignoreUnknownFields true) public class ServerProperties { private Integer port; private InetAddress address; private Servlet servlet new Servlet(); private ServletContextInitializer[] servletContextInitializers; private Compression compression new Compression(); ... }这个类最值得关注的是它的嵌套结构。以server.servlet.context-path为例它对应的并非ServerProperties的顶层字段而是ServerProperties.Servlet里的contextPath。这意味着写配置的时候要严格对齐层级写错位置不会报错但不会生效。我见过不少新手把server.servlet当成server.context来配结果路径一直不对排查半天发现配置项已经在新版本换了位置。另外要注意ServerProperties只是一个数据载体真正被工厂消费的时机是在prepareContext创建TomcatServletWebServerFactory实例之后。工厂在getWebServer方法里读取这些属性逐个设置到Tomcat的各个组件上。这一段时序关系是理解整个启动过程的关键线索。2. 关键对象与源码骨架从工厂到WebServer光看自动配置的注解还不够得进入具体类的方法才能看到Tomcat是怎么被一步步组装出来的。这一节把ServletWebServerFactory和TomcatWebServer这层核心骨架拆开。2.1 ServletWebServerFactory接口职责与实现体系ServletWebServerFactory接口定义只有一个方法getWebServer(ServletContextInitializer... initializers)返回WebServer。FunctionalInterface public interface ServletWebServerFactory { WebServer getWebServer(ServletContextInitializer... initializers); }这个接口在Spring Boot中只有三个实现类TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory。三者没有公共抽象父类各自直接实现接口因为Tomcat、Jetty、Undertow的API接口差异实在太大强行抽象只会把代码搞得更复杂。这个取舍很有意思也提示我们做设计时不要为了统一而统一。TomcatServletWebServerFactory继承自AbstractServletWebServerFactory这个抽象类提供了大部分通用逻辑比如createTempDir创建工作临时目录、checkValidServletContextInitializer、mergeInitializers。而Tomcat相关的特殊逻辑比如configureTomcat、prepareContext、getWebServer里的具体构建步骤全部留在TomcatServletWebServerFactory内部。从使用者的角度你唯一需要关心的入口就是getWebServer方法。它接收的ServletContextInitializer数组非常重要——Spring Boot把DispatcherServlet注册、ContextLoaderListener配置、过滤器注册等事情全部封装成ServletContextInitializer回调在容器启动后执行。理解这一点就理解了为什么嵌入式Tomcat不需要web.xml。2.2 TomcatWebServerWebServer的实现与生命周期WebServer接口是Spring Boot对容器生命周期的抽象public interface WebServer { void start() throws WebServerException; void stop() throws WebServerException; int getPort(); }TomcatWebServer是这个接口的Tomcat实现。它内部持有真正的org.apache.catalina.startup.Tomcat实例所有的start、stop操作都是对该实例的调用。换言之Spring Boot没有自己实现一个Tomcat而是利用Tomcat官方提供的嵌入式API来操作Tomcat。构造函数里有一段值得细看的逻辑如果外部传入了TomcatConnectorCustomizer或TomcatContextCustomizer会做一次拷贝防止后续修改影响当前实例否则直接使用内部保存的集合。接着调用initialize()方法这个方法负责创建Tomcat实例、初始化Connector、Host、Engine等。有一个细节很容易踩坑TomcatWebServer在构造时并不会立刻启动Tomcat它只完成初始化。真正的启动发生在ServletWebServerApplicationContext的onRefresh阶段通过webServer.start()触发。也就是说“创建工厂”和“启动容器”是两个独立步骤中间隔着一个上下文刷新过程。public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) { this.tomcat tomcat; this.autoStart autoStart; this.gracefulShutdown (shutdown Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null; initialize(); }这个构造函数还有第三个参数Shutdown对应server.shutdown配置项默认是IMMEDIATE可配为GRACEFUL。优雅停机不是Tomcat原生的行为是Spring Boot在TomcatWebServer里封装的一个功能点后面会在实操章节专门展开。3. 核心启动链路从SpringApplication到Tomcat端口监听很多文章讲源码时习惯按类来逐个点但理解起来最顺的方式是跟时间线。这一节从一个Spring Boot应用启动开始一步步走到Tomcat真正监听端口把中间涉及的关键方法和调用时机串起来。3.1 触发时机上下文刷新与onRefresh方法入口在SpringApplication.run()最终会走到AbstractApplicationContext.refresh()。ServletWebServerApplicationContext覆写了onRefresh()方法在这里完成WebServer的创建——但请注意这里只创建、不启动。Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException(Unable to start web server, ex); } }createWebServer()的逻辑是这样的先从容器里拿ServletWebServerFactory如果没有且当前上下文是ServletWebServerApplicationContext实例会尝试从父上下文获取拿到工厂后调用getWebServer(getSelfInitializer())创建WebServer。getSelfInitializer()返回的是一个ServletContextInitializer它会在稍后回调selfInitialize()方法把Spring的DispatcherServlet、CharacterEncodingFilter等组件注册进ServletContext。private void createWebServer() { WebServer webServer this.webServer; ServletContext servletContext getServletContext(); if (webServer null servletContext null) { ServletWebServerFactory factory getWebServerFactory(); this.webServer factory.getWebServer(getSelfInitializer()); } else if (servletContext ! null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException(Cannot initialize servlet context, ex); } } initPropertySources(); }这个方法还处理了一个边界情况如果你在外部容器里运行WAR包servletContext不为空此时不会再去创建嵌入式容器而是直接把初始化逻辑交给外部容器的ServletContext。这也就是Spring Boot同时支持java -jar和WAR部署两种模式的原因。3.2 getWebServer嵌入式Tomcat的完整组装过程进入TomcatServletWebServerFactory.getWebServer()这里就是Tomcat实例被一点点搭起来的地方。核心步骤如下创建Tomcat实例设置baseDir默认是当前系统临时目录下的随机子目录。调用configureEngine配置Engine主要是设置Engine名称和Host名称并为Host配置Valve——TomcatWebServer里存放的Valve列表会逐一添加。遍历connectorCustomizers把自定义的TomcatConnectorCustomizer应用到连接器上。为Host添加TomcatContextCustomizer这些定制器会在上下文创建时生效。调用configureTomcat配置Tomcat实例本身比如Manager、Realm等。通过prepareContext准备应用上下文这里的TomcatEmbeddedContext对应的就是一个Web应用。调用getTomcatWebServer包装并返回TomcatWebServer。public WebServer getWebServer(ServletContextInitializer... initializers) { Tomcat tomcat new Tomcat(); File baseDir (this.baseDirectory ! null) ? this.baseDirectory : createTempDir(tomcat); tomcat.setBaseDir(baseDir.getAbsolutePath()); for (LifecycleListener listener : this.serverLifecycleListeners) { tomcat.getServer().addLifecycleListener(listener); } Connector connector new Connector(this.protocol); tomcat.getService().addConnector(connector); customizeConnector(connector); this.tomcat tomcat; return getTomcatWebServer(tomcat); }有一个细节值得单独拿出来说默认情况下Spring Boot创建的Connector使用的是org.apache.coyote.http11.Http11NioProtocol也就是NIO模型。它的protocol字段可以通过server.tomcat.protocol配置项修改比如改成org.apache.coyote.http11.Http11AprProtocol。prepareContext里发生的事情值得展开因为它决定了应用上下文和ServletContext之间的关系。这里创建了TomcatEmbeddedContext设置了ConfigClass添加了TomcatEmbeddedServletContainerInitializer这个Initializer负责在上下文启动后回调传入的ServletContextInitializer也就是前面提到的selfInitialize。同时还会注册Tomcat默认的Servlet——JSP引擎的JspServlet和默认Servlet。这个过程中如果检测到classpath里有Jasper依赖就注册JSP相关配置。3.3 Connector的配置机理与端口绑定Connector是整个Tomcat的“门面”它决定了HTTP请求怎么进来、用什么协议解析、解析后交给哪个Container。Spring Boot对Connector的定制都集中在customizeConnector方法里。protected void customizeConnector(Connector connector) { if (this.port 0) { connector.setPort(this.port); } if (connector.getConnectionTimeout() 0) { connector.setConnectionTimeout(this.connectionTimeout); } ... }ServerProperties里的port字段最终就是在这里被写入Connector。如果port为0表示随机端口这在测试场景里非常有用后面实操部分会专门讲。继续往下TomcatWebServer.initialize()方法里会做两件关键的事调用tomcat.start()启动Tomcat核心组件同时注册Connector到Service。tomcat.start()本身会触发连接的初始化但Spring Boot在检测到autoStart为false时会用Connector的pause()方法暂停连接器直到后续真正需要启动时才恢复。这套机制是为了配合ServletWebServerApplicationContext的刷新顺序先启动容器再注册DispatcherServlet会造成请求丢失所以Spring Boot会延后连接器的真正工作时刻。端口绑定的真正发生位置在Connector的AbstractEndpoint里NIO模式下对应的类是NioEndpoint它通过bind()方法完成ServerSocketChannel的绑定和监听。从源码追踪到这一层就能看到端口是从ServerProperties→Connector→NioEndpoint这条链路逐步传递的。排查端口冲突问题时理解这条链路很重要因为你在配置里写的server.port8080要经过这么多跳转才能真正生效。4. 实操场景常用定制与参数落地源码看完了最终还是要落到自己项目里。这一节挑几个最常见的定制场景把源码知识换算成可以直接抄的配置和代码。4.1 端口、上下文路径与随机端口的使用技巧端口和上下文路径是最基础的配置server.port8081 server.servlet.context-path/my-app server.address0.0.0.0随机端口在测试和微服务场景里非常实用。设置成0后Spring Boot会随机分配一个可用端口这个端口值可以在代码中通过WebServerApplicationContext获取。RestController public class PortController { Autowired private WebServerApplicationContext context; GetMapping(/port) public String port() { return port context.getWebServer().getPort(); } }这条链路对应源码里Connector端口为0时NioEndpoint的bind()会调用serverSocket.bind(null)由操作系统分配可用端口。4.2 优雅停机从配置到源码实现优雅停机是server.shutdowngraceful配置项背后的完整机制。开启后Spring Boot在TomcatWebServer里启用GracefulShutdown类。它的处理逻辑是在停止时不再接受新请求同时等待已进入处理流程的请求完成。想用好优雅停机单靠默认配置还不够建议结合超时时间一起设置server.shutdowngraceful spring.lifecycle.timeout-per-shutdown-phase30s实际效果是应用收到停机信号后Spring会先停止接收新请求给正在处理的接口最多30秒的收尾时间超时后强制结束。这个收尾顺序在源码里是通过SmartLifecycle的stop(Runnable callback)方法实现的它保证在容器销毁前先关闭WebServer。有一点必须提醒优雅停机只对Spring Boot管理范围内的请求生效像Tomcat内部的一些后台任务、自己new出来的线程池不会自动纳入等待范围。所以长耗时任务要么用TaskDecorator传递上下文要么确保线程用的是Spring管理的ThreadPoolTaskExecutor。4.3 压缩、SSL与内置定制器的实战配置响应压缩能有效减小传输体积配置很简单server.compression.enabledtrue server.compression.mime-typesapplication/json,application/xml,text/html,text/plain server.compression.min-response-size1024min-response-size是触发压缩的最小响应字节数默认是2KB实际使用时建议压到1KB接口响应经常在几KB左右波动设得太大等于没配。它的源码实现是Tomcat的CompressionConfig在请求进入管道后判断响应头决定是否启用gzip压缩。SSL配置则涉及更多细节最基础的方式是配置key-storeserver.port8443 server.ssl.enabledtrue server.ssl.key-storeclasspath:keystore.p12 server.ssl.key-store-passwordchangeit server.ssl.key-store-typePKCS12更灵活的做法是自定义TomcatConnectorCustomizer在Connector创建后直接操作SSLHostConfig。这种方式适合需要多个证书、需要配置clientAuth等高级场景。Bean public TomcatConnectorCustomizer sslCustomizer() { return connector - { Http11NioProtocol protocol (Http11NioProtocol) connector.getProtocolHandler(); protocol.setSSLEnabled(true); protocol.setKeystoreFile(/path/to/keystore.p12); protocol.setKeystorePass(changeit); protocol.setSslProtocol(TLS); }; }这种做法的好处是逻辑集中便于从配置中心动态拉取也有一个很明显的坑如果走的是Http11NioProtocol路径强转类型时一定要先确认当前协议否则会抛ClassCastException。更稳妥的做法是判断协议实例类型再执行转换。5. 常见问题与排查实录源码分析的价值最终要体现在排查问题的效率上。这一节记录几个真实场景里遇到过的问题每个问题最后都能沿着源码链路找到根因。5.1 端口被占用根因不在端口本身报错信息通常是Port already in use: 8080。表面上是端口被占但源码链路告诉我们这个异常在Tomcat的Connector.bind()方法里抛出属于LifecycleException的子类型。排查步骤可参考如下顺序先用lsof -i :8080或netstat -ano | grep 8080确认占用进程。确认是否是自己项目重复启动比如IDE的热部署残留了旧进程。排除系统服务占用比如其他开发工具内置的WebServer。最后才是考虑换端口。如果确实需要动态避开冲突可以设置server.port0让操作系统分配端口。这个做法在微服务多实例本地联调时特别好用配合注册中心上报端口完全不用手动维护端口配置。补充一个冷门但有效的排查技巧如果本地是IPv6环境有时候端口看起来被占用其实是java.net.BindException: Cannot assign requested address这通常不是端口冲突而是IP绑定问题检查server.address是否设置了不存在的本机地址。5.2 自定义AccessLog不生效想在嵌入式Tomcat里记录访问日志最直接的方式是配置server.tomcat.accesslog.enabledtrue server.tomcat.accesslog.directory/var/log/tomcat server.tomcat.accesslog.pattern%h %l %u %t %r %s %b %D但很多人配置完发现日志没生成。源码里有答案AccessLog的开关由AbstractAccessLogValve控制它必须在Host启动前被添加。Spring Boot通过StandardHostValve初始化当accesslog.enabledtrue时在Engine配置阶段就把AccessLogValve加到Host上。常见失效原因有两个一是directory路径不存在或没写权限二是pattern格式字符串写错导致AccessLogValve初始化失败被静默跳过。建议先改到项目目录下试跑通再改绝对路径server.tomcat.accesslog.directory./logs server.tomcat.accesslog.pattern%h %l %u [%t] %r %s %b %D5.3 测试环境反复创建和销毁Tomcat的影响单元测试或集成测试里经常会遇到每跑一个测试类就启动一次Tomcat整体测试时间被拖得很长。源码之所以慢是因为每次getWebServer都会创建临时目录、初始化Tomcat实例、绑定端口直到stop时才释放。同样重要的是如果在测试中使用了随机端口一个测试类结束后端口并不会立刻释放处于TIME_WAIT状态。高并发跑完整套测试后大量端口堆积在TIME_WAIT会拖慢后续测试用例的启动速度。业内通用的做法是把需要容器环境的测试收敛到少数测试类或者直接用SpringBootTest(webEnvironment RANDOM_PORT)配合DirtiesContext(classMode AFTER_CLASS)来平衡。还有一个冷门知识点Spring Boot对于webEnvironment MOCK的测试是不会启动真实Tomcat的。如果你的测试只是验证Controller业务逻辑不涉及真正的Socket交互完全没必要起容器。5.4 如何快速定位当前源码版本看源码最怕版本对不上。Spring Boot版本迭代很快类的位置、方法名、参数顺序都可能变化。推荐两个动作第一在IDE里直接打开ServletWebServerFactoryAutoConfiguration跳到TomcatServletWebServerFactory用Find Usages查找getWebServer的调用链顺着就能看到当前版本的真实逻辑。第二确认依赖树里到底引入了哪个Tomcat版本。Spring Boot的spring-boot-starter-tomcat会传递引入一个固定版本的tomcat-embed-core。用mvn dependency:tree或者Gradle的dependencies命令把Tomcat版本看清楚再对照源码时就不会被网上的旧文章带偏。mvn dependency:tree -Dincludesorg.apache.tomcat.embed输出结果里会显示类似org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.x这个版本号就是你要对照的源码版本。6. 自查清单与一些体会嵌入式Tomcat的源码初看是一堆类之间的互相调用但抽离出来其实就三件事怎么创建、怎么启动、怎么定制。把这个主线条理清楚之后再去看Jetty和Undertow的实现会发现它们走的是完全相同的模板套路只是API细节不同。我在实际阅读这个源码版本时最受益的一个习惯是不要从SpringApplication.run()开始逐行跟而是先找到WebServer实例的创建点从最终产物反推上游依赖。这样看书看代码的效率高很多尤其是面对类层次比较深、调用链比较长的情况。最后再分享一个我踩过的坑自定义TomcatServletWebServerFactory的时候如果直接new一个实例并作为Bean返回自动配置的ServerProperties不会自动注入到你的工厂里必须自己手动把ServerProperties绑定进去。不然会出现配置了server.port却不生效的诡异问题。正确做法是保留自动配置只通过WebServerFactoryCustomizer做增量定制这也正是Spring Boot官方推荐的扩展方式。保持自动配置的完整性能省下非常多排查配置生效的时间。

相关新闻

DQN最简可理解实现:120行PyTorch代码解剖CartPole

DQN最简可理解实现:120行PyTorch代码解剖CartPole

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 4:40:23 阅读更多 →
RAG工程实战:从面试六连问看真实落地的五层优化体系

RAG工程实战:从面试六连问看真实落地的五层优化体系

1. 面试官为什么突然爱问RAG——不是考概念,是验真活“RAG面试6连问”这个标题在技术社区刷屏那天,我正帮某高校实验室调试一个跨平台文档问答系统。学生把检索结果直接拼进提示词丢给大模型,结果PDF里一页表格被拆成七段乱码,模型…

2026/10/16 4:40:23 阅读更多 →
AI产品经理的核心能力:人机协作直觉与Agent设计断层

AI产品经理的核心能力:人机协作直觉与Agent设计断层

1. 为什么“会调 API”不等于“懂 Agent 产品”我带过三支不同背景的团队落地 Agent 项目:一支是纯算法出身、能手写 LLM 微调 pipeline 的博士小组;一支是资深 ToB SaaS 产品经理,做过五个以上千万级客户交付系统;还有一支是刚毕…

2026/10/16 4:40:23 阅读更多 →

最新新闻

咸阳工商招标信息抓取实战:从数据源分析到合规爬虫方案

咸阳工商招标信息抓取实战:从数据源分析到合规爬虫方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:30:36 阅读更多 →
kolla-ansible构建可调试OpenStack开发环境实战指南

kolla-ansible构建可调试OpenStack开发环境实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:30:36 阅读更多 →
MP2642GR-Z与ATmega328协同实现2S锂电精准主动均衡

MP2642GR-Z与ATmega328协同实现2S锂电精准主动均衡

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:30:36 阅读更多 →
生成对抗网络GAN入门:从原理到MNIST实战

生成对抗网络GAN入门:从原理到MNIST实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:30:36 阅读更多 →
MathCyclus:构建高中数学循环学习题库的核心设计

MathCyclus:构建高中数学循环学习题库的核心设计

1. 项目概述与设计起点1.1 这个项目到底要解决什么力了不少高中数学题库类产品,说实话,大多数都是把题目堆在一起,按章节分个类就算完事。学生刷题刷得挺爽,但过两周回头再看,同一个知识点换件马甲又不会了。这根本不是…

2026/10/16 5:30:36 阅读更多 →
Python入门必破关:PTA判题机制与输出输入规范解析

Python入门必破关:PTA判题机制与输出输入规范解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:29:36 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →