尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
SpringMVC请求链路全拆解:从DispatcherServlet到视图渲染
简介这是一份面向 Java Web 初学者的 SpringMVC 入门项目通过一个经典的 Hello World 示例完整演示了 SpringMVC 框架从请求接收到视图响应的核心流程。项目采用 MVC 分层架构涵盖前端控制器 DispatcherServlet、处理器映射器 HandlerMapping、处理器适配器 HandlerAdapter、模型视图对象 ModelAndView、视图解析器 ViewResolver 等核心组件清晰展现了控制器如何解析用户请求、调用业务逻辑并选择合适的视图进行渲染。压缩包共包含 22 个文件整体大小只有 11KB主要文件类型有 Spring 与 web 相关的 XML 配置、JSP 视图页面、Java 控制器源码及编译后的 class 文件同时包括 Eclipse 工程配置、Maven 配置文件等辅助内容目录结构完整能够直接导入集成开发环境运行调试。目前已有 269 人浏览学习这一资源。通过该示例项目读者可以亲手完成 web.xml 中 DispatcherServlet 的配置、Controller 注解控制器的编写、RequestMapping 请求路径的映射、返回 ModelAndView 或视图名以及 JSP 页面渲染等关键步骤并在此基础上进一步掌握 Spring 核心的依赖注入、面向切面编程、异常处理及 RESTful 接口扩展是适合课程设计、毕业设计或自学入门的轻量级参考素材。1. SpringMVC项目拆解一个请求从进入到返回的完整链路做了多年 Java Web 开发SpringMVC 大概是最熟悉的陌生人。很多人会用 Controller、RequestMapping 写接口但问到 DispatcherServlet 怎么把请求路由到方法、参数绑定失败发生在哪一环、为什么静态资源会被拦截就沉默了。这份 SpringMVC 项目源码恰好是一个典型的 SSM 结构单体应用包含了从前端页面到数据库访问的完整闭环。把它拆完一遍你会发现 SpringMVC 的核心不是注解怎么写而是那套请求流转机制。这篇解析适合想跑通项目、或者打算在 SpringMVC 上做二次开发的读者我会把架构分层、核心源码、运行步骤和改造技巧逐个讲清楚。2. 项目架构与技术选型从依赖到目录结构的完整视图2.1 技术栈全景SpringMVC 5 MyBatis MySQL打开项目的 pom.xml依赖关系并不复杂但每一条都值得说清楚。Spring MVC 用的是 5.x 系列基于 Java 8 构建Servlet 容器要求 Tomcat 8.5 以上。ORM 层用了 MyBatis 而不是 JPA这在传统 SSM 项目里是主流选择因为 SQL 可控性强适合业务逻辑复杂的系统。dependencies dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.2.9.RELEASE/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.6/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.21/version /dependency /dependencies这里有个容易忽略的点spring-webmvc 依赖并不是只包含 MVC 本身它会把 spring-context、spring-core 等基础模块一起带进来所以不需要单独声明 spring-context。如果项目里用的不是 Maven 而是 Gradle同样只需要引入一个 webmvc 依赖即可。MyBatis-Spring 这个桥接依赖比较关键它负责把 MyBatis 的 SqlSessionFactory 交给 Spring 容器管理。版本上有一个血泪经验MyBatis-Spring 2.x 对应 MyBatis 3.5如果项目里 MyBatis 版本低于 3.4直接升到 2.x 会报 ClassNotFoundException因为内部引用了一些新版本才有的类。2.2 目录结构包设计与分层职责源码的包结构遵循标准的 SSM 三层架构从 controller 到 service 到 mapper每一层只做自己的事。controller 层只接收参数和返回视图service 层处理业务逻辑mapper 层负责数据库交互。src/main/java ├── com.example.demo │ ├── controller │ │ ├── UserController.java │ │ └── OrderController.java │ ├── service │ │ ├── UserService.java │ │ └── impl │ │ └── UserServiceImpl.java │ ├── dao │ │ ├── UserDao.java │ │ └── UserMapper.xml │ └── entity │ └── User.java src/main/resources ├── spring │ ├── spring-mvc.xml │ ├── spring-mybatis.xml │ └── spring-service.xml ├── mybatis-config.xml └── jdbc.properties src/main/webapp ├── WEB-INF │ ├── web.xml │ └── views │ ├── user/list.jsp │ └── user/edit.jsp └── static ├── css └── js注意 dao 层和 mapper 层在这里是同一个东西接口叫 UserDao对应的 XML 映射文件也叫 UserMapper.xml。这是 MyBatis 项目的常见命名习惯接口的名和 XML 的 namespace 必须完全一致否则启动时就会报异常。webapp 目录下区分了 WEB-INF/views 和 static 两个资源目录意味着视图页面放在受保护目录下浏览器不能直接访问由 Controller 转发进入而 css、js 这类静态资源放在 static 里需要 SpringMVC 配置放行规则。这个设计在第五章避坑里会重点讲很多项目跑不通都是因为这里没配置好。2.3 配置文件DispatcherServlet 与容器双轨制SpringMVC 项目的配置是典型的双容器结构。web.xml 里配置了一个 DispatcherServlet它加载 spring-mvc.xml 作为子容器而 spring-mybatis.xml 和 spring-service.xml 由 ContextLoaderListener 加载为父容器。子容器能看到父容器的 Bean父容器看不到子容器的 Bean这个规则决定了哪些 Bean 可以注入到 Controller 里。web-app context-param param-namecontextConfigLocation/param-name param-value classpath:spring/spring-mybatis.xml classpath:spring/spring-service.xml /param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping /web-appload-on-startup 设为 1表示应用启动时就初始化 DispatcherServlet而不是等第一个请求进来才初始化。contextConfigLocation 指向 spring-mvc.xml这个文件里配置了组件扫描、视图解析器、注解驱动等。这个双容器结构有一个经典陷阱如果 Controller 里的 Service 由父容器管理而子容器扫描 Controller 时又把 Service 接口也扫进来了就会产生两个实例导致 Autowired 注入时出现冲突。常见做法是 spring-mvc.xml 只扫描 controller 包spring-service.xml 扫描 service 包职责分开避免重复扫描。3. 核心流程拆解一个请求从进入到返回的完整链路3.1 DispatcherServlet请求分发的中枢SpringMVC 的请求处理入口不是某个 Controller而是 DispatcherServlet。它本质上是一个 Servlet继承了 HttpServlet 并重写了 doGet、doPost 方法。当浏览器发送一个请求到 TomcatTomcat 根据 url-pattern 匹配到 DispatcherServlet然后把请求交给它处理。DispatcherServlet 的核心逻辑在 doDispatch 方法里虽然 SpringMVC 5.x 的源码几百行但核心步骤其实就四步调用 getHandler 获取处理器执行链、调用 getHandlerAdapter 获取处理器适配器、调用适配器的 handle 方法执行 Controller 方法、处理返回结果。protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest request; HandlerExecutionChain mappedHandler null; boolean multipartRequestParsed false; // 检查是否文件上传请求 if (checkMultipart(request)) { processedRequest checkMultipart(request); multipartRequestParsed true; } // 第一步通过 HandlerMapping 拿到执行链 mappedHandler getHandler(processedRequest); if (mappedHandler null) { noHandlerFound(processedRequest, response); return; } // 第二步获取对应的 HandlerAdapter HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler()); // 第三步执行拦截器前置方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 第四步真正调用 Controller 方法 mv ha.handle(processedRequest, response, mappedHandler.getHandler()); // 第五步执行拦截器后置方法 mappedHandler.applyPostHandle(processedRequest, response, mv); // 第六步处理视图渲染 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }这段源码是理解整个 SpringMVC 的关键。getHandler 方法返回的不是一个普通的 Controller 对象而是一个 HandlerExecutionChain它包装了 Controller 方法以及一组拦截器。拦截器的执行顺序是 preHandle 按正序执行postHandle 按逆序执行afterCompletion 也按逆序执行这个顺序写错会导致事务或日志记录出问题。HandlerAdapter 的作用是适配不同类型的处理器。RequestMappingHandlerAdapter 专门处理基于注解的 Controller 方法而 HttpRequestHandlerAdapter 处理实现了 HttpRequestHandler 接口的处理器。SpringMVC 用适配器模式避免了 DispatcherServlet 直接依赖具体的 Controller 类型。3.2 HandlerMapping 与 Controller路由匹配与请求绑定RequestMappingHandlerMapping 是默认的 HandlerMapping 实现它会在应用启动时扫描所有带有 RequestMapping 注解的方法建立 URL 到 HandlerMethod 的映射表。这个映射表以 PathPattern 或 AntPath 模式存储支持{id}路径变量、*通配符等。Controller RequestMapping(/user) public class UserController { Autowired private UserService userService; GetMapping(/list) public String list(Model model) { ListUser users userService.findAll(); model.addAttribute(users, users); return user/list; } GetMapping(/detail/{id}) public String detail(PathVariable(id) Integer id, Model model) { User user userService.findById(id); model.addAttribute(user, user); return user/detail; } }GetMapping 是 RequestMapping(method RequestMethod.GET) 的组合注解语义更清晰。这里的返回值 user/list 是逻辑视图名不是真实的 JSP 路径最终由视图解析器拼接前缀后缀得到实际路径。Model 对象用于携带数据到视图层它的实现类是 ModelAndViewContainer 内部持有的 ExtendedModelMap。PathVariable 绑定 URL 模板变量这里的 {id} 和 PathVariable(id) 必须对应上。如果不写注解里的 valueSpringMVC 会尝试用参数名去匹配但参数名在编译时可能被改变所以显式写上更保险。3.3 视图解析器与响应渲染ModelAndView 的传递路径Controller 方法执行完返回字符串后DispatcherServlet 会构造 ModelAndView 对象然后交给视图解析器处理。项目里配置的是 InternalResourceViewResolver它是最常用的 JSP 视图解析器。bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean当 Controller 返回 user/list 时InternalResourceViewResolver 会拼接出 /WEB-INF/views/user/list.jsp 这个内部资源路径然后通过 RequestDispatcher 的 forward 方法把请求转发到 JSP。这就是为什么 JSP 页面放在 WEB-INF 下是安全的因为浏览器不能直接访问 WEB-INF 目录必须经过 Controller 转发才能看到页面。视图渲染完成后DispatcherServlet 会调用拦截器的 afterCompletion 方法。这个方法的调用时机是在视图渲染完成之后、响应提交之前。常用的场景是记录接口耗时、清理请求级别的资源、统一异常处理。这里有一个值得注意的设计细节ModelAndView 里的 Model 数据是通过 request.setAttribute 放到 request 作用域的JSP 里可以直接用${user.name}这种 EL 表达式访问。如果同一份数据在多个请求间共享应该放到 session 作用域项目里的做法是给 session 单独做一个工具类来管理而不是直接在 Controller 里操作 HttpSession。4. 数据交互实战参数绑定、校验与 JSON 序列化4.1 参数绑定从 RequestParam 到 RequestBodySpringMVC 的参数绑定是一个经常让人翻车的环节。 GET 请求的查询参数、POST 请求的表单字段甚至 JSON 请求体SpringMVC 都有不同的绑定策略。项目里同时存在页面表单和 JSON 接口两种风格正好可以对比例子。PostMapping(/save) public String save(RequestParam(name) String name, RequestParam(value age, required false) Integer age) { userService.save(name, age); return redirect:/user/list; } PostMapping(/api/save) ResponseBody public Result saveUser(RequestBody User user) { int id userService.saveWithId(user); return Result.success(id); }RequestParam 绑定的是 request.getParameter 获取到的值适用于表单提交和 URL 查询参数。required 默认为 true如果请求里没有该参数会直接抛出 MissingServletRequestParameterException。这里 age 参数被设置为可选可以避免前端不传年龄导致 400 错误。RequestBody 绑定的是整个请求体需要 HttpMessageConverter 把 JSON 字符串转换成 Java 对象Jackson 是默认实现。需要注意的是 RequestBody 和 RequestParam 不能混用在同一接口里一个方法要么用表单参数要么用 JSON 体混用时 GET 请求没有请求体会导致参数绑定全部失败。4.2 数据校验JSR-303 注解与服务端验证项目里用 Hibernate Validator 作为 JSR-303 规范的实现在实体类的字段上标注校验注解。服务端校验是最后一道防线前端校验只能提升用户体验不能作为安全边界。实际项目里经常看到只做了前端校验就上线的情况换一个接口工具直接绕过前端脏数据就进来了。public class User { NotBlank(message 用户名不能为空) Size(min 2, max 20, message 用户名长度必须在2到20之间) private String name; NotNull(message 年龄不能为空) Min(value 1, message 年龄最小为1) Max(value 120, message 年龄最大为120) private Integer age; Pattern(regexp ^1[3-9]\\d{9}$, message 手机号格式不正确) private String phone; }校验注解标注好之后Controller 里需要在参数前加上 Valid 注解SpringMVC 会在参数绑定完成后立刻执行校验。如果校验失败会抛出 MethodArgumentNotValidException默认返回 400 状态码。项目里配置了全局异常处理器来统一返回错误消息格式类似{code: 400, message: 用户名不能为空}。Pattern 的正则做手机号校验时要注意转义问题。\\d在 Java 字符串里表示正则表达式中的\d如果写成\d会直接报 PatternSyntaxException。另外1[3-9]并没有覆盖所有合法号段但作为项目里的基础校验已经够用了。4.3 JSON 序列化ResponseBody 与消息转换器Controller 方法返回 Result 对象并加上 ResponseBody 注解SpringMVC 会通过 HttpMessageConverter 把对象序列化为 JSON 字符串。项目里配置了 MappingJackson2HttpMessageConverter它是默认的消息转换器使用 Jackson 2.x 完成序列化和反序列化。{code: 200, message: success, data: {id: 1, name: 张三}}Jackson 序列化时有一些常见问题。Date 类型的字段默认会被序列化成时间戳项目里通过 JsonFormat 注解统一处理成指定格式。另外一个坑是 null 值字段默认会输出field: null如果接口对数据体积有要求可以在类上配置 JsonInclude(Include.NON_NULL) 忽略 null 字段。public class Result { private int code; private String message; private Object data; JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8) private Date createTime; public static Result success(Object data) { Result r new Result(); r.code 200; r.message success; r.data data; return r; } }Result 类封装了统一的响应结构这是前后端分离项目中比较标准的做法。前端拿到响应后只需要判断 code 是否为 200不需要根据业务接口定制逻辑。改造项目时这个类基本不用动直接复用就可以。5. 避坑指南SpringMVC 开发中常见的坑与排查方案5.1 静态资源被拦截配置 default-servlet-handler现象启动项目后访问 /css/style.css 返回 404但文件确实放在 static 目录下。原因web.xml 里 DispatcherServlet 的 url-pattern 配置为/这意味着所有请求都会进入 SpringMVC包括 css、js、图片等静态资源。DispatcherServlet 没有对应的 HandlerMapping 可以处理静态资源请求就会直接返回 404。解决在 spring-mvc.xml 中增加资源映射配置。mvc:resources mapping/static/** location/static//配置后访问 /static/css/style.css 就能直接命中文件。这是最简单的方案。如果项目里用 Maven 的 resource 过滤还要检查 target 目录下是否真的有静态文件被复制进去有时候源文件在但构建后丢失了会让人误判成配置问题。5.2 POST 请求中文乱码CharacterEncodingFilter 配置顺序现象POST 请求提交的中文参数在 Controller 里取出来是乱码但 GET 请求正常。原因Tomcat 8.5 版本后 GET 请求的 URI 编码默认是 UTF-8而 POST 请求的请求体编码取决于 request.setCharacterEncoding 是否被调用。如果应用没有设计编码过滤器POST 表单数据会以 ISO-8859-1 解析中文必然乱码。解决filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mappingforceEncoding 设为 true 时不仅 request 会被设置编码response 也会被统一设置为 UTF-8。这个过滤器必须配置在 web.xml 所有过滤器的第一位如果它被其他过滤器挡在后面编码设置就失效了。5.3 日期格式参数绑定失败DateTimeFormat 与类型转换器现象前端传参birthday1995-08-10Controller 里用 Date 类型接收报 Failed to convert property type of java.lang.String to java.util.Date。原因SpringMVC 默认只支持把字符串转换成基本类型和包装类型Date 类型没有默认的转换器。前端传来的字符串 1995-08-10 无法被自动解析成 Date 对象。解决在字段或参数上标注 DateTimeFormat。PostMapping(/search) public String search(RequestParam(birthday) DateTimeFormat(pattern yyyy-MM-dd) Date birthday) { // 业务处理 }这里容易踩的坑是 DateTimeFormat 只对 SpringMVC 的参数绑定生效MyBatis 写入数据库时如果 JDBC 驱动和数据库时区不一致还会出现日期偏移 8 小时的问题。排查时需要分清楚是参数绑定失败还是数据库存储偏移两者的解决方案完全不一样。还有一种幸运的情况是前端传的已经是时间戳字符串用 Long 类型接收再 new Date()永远不会报错。5.4 Autowired 注入为 null包扫描范围冲突现象Controller 里 Autowired 的 Service 启动时没有报错但第一次调用就抛 NullPointerException。原因Spring 容器中有两个 Service 实例。一个是 spring-mybatis.xml 里使用 Service 注解扫描生成的另一个是 spring-mvc.xml 里组件扫描时把 service 包也包含进来生成的。Controller 注入的是其中一个容器里的实例而该实例的属性没有完成注入。排查方法是在启动日志里搜索 Service 类名出现两次 Bean 创建日志基本可以确认是包扫描重复。解决方案是 spring-mvc.xml 的 component-scan 明确限制为 controller 包service 和 dao 的事务管理全部交给父容器。6. 二次开发技巧改造这个 SpringMVC 项目6.1 一步一步加一个 RESTful 接口在前面拆完整个请求链路之后实际的改造过程会清晰很多。以下是一个具体的操作流程每一步都可以对照源码验证。RestController RequestMapping(/api/user) public class UserApiController { Autowired private UserService userService; GetMapping(/{id}) public Result getUser(PathVariable Integer id) { User user userService.findById(id); return Result.success(user); } }步骤一新建 UserApiController 类使用 RestController 而不是 Controller这样方法上不用再加 ResponseBody。步骤二确保该类放在 controller 包下被 spring-mvc.xml 的组件扫描覆盖到。步骤三检查 service 层是否已有 findById 方法没有的话需要在 UserService 接口和实现类中补上。步骤四重启应用后直接用接口工具测试GET /api/user/1。改造时建议先看 service 层有没有现成方法可以直接复用很多情况下 Controller 只是薄薄一层核心代码都在 service 里。如果 service 方法缺失只需要在接口中加方法签名、在实现类中加业务逻辑不要试图在 Controller 里直接操作 dao 层那样会破坏分层结构。6.2 替换视图层从 JSP 切换到前后端分离如果不想继续用 JSP 作为视图层可以保留 Controller 的请求映射不变去掉 InternalResourceViewResolver 配置把 Controller 返回值改成 Result 对象。这样需要把原有的返回字符串方法改成返回 Result前端用框架接收 JSON 数据。这个改造要注意一个细节原来返回 redirect:/user/list 的请求改造后变成 JSON 响应前端跳转逻辑要放到 JS 里处理比如 window.location.href /user/list。否则会出现接口返回 JSON 但页面不跳转的情况看起来像是接口挂了其实只是响应格式变了。6.3 一个值得养成的排查习惯项目里最隐蔽的问题往往出现在配置层而不是代码层。我拆这个项目最大的收获就是建立一套固定的排查路线先从 url-pattern 确认请求有没有进入 DispatcherServlet再从 mapper 映射文件确认 SQL 有没有执行最后才看业务逻辑。每次接口返回的结果和预期不符我都强制走一遍这个顺序。这套方法比对着代码一行行看有效得多因为大部分问题都出在配置和资源文件上而不是 Java 逻辑。从那以后我每次接手新项目都会先读一遍 web.xml 和 spring-mvc.xml 再去看代码。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

理解策略单调提升:TRPO与PPO背后的信任域与KL散度

理解策略单调提升:TRPO与PPO背后的信任域与KL散度

2021年2月12日,我把强化学习笔记翻到第四十三篇,正好卡在Monotonic Improvement Theory,也就是策略的单调提升理论。说实话,策略优化这一块最容易让人懵的不是某个算法有多复杂,而是“为什么TRPO要限定KL散度”“为什么…

📅 2026/10/17 19:05:17
IFRS S1合规实践指南:从ESG叙事到可持续财务披露的准则落地

IFRS S1合规实践指南:从ESG叙事到可持续财务披露的准则落地

简介:针对国际可持续准则理事会(ISSB)2023年6月发布的IFRS S1,安永推出了系统性深度解读,面向企业财务报告编制者、ESG合规管理人员、审计与咨询从业者,旨在帮助读者理解和应对可持续相关财务信息披露新规的…

📅 2026/10/17 19:05:17
互金合规指引拆解:从监管语言到技术落地

互金合规指引拆解:从监管语言到技术落地

简介:这是一份面向互联网金融行业从业者、合规管理人员及金融科技系统开发者的专业指引文档。内容聚焦互联网金融业务在监管框架下的合规要点,覆盖业务运营、风险控制、信息安全等关键方向,适合用于搭建合规知识体系或作为实际业务的对照参考…

📅 2026/10/17 19:05:17
MORE NEWS

更多资讯

📰

DeepSeek-R1:面向中文逻辑推演的可验证推理引擎

简介:本资源是北京大学DeepSeek系列研讨讲座的配套讲义《DeepSeek与AIGC应用》,面向AI初学者、教育科研人员及AIGC工具实践者,旨在破除仅停留在操作层面的浅层认知,系统厘清DeepSeek-R1推理模型的技术原理、能力边界与真实应用场景…

📰

TSM6.3+Mhvtl搭建虚拟带库实现Oracle备份与异机恢复

简介:面向企业级备份/灾备工程师,这份原创文档系统演示了在 CentOS 6.5 环境中,如何用 TSM 6.3 结合 Mhvtl 开源虚拟带库完成备份恢复测试。全文围绕四部分展开:Mhvtl 的编译安装与带库可用性验证、TSM 主服务器端配置&#xff08…

📰

SC850SL 图像传感器调试实战:从上电时序到 MIPI 出图

简介:SC850SL数据手册面向安防监控、网络摄像机与车载摄像头领域的硬件工程师、驱动开发者及图像调试人员,帮助其快速掌握这款800万像素CMOS传感器的寄存器配置、时序控制与系统集成方法。资源包共1个文件,为PDF格式的数据手册,压…

📰

MATLAB实现量子近似优化算法:组合优化与最大割问题详解

简介:面向需要求解旅行商、背包、图着色等组合优化问题的技术人员与科研人员,这份PDF文档讲解如何基于MATLAB实现量子近似优化算法(QAOA)。资源共1个文件,文件类型为PDF,资源包约4.01MB,文档支持…

📰

音视频开发入门:从采集到播放的完整链路与实操指南

音视频这个领域,刚接触的时候很容易被一堆名词砸晕:编码、封装、协议、推流、拉流、转码、解码、渲染,每个词单拎出来都能写一本书。但真正上手做项目之后你会发现,日常打交道的东西其实就那么几样,把核心链路跑通一遍…

📰

【AI 工作流】用 TaoToken 统一 Key 打通 Gemini、Claude Code 与 Codex 的配置实践

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬