1. Spring Boot项目结构概述

第一次接触Spring Boot项目时,我盯着密密麻麻的目录结构发懵——这跟我之前学的Java SE项目完全不同。后来才明白,这种结构是Spring Boot"约定大于配置"理念的直观体现。典型的Spring Boot项目分为以下几个核心部分:

  • src/main/java :存放所有Java源代码
  • src/main/resources :配置文件、静态资源
  • src/test :测试代码
  • target :编译后的输出目录

这种标准化结构有个巨大优势:任何熟悉Spring Boot的开发者接手项目时,都能快速定位代码位置。我去年参与的一个校园二手交易平台项目,团队5个人能在两周内完成联调,很大程度上就归功于这种规范的项目结构。

2. 项目入口类解析

2.1 主启动类的作用

每个Spring Boot项目都有一个标注 @SpringBootApplication 的主类。这个注解实际上是个"三合一"组合注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

记得第一次写课程设计时,我好奇为什么启动类放在根包下很重要。后来发现 @ComponentScan 默认会扫描当前包及其子包,如果放错位置会导致其他组件无法被自动装配。

2.2 启动流程详解

当执行main方法时,Spring Boot会:

  1. 创建Spring应用上下文
  2. 自动配置Tomcat等嵌入式容器
  3. 加载所有 @Component 注解的Bean
  4. 根据application.properties/yml配置应用

我曾遇到一个典型问题:启动时报"Port 8080 already in use"。解决方法很简单,在application.yml中添加:

server:
  port: 8081

3. 分层架构设计与实践

3.1 Controller层设计

Controller是MVC中的C层,负责接收HTTP请求并返回响应。建议采用RESTful风格设计:

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }
}

3.2 Service层实现

Service层处理业务逻辑,建议接口与实现分离:

public interface UserService {
    User getUserById(Long id);
}

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public User getUserById(Long id) {
        return userRepository.findById(id)
               .orElseThrow(() -> new ResourceNotFoundException("User not found"));
    }
}

3.3 DAO层与MyBatis整合

Spring Boot整合MyBatis非常简单:

  1. 添加依赖:
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
  1. 创建Mapper接口:
@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User findById(Long id);
}

4. 配置文件详解

4.1 application.yml配置

YAML相比properties文件更具可读性:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.entity

4.2 多环境配置

实际项目中通常需要多环境配置:

application.yml        # 公共配置
application-dev.yml    # 开发环境
application-prod.yml   # 生产环境

通过 spring.profiles.active 指定激活的环境。

5. 实体类与数据库映射

5.1 实体类设计

使用JPA注解简化ORM映射:

@Entity
@Table(name = "users")
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 50)
    private String username;
    
    // getters and setters
}

5.2 常用注解说明

  • @Entity :声明为JPA实体
  • @Table :指定映射的表名
  • @Id :标识主键
  • @GeneratedValue :主键生成策略
  • @Column :字段映射配置

6. 自定义注解开发

6.1 创建自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
    String value() default "";
    OperationType type() default OperationType.QUERY;
}

6.2 注解处理器实现

通过AOP实现注解功能:

@Aspect
@Component
public class LogAspect {
    
    @Around("@annotation(log)")
    public Object around(ProceedingJoinPoint joinPoint, OperationLog log) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        
        System.out.println("操作[" + log.value() + "]耗时:" + (end - start) + "ms");
        return result;
    }
}

7. 拦截器与过滤器

7.1 实现权限拦截器

@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                           HttpServletResponse response, 
                           Object handler) throws Exception {
        
        String token = request.getHeader("Authorization");
        if (!validateToken(token)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), "无效令牌");
            return false;
        }
        return true;
    }
}

7.2 注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/login");
    }
}

8. 配置类与自动装配

8.1 自定义配置类

@Configuration
public class AppConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

8.2 条件化配置

Spring Boot提供了丰富的条件注解:

@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    // 当类路径存在DataSource时自动配置
}

9. 异常处理机制

9.1 全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        ErrorResponse response = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage()
        );
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }
}

9.2 自定义异常

public class BusinessException extends RuntimeException {
    private final ErrorCode errorCode;
    
    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }
}

10. 测试与部署

10.1 单元测试示例

@SpringBootTest
public class UserServiceTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testGetUserById() {
        User user = userService.getUserById(1L);
        assertNotNull(user);
        assertEquals("admin", user.getUsername());
    }
}

10.2 打包与运行

使用Maven打包:

mvn clean package

运行jar包:

java -jar target/your-project.jar

在开发电商系统项目时,我们团队通过合理的项目结构划分和规范的代码组织,使得后期功能扩展变得非常顺畅。特别是采用分层架构后,前端同学只需要了解Controller层的接口定义,不需要关心后端具体实现,大大提高了协作效率。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐