简介:本资源是一套基于SSM(SpringMVC+Spring+MyBatis)框架开发的学生社团活动管理系统,面向高校计算机类专业本科生毕业设计与课程设计场景,切实解决社团管理信息化需求。系统采用JSP前端页面、jQuery与Ajax实现动态交互,后端整合三大核心框架,支持管理员、学生、社团负责人及普通用户四类角色,涵盖社团申请与审核、活动发布与报名、新闻资讯管理、在线留言等完整业务闭环。压缩包共750个文件,含125个JSP页面、78个Java业务类、80个Jar依赖库、57个PNG/GIF图标资源、55个JS脚本及30个XML配置文件,整体体积33MB,结构清晰、模块分明,便于理解MVC分层与前后端协作逻辑。目前已有80人学习下载,提供可直接运行的完整工程、规范的目录组织、适配主流Tomcat环境的部署配置,是掌握Java Web企业级开发流程的典型实践案例。
1. 项目概述与核心价值
最近在整理过去的项目资料,翻到了一个挺有代表性的老项目——基于SSM框架的学生社团活动管理系统。虽然现在Spring Boot大行其道,但SSM(Spring + Spring MVC + MyBatis)这套经典组合拳,依然是理解Java Web开发从“青铜”到“王者”演进过程的最佳标本。这个项目麻雀虽小,五脏俱全,它完整地串联起了从需求分析、技术选型、框架整合到业务实现的整个链条。对于刚学完Java基础,想踏入企业级开发门槛的朋友来说,亲手从零搭建一个SSM项目,远比只看理论或使用现成的Spring Boot Starter来得深刻。这个系统核心解决的是高校社团活动管理的数字化痛点:活动发布靠海报、报名统计靠Excel、经费审批靠跑腿。我们将用一套Web系统,把这些流程全部线上化、规范化。接下来,我会带你深入这个项目的肌理,不仅告诉你代码怎么写,更会拆解每个技术决策背后的“为什么”,以及那些只有踩过坑才知道的实操细节。
2. 技术栈深度解析与选型考量
2.1 为什么是SSM?一个经典架构的再审视
在微服务和云原生概念满天飞的今天,回头选择SSM似乎有些“复古”。但这恰恰是其教学和入门价值所在。SSM代表了一个清晰的分层架构时代,每一层职责分明,是理解MVC模式和持久层框架的绝佳载体。
Spring: 作为项目的“大管家”和“粘合剂”,它的核心是IoC(控制反转)容器。在这个社团管理系统中,所有Controller、Service、Dao(Mapper)对象,以及数据源、事务管理器等,都由Spring容器统一创建和管理。比如,ActivityService需要依赖ActivityMapper来操作数据库,在Spring中,我们不需要在Service里new ActivityMapperImpl(),而是通过@Autowired注解,由容器将装配好的Mapper实例“注入”进来。这种模式的好处是解耦。假设后期我们发现MyBatis不合适,想换成JPA,那么只需要替换Dao层的实现和配置,Service层的代码几乎不用动,因为依赖关系是通过接口注入的,而非硬编码。Spring的AOP(面向切面编程)则为我们处理横切关注点提供了优雅方案,比如全局的事务管理。我们只需在Service方法上添加@Transactional注解,Spring就能在方法开始时开启事务,方法结束时根据是否抛出异常来决定提交或回滚。这避免了在每个数据库操作前后手动编写事务代码的繁琐和易错。
Spring MVC: 负责处理HTTP请求和响应的“调度中心”。它是MVC模式中的“C”(控制器)。当用户在浏览器点击“发布活动”按钮,一个HTTP请求就会发到服务器。Spring MVC的DispatcherServlet(前端控制器)会拦截这个请求,然后根据配置的@RequestMapping注解,找到对应的ActivityController中的publishActivity方法。这个方法处理请求参数(如表单数据),调用ActivityService的业务逻辑,最后返回一个逻辑视图名(如”success”)或一个ModelAndView对象。DispatcherServlet再根据视图解析器的配置,找到对应的JSP页面进行渲染,最终生成HTML返回给浏览器。整个过程清晰地将请求处理、业务调用和视图渲染分离。
MyBatis: 数据持久层的“实干家”,负责与数据库对话。它避免了JDBC中大量的样板代码(如手动创建Connection、Statement、处理ResultSet)。MyBatis的核心思想是将SQL语句从Java代码中彻底分离,写入XML映射文件。例如,我们有一个Activity实体类,对应数据库的t_activity表。在ActivityMapper.xml文件中,我们会这样定义一个插入语句:
<insert id="insertActivity" parameterType="com.example.entity.Activity"> INSERT INTO t_activity (name, organizer_id, start_time, location, description) VALUES (#{name}, #{organizer.id}, #{startTime}, #{location}, #{description}) </insert>在Java的ActivityMapper接口中,只需要声明对应的方法:int insertActivity(Activity activity);。MyBatis通过动态代理,在运行时为我们生成这个接口的实现。当调用mapper.insertActivity(activity)时,MyBatis会找到对应的SQL,并将activity对象的属性值安全地设置到#{}占位符上。这里就引出一个经典面试题:#{}和${}的区别。#{}是预编译处理,MyBatis会将其替换为?,然后使用PreparedStatement的set方法赋值,能有效防止SQL注入。而${}是字符串替换,直接将参数值拼接到SQL语句中,存在安全风险,通常只用于动态指定表名、列名等非用户输入的场景。在这个社团系统里,所有涉及用户输入(如活动名称、描述)的地方,必须使用#{}。
整合的价值:Spring IoC容器管理了Spring MVC的Controller和MyBatis的Mapper,Spring MVC处理Web请求并调用Spring管理的Service,Service再调用MyBatis的Mapper操作数据库。事务由Spring统一管理。这个闭环,构成了一个稳固、可维护的企业级应用基础。
2.2 关键依赖与版本锁定策略
一个稳定的项目始于依赖管理。使用Maven时,pom.xml文件的依赖配置是重中之重。除了引入spring-webmvc,spring-jdbc,mybatis,mybatis-spring这些核心jar包,需要特别注意版本兼容性。例如,Spring 5.x 和 MyBatis 3.5.x 可以很好协作,但和旧版的MyBatis-Spring整合包可能有问题。
实操心得:强烈建议使用
<dependencyManagement>配合BOM(材料清单)或父POM来统一管理所有依赖的版本。比如继承spring-boot-dependencies(即使你不用Spring Boot)或者自定义一个版本属性集合。这能彻底避免不同子模块或传递依赖引入版本冲突,这是构建可维护项目的第一步,很多新手会忽略这一点,直到出现ClassNotFoundException或NoSuchMethodError时才头疼。
对于数据库驱动(如mysql-connector-java)、连接池(如HikariCP,它是目前性能最好的JDBC连接池之一,Spring Boot默认集成)、日志框架(如slf4j配合logback)等,都需要明确指定版本。一个清晰的依赖树,是项目健康的标志。
3. 系统核心模块设计与实现拆解
3.1 数据库设计与领域模型映射
任何系统的基石都是数据库。对于社团活动管理系统,核心实体包括:
- 用户(
sys_user):区分学生、社团负责人、系统管理员。字段包括id、学号/工号、姓名、密码(加密存储)、角色、所属社团ID等。 - 社团(
club):id、名称、简介、成立时间、负责人ID(关联用户)。 - 活动(
activity):id、标题、内容、发布社团ID、活动时间、地点、状态(待审核、已发布、已结束)、报名人数上限等。 - 活动报名(
activity_application):id、活动ID、学生ID、报名时间、状态(已报名、已取消)等。 - 公告(
notice):id、标题、内容、发布者、发布时间等。
设计时要遵循范式以减少冗余,但也要兼顾查询效率,必要时做反范式设计。比如,在activity表中可以冗余一个current_application_count(当前报名数)字段,避免每次查询报名人数都要联表COUNT。
MyBatis映射的精髓:实体类与数据库表的映射,不仅仅是字段名对应。更重要的是处理对象关系。比如,一个Activity对象中可能包含一个Club对象(发布社团)和一个User对象(发布者)。在MyBatis的XML映射文件中,可以使用<association>和<collection>来处理这种“一对一”和“一对多”关系。
<resultMap id="ActivityDetailMap" type="Activity"> <id property="id" column="id"/> <result property="title" column="title"/> <!-- 一对一关联社团 --> <association property="club" javaType="Club"> <id property="id" column="club_id"/> <result property="name" column="club_name"/> </association> <!-- 一对多关联报名列表(如果需要) --> <collection property="applications" ofType="ActivityApplication"> <id property="id" column="app_id"/> <result property="applyTime" column="apply_time"/> <association property="student" javaType="User">...</association> </collection> </resultMap>这样,通过一次复杂的SQL联表查询,MyBatis就能自动帮我们组装成一个结构完整的Activity对象,业务层(Service)可以直接使用,这就是ORM(对象关系映射)带来的便利。
3.2 控制层(Controller)设计与请求处理流程
Controller是系统的门面,负责接收和响应HTTP请求。设计时要遵循RESTful风格,让API更清晰。例如:
GET /api/activities:获取活动列表(可分页、过滤)GET /api/activities/{id}:获取活动详情POST /api/activities:创建新活动(需要权限校验)PUT /api/activities/{id}:更新活动信息DELETE /api/activities/{id}:删除活动
在Spring MVC中,一个典型的Controller如下:
@RestController // 等同于@Controller + @ResponseBody,直接返回JSON @RequestMapping("/api/activities") public class ActivityController { @Autowired private ActivityService activityService; @GetMapping public PageResult<ActivityVO> listActivities( @RequestParam(required = false) String keyword, @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { // 构造查询条件,调用Service return activityService.getActivityPage(keyword, pageNum, pageSize); } @PostMapping @PreAuthorize("hasRole('CLUB_ADMIN')") // 使用Spring Security进行方法级权限控制 public Result publishActivity(@Valid @RequestBody ActivityDTO activityDTO) { // @Valid 触发JSR-303 Bean Validation,如@NotBlank校验 activityService.publish(activityDTO); return Result.success("发布成功"); } }这里有几个关键点:
- 参数绑定:
@RequestParam绑定查询参数,@RequestBody绑定JSON请求体,@PathVariable绑定URL路径参数。 - 数据校验:在DTO(Data Transfer Object)对象的属性上使用
@NotBlank、@Size、@Future等注解,并在Controller参数前加@Valid,Spring MVC会自动校验,校验失败会抛出MethodArgumentNotValidException,可以通过全局异常处理器统一处理,返回友好的错误信息。 - 全局异常处理:使用
@ControllerAdvice或@RestControllerAdvice定义一个全局异常处理类,集中处理业务异常、校验异常、数据库异常等,避免将堆栈信息直接暴露给前端,并统一返回格式。 - 拦截器(Interceptor)的应用:这是Spring MVC的强大特性。我们可以实现
HandlerInterceptor接口,创建登录拦截器、权限拦截器、日志拦截器等。例如,一个简单的登录拦截器会检查Session中是否存在用户信息,如果没有,则重定向到登录页面。在Spring配置文件中(或通过Java Config)将其添加到拦截路径中。
3.3 业务逻辑层(Service)与事务管理
Service层是业务逻辑的核心,它协调多个Mapper(Dao)的操作,形成一个完整的业务单元。事务管理是这一层的重中之重。
假设“学生报名活动”这个业务:1. 检查活动状态和名额;2. 在报名表中插入一条记录;3. 更新活动的当前报名人数。这三个步骤必须在一个事务里,要么全部成功,要么全部失败。
@Service public class ActivityServiceImpl implements ActivityService { @Autowired private ActivityMapper activityMapper; @Autowired private ApplicationMapper applicationMapper; @Transactional(rollbackFor = Exception.class) // 声明式事务 @Override public void applyActivity(Long activityId, Long studentId) { // 1. 查询活动并校验 Activity activity = activityMapper.selectByIdForUpdate(activityId); // 使用SELECT ... FOR UPDATE加行锁,防止超卖 if (activity.getStatus() != ActivityStatus.PUBLISHED) { throw new BusinessException("活动不在报名阶段"); } if (activity.getCurrentCount() >= activity.getMaxCount()) { throw new BusinessException("名额已满"); } // 2. 插入报名记录 ActivityApplication app = new ActivityApplication(activityId, studentId); applicationMapper.insert(app); // 3. 更新活动人数 activityMapper.incrementCurrentCount(activityId); } }@Transactional注解使得这个方法被一个事务包围。rollbackFor = Exception.class表示遇到任何异常都回滚。注意,为了在高并发下防止“超卖”(名额被重复占用),我们在查询活动时使用了SELECT ... FOR UPDATE(悲观锁),这会锁定这条记录,直到当前事务提交。这是处理此类并发问题的经典方案之一。另一种方案是使用乐观锁(通过版本号字段),根据业务场景选择。
注意事项:Spring的事务管理默认只对运行时异常(
RuntimeException)和错误(Error)进行回滚,受检异常(Exception)不会触发回滚。因此,通常建议设置rollbackFor = Exception.class。另外,@Transactional注解要生效,必须是通过Spring代理对象调用的方法。在同一个类中,一个非事务方法A调用同一个类中的事务方法B,B的事务是不会生效的,因为这是内部调用,绕过了代理。这是常见的坑点。
3.4 数据访问层(Mapper)与动态SQL实战
MyBatis的XML映射文件是发挥其威力的地方。除了基本的CRUD,动态SQL是处理复杂查询条件的利器。
场景:在活动列表页面,我们需要根据多个可选条件(活动名称关键词、社团ID、活动状态、时间范围)来筛选活动。
<select id="selectActivityByCondition" resultMap="ActivityDetailMap"> SELECT a.*, c.name as club_name FROM t_activity a LEFT JOIN t_club c ON a.club_id = c.id <where> <if test="keyword != null and keyword != ''"> AND (a.title LIKE CONCAT('%', #{keyword}, '%') OR a.description LIKE CONCAT('%', #{keyword}, '%')) </if> <if test="clubId != null"> AND a.club_id = #{clubId} </if> <if test="status != null"> AND a.status = #{status} </if> <if test="startTimeBegin != null"> AND a.start_time >= #{startTimeBegin} </if> <if test="startTimeEnd != null"> AND a.start_time <= #{startTimeEnd} <!-- 注意小于号的XML转义 --> </if> </where> ORDER BY a.create_time DESC </select><where>标签会智能地处理AND前缀,如果它包含的标签有内容,就在SQL前插入WHERE,并且会自动去掉第一个多出来的AND或OR。<if>标签根据条件判断是否拼接SQL片段。这比在Java代码里拼接字符串要安全、清晰得多。
分页查询:在旧版MyBatis中,分页通常借助插件,比如著名的PageHelper。它在你的查询方法前调用PageHelper.startPage(pageNum, pageSize),后续的第一个MyBatis查询就会自动进行物理分页(生成带有LIMIT的SQL)。在我们的Service中,可以这样封装一个通用的分页结果:
public PageResult<ActivityVO> getActivityPage(ActivityQuery query, Integer pageNum, Integer pageSize) { PageHelper.startPage(pageNum, pageSize); List<Activity> activities = activityMapper.selectByCondition(query); PageInfo<Activity> pageInfo = new PageInfo<>(activities); // 将Activity实体列表转换为ActivityVO视图对象列表 List<ActivityVO> voList = convertToVOList(activities); return new PageResult<>(pageInfo.getTotal(), voList); }PageInfo对象包含了总记录数、总页数、当前页等丰富信息。
4. 项目整合、配置与部署实战
4.1 SSM框架整合配置文件详解
SSM整合的核心在于配置文件,它们定义了Spring容器如何组装各个部件。主要涉及三个文件:
web.xml:Web应用的部署描述符。在这里,我们配置:ContextLoaderListener:负责启动Spring的根应用上下文(Root WebApplicationContext),主要加载Service、Dao、数据源、事务等业务层和持久层Bean。DispatcherServlet:Spring MVC的前端控制器,配置其加载的Spring MVC配置文件(如spring-mvc.xml),并指定URL映射(通常是/)。- 字符编码过滤器:如
CharacterEncodingFilter,解决POST请求中文乱码问题。 - Session超时时间等。
applicationContext.xml(Spring根配置):- 组件扫描:
<context:component-scan base-package="com.example">,让Spring自动扫描并注册@Component,@Service,@Repository注解的Bean。 - 数据源配置:配置
DataSource,通常使用连接池(如HikariCP)。 - 事务管理:配置
DataSourceTransactionManager,并开启注解驱动事务<tx:annotation-driven />。 - MyBatis整合:配置
SqlSessionFactoryBean,指定数据源和MyBatis配置文件路径。配置MapperScannerConfigurer,自动扫描Mapper接口并注册为Spring Bean。
- 组件扫描:
spring-mvc.xml(Spring MVC配置):- MVC注解驱动:
<mvc:annotation-driven />,启用@Controller、@RequestMapping等注解。 - 静态资源处理:
<mvc:resources mapping="/static/**" location="/static/" />,防止静态资源(CSS, JS, 图片)被DispatcherServlet拦截。 - 视图解析器:配置
InternalResourceViewResolver,定义JSP文件的前缀(/WEB-INF/views/)和后缀(.jsp)。 - 拦截器配置:定义
<mvc:interceptors>。 - JSON消息转换器:如果使用
@RestController返回JSON,需要配置MappingJackson2HttpMessageConverter(<mvc:annotation-driven>默认会配置)。
- MVC注解驱动:
4.2 前后端交互与数据格式规范
虽然这是一个传统的SSM项目,后端主要渲染JSP,但如今前后端分离是主流。即使在本项目中,对于部分异步操作(如活动报名、点赞),也常使用Ajax与后端交互。因此,定义统一的数据返回格式至关重要。
public class Result<T> { private Integer code; // 状态码,如200成功,400客户端错误,500服务器错误 private String msg; // 提示信息 private T data; // 响应数据 public static <T> Result<T> success(T data) { return new Result<>(200, "操作成功", data); } public static Result<?> error(String msg) { return new Result<>(500, msg, null); } // ... 构造方法、getter/setter }Controller的方法返回Result对象,Spring MVC的消息转换器会将其序列化为JSON。前端根据code判断成功与否,用data渲染,用msg提示用户。
日期时间处理:这是前后端交互的另一个常见痛点。Java中的Date或LocalDateTime在序列化为JSON时,默认格式可能不符合前端要求。可以在application.properties中全局配置,或在实体类的字段上使用@JsonFormat注解:
public class Activity { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime startTime; }4.3 项目部署与基础运维考量
开发完成后,需要将项目打包部署到Tomcat等Servlet容器中。
- 打包:使用Maven的
package命令,生成WAR包(项目名.war)。 - 部署:将WAR包放入Tomcat的
webapps目录,启动Tomcat,它会自动解压并部署应用。 - 数据库初始化:首次部署需要执行数据库脚本,创建表结构和初始化数据(如管理员账号)。可以考虑使用Flyway或Liquibase这样的数据库版本管理工具,将SQL脚本纳入版本控制。
- 日志配置:在生产环境,必须配置好日志。将
logback-spring.xml文件放在类路径下,配置日志级别(生产环境通常用INFO或WARN)、输出格式,以及最重要的——日志滚动策略。避免日志文件无限增长占满磁盘。<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/app.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <maxHistory>30</maxHistory> <!-- 保留30天 --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> <!-- 单个文件最大100MB --> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder>...</encoder> </appender> - 连接池监控:配置HikariCP的连接池参数(最大连接数、最小空闲数、连接超时时间等)并监控。连接泄露是Web应用常见的性能杀手。
5. 开发中的常见“坑”与调试技巧
5.1 MyBatis映射与N+1查询问题
问题描述:在查询活动列表时,如果使用<collection>关联查询了报名学生列表,当活动数量很多时,可能会产生“N+1查询”问题。即先查询出N个活动,然后对每个活动再执行一次查询去获取它的报名列表,导致数据库查询次数暴增。
解决方案:
- 使用嵌套结果映射(如上文示例):通过一个复杂的SQL联表查询,一次性将所有数据(活动及其报名列表)查询出来。这是最高效的方式,但SQL可能较复杂。
- 开启MyBatis的延迟加载(Lazy Loading):在映射配置中设置
fetchType=”lazy”。这样,只有在代码中真正访问activity.getApplications()时,MyBatis才会执行第二次查询去加载报名列表。这要求Session(在SSM中,通常对应一次Service方法调用,因为Spring默认将事务与Session绑定)在访问关联属性时仍然打开。需要确保在视图层(如JSP)或序列化为JSON前完成所有数据的加载,否则会报LazyInitializationException。 - 在Service层进行数据组装:先查询出活动列表,再根据活动ID列表批量查询出所有相关的报名数据,最后在Java内存中进行组装。这需要编写额外的代码,但可控性最强。
调试技巧:务必开启MyBatis的SQL日志打印。在
mybatis-config.xml中设置<setting name="logImpl" value="STDOUT_LOGGING" />,或在logback.xml中将org.apache.ibatis和com.example.mapper包的日志级别设为DEBUG。这样,所有执行的SQL语句和参数都会打印在控制台,是排查SQL问题和性能瓶颈的利器。
5.2 Spring事务失效的几种典型场景
- 方法非public:
@Transactional注解只能用于public方法上。 - 自调用问题:同一个类中,方法A(无事务)调用方法B(有
@Transactional),B的事务不生效。因为事务管理是通过AOP代理实现的,自调用绕过了代理。 - 异常被捕获:在事务方法中,异常被
try-catch捕获且没有重新抛出,事务管理器感知不到异常,不会回滚。 - 默认回滚异常类型:抛出的异常不是
RuntimeException或Error,且未在@Transactional中通过rollbackFor指定。 - 数据库引擎不支持事务:如MySQL的MyISAM引擎。务必使用InnoDB引擎。
5.3 中文乱码问题一站式解决
乱码是Web开发的老朋友,在SSM中主要检查以下几点:
- 数据库连接:在JDBC URL后加上参数:
jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8。 - Tomcat服务器:在
server.xml的Connector配置中,添加URIEncoding="UTF-8"。 - Spring MVC字符编码过滤器:确保
web.xml中配置了CharacterEncodingFilter,并设置了encoding和forceEncoding为UTF-8。 - JSP页面:在页面顶部添加
<%@ page contentType="text/html;charset=UTF-8" language="java" %>。 - 响应编码:在
spring-mvc.xml中配置StringHttpMessageConverter的默认编码为UTF-8。
5.4 分页插件PageHelper的“坑”
PageHelper.startPage(pageNum, pageSize)必须紧跟在需要分页的Mapper查询方法之前。并且,它只对其后第一个MyBatis查询语句生效。如果在一个Service方法中执行了多个查询,只有第一个会被分页。此外,分页参数是存储在ThreadLocal中的,如果使用了异步线程,分页参数会失效或错乱。
6. 从SSM到Spring Boot的平滑演进思考
完成这个SSM项目后,你可能会想,现在都是Spring Boot的天下了,学SSM还有用吗?我的体会是,SSM是“手动挡”,Spring Boot是“自动挡”。精通手动挡,能让你更深刻地理解汽车的原理(即Spring生态的核心思想),以后开自动挡(用Spring Boot)时,遇到问题你才知道引擎盖下面发生了什么。
Spring Boot的本质是“约定大于配置”,它通过Starter依赖和自动配置,将SSM中那些繁琐的XML配置全部内化。例如:
- 内嵌了Tomcat,无需单独部署WAR。
- 自动配置数据源、MyBatis、事务管理器。
- 提供了统一的
application.properties/yml进行配置管理。 - 强大的Actuator提供监控端点。
如果你要将这个社团系统升级到Spring Boot,主要工作就是:
- 将项目改为Spring Boot的目录结构。
- 用
spring-boot-starter-web,spring-boot-starter-data-jdbc(或mybatis-spring-boot-starter)等替换原有的SSM依赖。 - 将
applicationContext.xml和spring-mvc.xml中的配置,转化为application.yml中的属性配置或Java Config配置类。 - 原有的业务代码(Controller, Service, Mapper)几乎可以无缝迁移。
这个过程本身就是一个极佳的学习项目,它能让你彻底打通从传统SSM到现代Spring Boot应用的理解。你会发现,之前你在SSM中手动配置的每一个Bean,Spring Boot都为你提供了默认的、最优的自动配置方案,而你需要做的,只是在有特殊需求时去覆盖它。这,就是技术进步带来的效率提升。
本文还有配套的精品资源,点击获取