资讯动态

IDEA2021可运行SSM源码:JavaWeb配置链路与避坑

发布时间:2026/10/26 2:20:19 来源:尧图企业网站定制
简介面向JavaWeb初学者与需要从零搭建SSM项目的开发者这份IDEA2021实战源码围绕Spring、SpringMVC、MyBatis整合展开覆盖控制层、业务层、持久层及实体类等典型分层配套Tomcat 9.0与MySQL 8.0.25环境可用于课程练习、毕业设计原型或框架入门对照。压缩包共105个文件约22.34MB以jar依赖、xml配置、class编译文件为主另有java源码、jsp页面与properties配置能较完整呈现一个可运行Web工程的目录结构与依赖组成。该资源已有3137人学习下载说明其在SSM入门场景中具备一定参考价值。读者可据此理解各层之间的调用关系、配置文件组织方式与前后端资源放置位置并借助已有编译产物和依赖快速还原项目减少从零配置环境时的试错成本适合作为框架整合练习的对照样本。1. 从一份能跑的 IDEA2021 SSM 源码说起老 JavaWeb 项目到底卡在哪2026 年再打开 IDEA2021 去从零开始搭建 SSM 框架很多人第一反应是“这玩意不是被 Spring Boot 淘汰了吗”。但真到接手老 JavaWeb 系统、改课程设计、补一个能讲清链路的面试项目一套能跑通的 ssm 源码比十篇原理文都管用。这份资源给的是从零开始的工程骨架Maven 目录、web.xml、Spring 容器、SpringMVC 调度、MyBatis 映射、JSP 视图一条链路全在。它解决的不是“学不学 SSM”而是环境、依赖、配置、发布四个环节对不上导致的启动失败。比如 IDEA2021 里 Artifact 没挂上、Tomcat10 的 jakarta 包和 Spring5 的 javax 包打架、Mapper XML 没进 target/classes这些坑每个都能让新手卡一下午。适合要维护老项目、准备 javaweb 课设、想把 idea 里红色报错一次性清掉的人。下面按我实际搭一遍的顺序拆先对版本再建工程然后拆配置链路最后把踩坑点摆出来。2. 环境与工程骨架JDK8、Maven、Tomcat9 三件套怎么对齐2.1 版本组合与选型理由SSM 不是单个框架Spring、SpringMVC、MyBatis 三者对 Servlet 容器和 JDK 版本都有要求。IDEA2021 本身对 JDK8 支持最稳Spring5 又还在 javax.servlet 体系里所以这套骨架最省事的组合是 JDK8 Maven3.6/3.8 Tomcat9 Spring5 MyBatis3.5。别一上来追 JDK17 和 Tomcat10Tomcat10 默认走 jakarta.servletSpring5 的 DispatcherServlet 还是 javax.servlet启动时直接给你 ClassNotFoundException。组件建议版本作用注意点JDK8编译和运行IDEA2021 的 Maven 编译目标锁 1.8Maven3.6.x / 3.8.x依赖与构建别用太新的 4.x插件兼容性不稳Tomcat9.xServlet 容器支持 Servlet4包名仍是 javax.servletSpring5.3.xIoC、MVC、事务与 MyBatis 3.5 搭配成熟MyBatis3.5.xORM 映射配 mybatis-spring 2.0.xMySQL5.7 / 8.0数据库8.0 驱动类名带 cj这套组合不是唯一解但它是“照着抄能少改配置”的解。尤其 IDEA2021 的 Web Facet 和 Artifact 界面跟新版本有差异用 Tomcat9 能避开 Jakarta 命名空间迁移那一层玄学。2.2 用 Maven webapp 骨架建工程并补齐目录我一般不用 IDEA 的 New Project 一路点因为 webapp 骨架生成后目录经常缺 java 和 resources后面还要手动补。命令行生成更干净参数也看得见。mvn archetype:generate \ -DgroupIdcom.example.ssm \ -DartifactIdssm-demo \ -DarchetypeArtifactIdmaven-archetype-webapp \ -DinteractiveModefalse cd ssm-demo mkdir -p src/main/java/com/example/ssm/controller mkdir -p src/main/java/com/example/ssm/service mkdir -p src/main/java/com/example/ssm/mapper mkdir -p src/main/java/com/example/ssm/entity mkdir -p src/main/resources/mapper mkdir -p src/test/java/com/example/ssm mkdir -p src/main/webapp/WEB-INF/views/user这段命令做两件事先用maven-archetype-webapp生成标准 war 骨架再补齐 SSM 常用的包结构。groupId和artifactId按自己的项目改后面所有 XML 里的包名、Mapper 的 namespace 都要跟它对齐。src/main/resources/mapper专门放 MyBatis 的 XMLWEB-INF/views放 JSP视图解析器后面会指到这里。-DinteractiveModefalse表示不交互适合脚本化复现。生成后先在 IDEA2021 里打开 pom.xml让 Maven 把依赖拉完。如果控制台卡在 downloading先看 Maven 的 settings.xml 有没有配镜像别急着怀疑 IDEA。2.3 pom.xml依赖、作用域和编译插件pom 是 SSM 最容易埋雷的地方。依赖少了启动报 ClassNotFound依赖多了版本冲突servlet-api 不设 provided 还会跟 Tomcat 自带的包打架。下面这组版本是我本地长期用的能避开大部分冲突。project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.example.ssm/groupId artifactIdssm-demo/artifactId version1.0-SNAPSHOT/version packagingwar/packaging properties spring.version5.3.30/spring.version mybatis.version3.5.13/mybatis.version mybatis.spring.version2.0.7/mybatis.spring.version project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- Spring 核心与 Web MVC -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-tx/artifactId version${spring.version}/version /dependency !-- MyBatis 与 Spring 整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version${mybatis.spring.version}/version /dependency !-- 数据源与驱动 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.20/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- Servlet/JSP 由 Tomcat 提供scope 必须是 provided -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- JSON 与日志 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.13.4.2/version /dependency dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-core/artifactId version2.17.2/version /dependency dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-slf4j-impl/artifactId version2.17.2/version /dependency !-- 测试 -- dependency groupIdjunit/groupId artifactIdjunit/artifactId version4.13.2/version scopetest/scope /dependency dependency groupIdorg.springframework/groupId artifactIdspring-test/artifactId version${spring.version}/version scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source1.8/source target1.8/target encodingUTF-8/encoding /configuration /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-war-plugin/artifactId version3.3.2/version configuration failOnMissingWebXmlfalse/failOnMissingWebXml /configuration /plugin /plugins /build /project这段 pom 的逻辑是按“编译期、容器提供、测试期”三层分依赖。spring-webmvc带进 MVC 的 DispatcherServletspring-jdbc和spring-tx负责数据源与事务mybatis-spring是两者的胶水。javax.servlet-api和jsp-api的scope写成provided意思是编译时用、运行时由 Tomcat 提供写成默认的 compile 会把 servlet-api 打进 war和容器自带的类重复启动就可能报NoSuchMethodError。参数上maven-compiler-plugin的source和target都锁 1.8避免 IDEA2021 默认用高版本 JDK 编译出 Tomcat9 跑不了的 class。maven-war-plugin的failOnMissingWebXml设 false 是为了后面如果改用注解配置不写 web.xml 也能打包但这份骨架仍然保留 web.xml传统 SSM 的启动入口放在这里更直观。2.4 IDEA2021 的 Facet 与 Artifact 配置IDEA2021 里 SSM 跑不起来一半问题出在 Artifact。代码编译过了Tomcat 也起了访问 404很多时候不是 Controller 写错是部署包里没有 classes。打开 File - Project Structure按下面这张表对一遍。配置位置值作用Modules - Sourcessrc/main/java 标 Sourcessrc/main/resources 标 Resourcessrc/test/java 标 Tests让 IDEA 认对源码根和资源根Facets - WebDeployment Descriptors 指向 src/main/webapp/WEB-INF/web.xml让 IDEA 识别这是 Web 模块Facets - WebWeb Resource Directory 指向 src/main/webapp静态资源和 JSP 的根目录ArtifactsWeb Application: Exploded - From Modules生成可部署的 exploded warRun Configuration - Deployment加入上面的 exploded artifactApplication context 设 /ssm决定访问 URL 的前缀Artifact 的 Output Layout 里要能看到WEB-INF/classes和WEB-INF/lib。如果只有WEB-INF没有 classes说明模块的编译输出没挂进去启动后 Spring 的 XML 都加载不到。Application context 设成/ssm后Controller 上的/user/list实际访问地址是http://localhost:8080/ssm/user/list别漏掉前缀。注意Tomcat10 默认是 jakarta.servlet和 Spring5 的 javax.servlet 不兼容搭这套骨架直接用 Tomcat9 更省事。3. 配置链路拆解web.xml 到 SpringMVC 的启动顺序与参数3.1 web.xml 里两个容器ContextLoaderListener 与 DispatcherServletSSM 启动时其实起了两个 Spring 容器。ContextLoaderListener读applicationContext.xml创建父容器放 Service、Mapper、数据源、事务DispatcherServlet读spring-mvc.xml创建子容器放 Controller、视图解析器、静态资源映射。子容器能拿到父容器的 Bean父容器拿不到子容器的所以 Service 不能写在 MVC 配置里。?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 display-namessm-demo/display-name !-- 父容器Service、Mapper、数据源、事务 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- 编码过滤器要放在最前面 -- 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-mapping !-- 子容器Controller、视图解析、静态资源 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath: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 welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list /web-app这段 web.xml 的关键在三个参数。contextConfigLocation在context-param里是给父容器用的在servlet的init-param里是给子容器用的两个名字一样但作用域不同写串了就会有一个容器加载错文件。load-on-startup设 1 表示 Tomcat 启动时就初始化 DispatcherServlet而不是等第一个请求进来调试时能更早看到配置错误。url-pattern用/而不是/*/只接管没匹配到其他 Servlet 的请求/*会把 JSP 也拦下来视图直接 404。编码过滤器的forceEncodingtrue表示请求和响应都强制 UTF-8只设encoding有时只影响请求。过滤器映射的url-pattern用/*覆盖所有请求。3.2 applicationContext.xml数据源、SqlSessionFactory、事务父容器要干三件事连数据库、把 MyBatis 的 SqlSessionFactory 建出来、给 Service 方法加事务。数据源参数我一般放到jdbc.propertiesXML 里只留占位符换库时不用改 Spring 配置。?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:txhttp://www.springframework.org/schema/tx xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd !-- 加载 jdbc.properties -- context:property-placeholder locationclasspath:jdbc.properties/ bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ property nameinitialSize value2/ property nameminIdle value2/ property namemaxActive value10/ property namevalidationQuery valueSELECT 1/ property nametestWhileIdle valuetrue/ /bean !-- MyBatis 的 SqlSessionFactory -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ property nametypeAliasesPackage valuecom.example.ssm.entity/ property nameconfiguration bean classorg.apache.ibatis.session.Configuration property namemapUnderscoreToCamelCase valuetrue/ /bean /property /bean !-- 扫描 Mapper 接口生成代理 Bean -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.example.ssm.mapper/ property namesqlSessionFactoryBeanName valuesqlSessionFactory/ /bean !-- 事务管理器 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/ /beans这段配置的逻辑是“数据源 - SqlSessionFactory - Mapper 代理 - 事务”四步。mapperLocations写classpath:mapper/*.xml对应src/main/resources/mapper下的 XML打包后会在WEB-INF/classes/mapper里。typeAliasesPackage让 XML 里的resultType可以直接写User不用写全限定名。mapUnderscoreToCamelCase把数据库的created_at自动映射到 Java 的createdAt省掉一堆 resultMap。MapperScannerConfigurer的sqlSessionFactoryBeanName用字符串而不是ref是为了避免 Mapper 扫描过早触发 SqlSessionFactory 初始化导致数据源配置还没加载完就报错。事务管理器的dataSource必须和 SqlSessionFactory 用的是同一个否则事务不生效。tx:annotation-driven开启Transactional注解Service 方法上才起作用。jdbc.properties里 MySQL8 的 URL 要带时区和编码jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/ssm_demo?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.passwordrootcom.mysql.cj.jdbc.Driver是 MySQL8 的驱动类5.7 用com.mysql.jdbc.Driver。serverTimezone不配在部分环境会报时区错误useSSLfalse是本地开发省掉证书握手生产环境按实际改。3.3 spring-mvc.xml注解驱动、视图解析器、静态资源子容器只扫 Controller不扫 Service这是父子容器隔离的关键。如果component-scan写成com.example.ssm全扫Service 会被子容器再创建一次事务代理可能失效表现就是插入失败不回滚。?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:mvchttp://www.springframework.org/schema/mvc xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd !-- 只扫 ControllerService 留给父容器 -- context:component-scan base-packagecom.example.ssm.controller/ !-- 注册 HandlerMapping 和 HandlerAdapter -- mvc:annotation-driven/ !-- 静态资源放行 -- mvc:resources mapping/static/** location/static// mvc:default-servlet-handler/ !-- 视图解析器/WEB-INF/views/user/list.jsp -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean /beansmvc:annotation-driven会注册RequestMappingHandlerMapping和RequestMappingHandlerAdapter没有它RequestMapping不生效。mvc:resources把/static/**映射到 webapp 下的/static/目录CSS、JS、图片不走 Controller。mvc:default-servlet-handler是兜底把没被 Controller 匹配的请求交回 Tomcat 的默认 Servlet防止静态资源 404。视图解析器的prefix和suffix拼起来就是 JSP 路径。Controller 返回user/list实际找/WEB-INF/views/user/list.jsp。放在 WEB-INF 下是为了不让浏览器直接访问 JSP必须走 Controller。3.4 父子容器的分工与常见误配配置项放在哪个容器原因Service、Mapper、数据源、事务applicationContext.xml父容器负责业务和数据层Controller、视图解析器、静态资源spring-mvc.xml子容器负责 Web 层CharacterEncodingFilterweb.xml容器级过滤器早于 Spring 容器Mapper XMLresources/mapper打包进 classes由 mapperLocations 扫描常见误配是“两个容器都扫全包”。表面能启动实际 Service 被创建两次事务注解加在子容器的 Service 上父容器的 Mapper 事务管理器管不到回滚就变成玄学。另一个误配是把applicationContext.xml配到 DispatcherServlet 的contextConfigLocation里Controller 能扫到但父容器没起来Service 注入全是 null。4. 数据层闭环Mapper、Service、Controller 最小可运行链路4.1 建表与实体先让数据库和 Java 字段对得上先建一张最小的用户表字段不用多够验证增查就行。created_at用下划线命名后面靠mapUnderscoreToCamelCase映射到createdAt。CREATE DATABASE IF NOT EXISTS ssm_demo DEFAULT CHARACTER SET utf8mb4; USE ssm_demo; CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(64) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); INSERT INTO t_user(username, password) VALUES (admin, 123456);实体类按表字段写属性名用驼峰getter/setter 不要省。IDEA2021 可以 AltInsert 生成别用 Lombok 省事老项目里 Lombok 版本和编译插件经常打架。package com.example.ssm.entity; import java.util.Date; public class User { private Long id; private String username; private String password; private Date createdAt; public Long getId() { return id; } public void setId(Long id) { this.id id; } public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public String getPassword() { return password; } public void setPassword(String password) { this.password password; } public Date getCreatedAt() { return createdAt; } public void setCreatedAt(Date createdAt) { this.createdAt createdAt; } }字段类型上id用Long对应BIGINTcreatedAt用java.util.Date对应DATETIME。如果数据库是TIMESTAMP也可以换java.sql.Timestamp但Date在 JSP 里格式化更常见。4.2 Mapper 接口与 XMLnamespace 和方法名必须一一对应MyBatis 的绑定规则很死XML 的namespace等于 Mapper 接口的全限定名SQL 标签的id等于接口方法名parameterType和resultType对得上。错一个就是Invalid bound statement (not found)。package com.example.ssm.mapper; import com.example.ssm.entity.User; import java.util.List; public interface UserMapper { User selectById(Long id); ListUser selectAll(); int insert(User user); }?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.ssm.mapper.UserMapper select idselectById parameterTypelong resultTypeUser SELECT id, username, password, created_at FROM t_user WHERE id #{id} /select select idselectAll resultTypeUser SELECT id, username, password, created_at FROM t_user ORDER BY id DESC /select insert idinsert parameterTypeUser useGeneratedKeystrue keyPropertyid INSERT INTO t_user(username, password, created_at) VALUES (#{username}, #{password}, NOW()) /insert /mappernamespace写全限定名IDEA 里按住 Ctrl 能点进接口说明绑对了。resultTypeUser能用别名是因为typeAliasesPackage扫了 entity 包。#{id}是预编译占位符别写成${id}后者是字符串拼接有注入风险。useGeneratedKeystrue和keyPropertyid让插入后自增主键回填到实体后面取user.getId()能拿到。XML 放在src/main/resources/mapper/UserMapper.xml打包后进target/classes/mapper/。如果放在src/main/java下Maven 默认不拷 XML需要在 pom 的 build/resources 里额外配新手容易漏。4.3 Service 与事务边界注解加在实现类上Service 接口定义业务方法实现类加Service和Transactional。事务注解最好加在实现类的方法上别加在接口上Spring 的代理模式对接口注解的支持取决于配置。package com.example.ssm.service; import com.example.ssm.entity.User; import java.util.List; public interface UserService { User getById(Long id); ListUser listAll(); void create(User user); }package com.example.ssm.service.impl; import com.example.ssm.entity.User; import com.example.ssm.mapper.UserMapper; import com.example.ssm.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; Service public class UserServiceImpl implements UserService { Autowired private UserMapper userMapper; Override Transactional(readOnly true) public User getById(Long id) { return userMapper.selectById(id); } Override Transactional(readOnly true) public ListUser listAll() { return userMapper.selectAll(); } Override Transactional(rollbackFor Exception.class) public void create(User user) { userMapper.insert(user); } }Service让父容器扫描到实现类Autowired注入 Mapper 代理。readOnly true告诉数据库这是只读事务MySQL 会做一点优化也防止误写。rollbackFor Exception.class让受检异常也回滚默认只回滚 RuntimeException。事务生效的前提是applicationContext.xml里有tx:annotation-driven并且 Service 在父容器里。4.4 Controller 与 JSP把查询结果送到页面Controller 只负责收参数、调 Service、返回视图名。类上RequestMapping(/user)方法上再写子路径最终 URL 是 context path 类路径 方法路径。package com.example.ssm.controller; import com.example.ssm.entity.User; import com.example.ssm.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Cont p a hrefhttps://download.csdn.net/download/a2267542848/19730797 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑