一、课程初体验与认知转变
刚开始接触《软件工程实务》这门课程时,我对软件工程的理解还停留在"写代码"的层面。随着课程的深入,我逐渐认识到软件工程是一个系统化的过程,而编码只是其中的一个环节。
在SpringBoot项目实践中,我深刻体会到前期需求分析的重要性。记得第一个小组项目中,我们直接跳过了详细的需求分析阶段,结果在开发过程中不断遇到需求变更,导致代码结构频繁调整。这让我明白了为什么老师强调"磨刀不误砍柴工"的道理。
通过课程学习,我对软件生命周期有了清晰认识。从需求分析、系统设计、编码实现到测试部署,每个阶段都有其不可替代的价值。特别是在SpringBoot框架下开发时,良好的前期设计能显著减少后期的重构工作。
二、SpringBoot实战中的工程化思维
2.1 分层架构的实践体会
在课程项目中,我们采用标准的SpringBoot分层架构:
// 典型的Controller层代码
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
// 其他CRUD操作
}
// Service层实现
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public UserDTO getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
return convertToDTO(user);
}
// 其他业务方法
}
这种清晰的分层使代码维护变得容易,也便于团队协作。我学会了接口隔离原则的实际应用,以及如何通过依赖注入实现松耦合。
2.2 自动化测试的重要性
课程特别强调测试驱动开发(TDD),在SpringBoot环境中我们实践了各种测试:
// 单元测试示例
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserServiceImpl userService;
@Test
void getUserById_shouldReturnUser_whenUserExists() {
// 准备测试数据
User mockUser = new User(1L, "test", "test@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(mockUser));
// 执行测试
UserDTO result = userService.getUserById(1L);
// 验证结果
assertNotNull(result);
assertEquals("test", result.getUsername());
verify(userRepository).findById(1L);
}
}
// 集成测试示例
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void getUser_shouldReturn200_whenUserExists() throws Exception {
UserDTO mockDto = new UserDTO(1L, "test", "test@example.com");
when(userService.getUserById(1L)).thenReturn(mockDto);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("test"));
}
}
通过编写测试代码,我养成了先思考边界条件和异常场景的习惯,这显著提高了代码质量。
三、团队协作与版本控制
3.1 Git协作流程规范化
课程中我们使用Git进行团队协作,老师制定了严格的Git Flow:
feature/ - 功能开发分支
release/ - 发布准备分支
hotfix/ - 紧急修复分支
develop - 集成开发分支
main - 稳定主分支
# 规范的提交消息格式
git commit -m "feat(user): add user registration endpoint
- implement POST /api/users endpoint
- add input validation
- add related unit tests"
通过实际项目,我深刻体会到好的提交习惯对团队协作的重要性。清晰的提交信息能让其他成员快速理解变更内容。
3.2 代码审查的价值
我们建立了代码审查机制,每个PR(Pull Request)需要至少两名成员审查后才能合并。这个过程让我学到了很多:
-
如何编写易读的代码
-
如何给出建设性的代码评审意见
-
如何优雅地接受别人的批评和建议
例如,在一次评审中,有同学指出我的REST API设计不符合HATEOAS原则,这促使我去学习Spring HATEOAS:
// 改进后的Controller返回带链接的资源
@GetMapping("/{id}")
public EntityModel<UserDTO> getUserById(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
return EntityModel.of(user,
linkTo(methodOn(UserController.class).getUserById(id)).withSelfRel(),
linkTo(methodOn(UserController.class).getAllUsers()).withRel("users"));
}
通过实际项目,我深刻体会到好的提交习惯对团队协作的重要性。清晰的提交信息能让其他成员快速理解变更内容。
3.2 代码审查的价值
我们建立了代码审查机制,每个PR(Pull Request)需要至少两名成员审查后才能合并。这个过程让我学到了很多:
-
如何编写易读的代码
-
如何给出建设性的代码评审意见
-
如何优雅地接受别人的批评和建议
例如,在一次评审中,有同学指出我的REST API设计不符合HATEOAS原则,这促使我去学习Spring HATEOAS:
// 改进后的Controller返回带链接的资源
@GetMapping("/{id}")
public EntityModel<UserDTO> getUserById(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
return EntityModel.of(user,
linkTo(methodOn(UserController.class).getUserById(id)).withSelfRel(),
linkTo(methodOn(UserController.class).getAllUsers()).withRel("users"));
}
四、软件质量保障体系
4.1 持续集成实践
课程中我们搭建了基于GitHub Actions的CI流水线:
# .github/workflows/build.yml
name: Java CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Run Tests
run: mvn test
这种自动化流程确保每次提交都不会破坏现有功能,大大提高了代码稳定性。
4.2 代码质量监控
我们集成了SonarQube进行静态代码分析,这帮助我们发现了很多潜在问题:
-
重复代码
-
潜在的安全漏洞
-
代码复杂度问题
-
测试覆盖率不足
通过修复这些问题,我的代码质量有了明显提升。
五、项目部署与运维基础
5.1 Docker化部署
课程教授了如何使用Docker打包SpringBoot应用:
# Dockerfile示例
FROM adoptopenjdk:11-jre-hotspot
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=mydb
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
这种容器化部署方式简化了环境配置问题,使应用可以轻松地在不同环境中运行。
5.2 基础监控与日志
我们学习了如何添加Actuator端点进行应用监控:
# application.yml
management:
endpoints:
web:
exposure:
include: health, info, metrics
endpoint:
health:
show-details: always
以及如何配置日志系统:
// 日志记录最佳实践
@Slf4j
@Service
public class OrderService {
public Order createOrder(OrderDTO orderDTO) {
log.info("Creating order for user {}", orderDTO.getUserId());
try {
// 业务逻辑
return orderRepository.save(order);
} catch (Exception e) {
log.error("Failed to create order", e);
throw new OrderProcessingException("Order creation failed");
}
}
}
六、课程收获与未来规划
通过《软件工程实务》的学习,我不仅掌握了SpringBoot等具体技术,更重要的是建立了完整的软件工程思维:
-
系统化思维:不再只关注代码实现,而是从全局角度考虑软件生命周期
-
质量意识:将测试、代码审查、静态分析等质量保障措施纳入开发流程
-
团队协作能力:学会使用专业工具和方法进行高效协作
-
文档习惯:养成了编写API文档、项目文档的良好习惯
未来,我计划:
-
深入学习领域驱动设计(DDD)在SpringBoot中的应用
-
研究更复杂的分布式系统架构
-
实践更完善的DevOps流程
-
参与开源项目,积累实战经验
这门课程为我打开了软件工程的大门,让我认识到编程只是软件开发的冰山一角。感谢老师的悉心指导,我会将这些工程化思维应用到未来的学习和工作中,努力成为一名合格的软件工程师。