1. 项目结构设计骨架决定功能一个标准的SpringBootMyBatis-Plus项目就像乐高积木合理的文件结构能让各个模块各司其职。我常看到新手把Controller和Service代码混在一起就像把冰箱和洗衣机堆在客厅——功能能用但极其混乱。这里分享我优化过数十个项目总结的标准结构src/main/java ├── com │ └── yourpackage │ ├── annotation # 自定义注解目录 │ ├── config # 配置类集中营 │ ├── controller # MVC的交通枢纽 │ ├── dao # 数据通道接口 │ ├── entity # 数据库表镜像 │ ├── interceptor # 请求过滤器 │ └── service # 业务逻辑大本营 src/main/resources ├── static # 静态资源仓库 ├── templates # 页面模板 └── application.yml # 核心配置文件这种结构最妙的是符合约定大于配置原则。比如把Controller放在com.xxx.controller包下SpringBoot会自动扫描注册不需要像SSM时代那样写一堆XML配置。我曾接手过一个老项目Controller分散在多个包启动时要手动指定扫描路径维护起来简直噩梦。实体类(Entity)与数据库表的映射关系值得特别注意。MyBatis-Plus默认采用驼峰转下划线命名映射比如userName字段自动对应user_name列。但遇到特殊情况时可以用TableField注解显式指定TableField(value is_deleted) // 处理非常规字段名 private Integer deleted;2. 启动类项目的引擎室主启动类SpringbootSchemaApplication是项目的点火开关这个类看似简单却藏着几个关键设计点SpringBootApplication MapperScan(com.dao) // 重点1Mapper接口扫描 public class SpringbootSchemaApplication { public static void main(String[] args) { // 重点2启动时配置调整 SpringApplication app new SpringApplication(SpringbootSchemaApplication.class); app.setBannerMode(Banner.Mode.OFF); // 关闭烦人的启动图案 app.run(args); } }踩过坑才知道MapperScan的路径设置有多重要。有次我把路径写成com.dao.*结果运行时疯狂报Invalid bound statement异常。后来发现通配符写法会导致Mapper接口扫描失败必须精确到包路径。启动类还可以集成这些实用功能自定义异常处理ControllerAdvice开启异步支持EnableAsync定时任务开关EnableScheduling3. 数据访问层与数据库的对话艺术MyBatis-Plus让DAO层开发变得极其简洁但用好这些特性需要技巧3.1 基础CRUD的优雅实现public interface UserDao extends BaseMapperUser { // 继承BaseMapper就自动获得18个基础方法 Select(SELECT * FROM user WHERE age #{age}) ListUser selectByAge(Param(age) Integer age); }BaseMapper提供的Wrapper查询才是真正的生产力工具QueryWrapperUser wrapper new QueryWrapper(); wrapper.select(id,name) .like(name,张) .between(age,18,30) .orderByDesc(create_time); ListUser users userDao.selectList(wrapper);3.2 分页查询的正确姿势需要在配置类注册分页插件Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor interceptor new PaginationInterceptor(); interceptor.setOverflow(true); // 超出总页数时返回第一页 return interceptor; }使用时配合Page对象PageUser page new Page(1, 10); // 当前页,每页条数 IPageUser userPage userDao.selectPage(page, wrapper);4. 控制层业务逻辑的交通指挥Controller设计最容易被忽视的是参数校验。推荐使用Hibernate ValidatorPostMapping(/users) public R createUser(Valid RequestBody UserDTO dto) { // 自动校验DTO中的NotBlank等注解 } // 配合全局异常处理器 ExceptionHandler(MethodArgumentNotValidException.class) public R handleValidException(MethodArgumentNotValidException e) { String message e.getBindingResult().getAllErrors() .stream().map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(;)); return R.error(400, message); }接口版本控制是另一个实用技巧。当API需要升级时可以通过路径或header实现多版本共存GetMapping(/v1/users) public R getUsersV1() {...} GetMapping(/v2/users) public R getUsersV2() {...}5. 配置体系项目的神经中枢application.yml的配置项就像项目的控制面板这些配置经验值得收藏5.1 多环境配置技巧# application-dev.yml spring: datasource: url: jdbc:mysql://dev.db.com:3306/db --- # application-prod.yml spring: datasource: url: jdbc:mysql://prod.db.com:3306/db通过启动参数激活环境--spring.profiles.activeprod5.2 MyBatis-Plus黄金配置mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml # 支持多模块扫描 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发时开启SQL日志 default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段 logic-not-delete-value: 0 logic-delete-value: 16. 拦截器与注解打造系统防护网6.1 权限拦截器实战Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 放行OPTIONS请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } // 2. 检查是否需要跳过验证 if (handler instanceof HandlerMethod) { HandlerMethod hm (HandlerMethod) handler; if (hm.hasMethodAnnotation(IgnoreAuth.class)) { return true; } } // 3. Token验证逻辑 String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BusinessException(无效token, 401); } // ...验证token有效性 } }6.2 自定义注解开发创建RequiresRoles注解实现角色控制Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequiresRoles { String[] value() default {}; Logical logical() default Logical.AND; // AND表示需要同时具备 }通过AOP实现注解解析Aspect Component public class RoleAspect { Before(annotation(requiresRoles)) public void checkRole(RequiresRoles requiresRoles) { String[] roles requiresRoles.value(); // 获取当前用户角色并验证... } }7. 异常处理系统的安全气囊全局异常处理是项目健壮性的关键RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(Exception.class) public R handleException(Exception e) { log.error(系统异常, e); return R.error(500, 系统繁忙); } ExceptionHandler(BusinessException.class) public R handleBusinessException(BusinessException e) { return R.error(e.getCode(), e.getMessage()); } }自定义业务异常类public class BusinessException extends RuntimeException { private int code; public BusinessException(String msg, int code) { super(msg); this.code code; } // getter... }8. 实用配置类集锦8.1 跨域配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(*) .maxAge(3600); } }8.2 日期格式化Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder - { builder.simpleDateFormat(yyyy-MM-dd HH:mm:ss); builder.timeZone(TimeZone.getTimeZone(Asia/Shanghai)); }; }8.3 静态资源处理Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceLocations(file:/opt/uploads/); }在真实项目开发中我习惯把Swagger文档配置也放在config包下。这样当新人接手项目时通过访问/swagger-ui.html就能立即了解所有接口定义比写文档高效得多。
JavaEE课程设计:从项目结构到核心配置的详细设计剖析
1. 项目结构设计骨架决定功能一个标准的SpringBootMyBatis-Plus项目就像乐高积木合理的文件结构能让各个模块各司其职。我常看到新手把Controller和Service代码混在一起就像把冰箱和洗衣机堆在客厅——功能能用但极其混乱。这里分享我优化过数十个项目总结的标准结构src/main/java ├── com │ └── yourpackage │ ├── annotation # 自定义注解目录 │ ├── config # 配置类集中营 │ ├── controller # MVC的交通枢纽 │ ├── dao # 数据通道接口 │ ├── entity # 数据库表镜像 │ ├── interceptor # 请求过滤器 │ └── service # 业务逻辑大本营 src/main/resources ├── static # 静态资源仓库 ├── templates # 页面模板 └── application.yml # 核心配置文件这种结构最妙的是符合约定大于配置原则。比如把Controller放在com.xxx.controller包下SpringBoot会自动扫描注册不需要像SSM时代那样写一堆XML配置。我曾接手过一个老项目Controller分散在多个包启动时要手动指定扫描路径维护起来简直噩梦。实体类(Entity)与数据库表的映射关系值得特别注意。MyBatis-Plus默认采用驼峰转下划线命名映射比如userName字段自动对应user_name列。但遇到特殊情况时可以用TableField注解显式指定TableField(value is_deleted) // 处理非常规字段名 private Integer deleted;2. 启动类项目的引擎室主启动类SpringbootSchemaApplication是项目的点火开关这个类看似简单却藏着几个关键设计点SpringBootApplication MapperScan(com.dao) // 重点1Mapper接口扫描 public class SpringbootSchemaApplication { public static void main(String[] args) { // 重点2启动时配置调整 SpringApplication app new SpringApplication(SpringbootSchemaApplication.class); app.setBannerMode(Banner.Mode.OFF); // 关闭烦人的启动图案 app.run(args); } }踩过坑才知道MapperScan的路径设置有多重要。有次我把路径写成com.dao.*结果运行时疯狂报Invalid bound statement异常。后来发现通配符写法会导致Mapper接口扫描失败必须精确到包路径。启动类还可以集成这些实用功能自定义异常处理ControllerAdvice开启异步支持EnableAsync定时任务开关EnableScheduling3. 数据访问层与数据库的对话艺术MyBatis-Plus让DAO层开发变得极其简洁但用好这些特性需要技巧3.1 基础CRUD的优雅实现public interface UserDao extends BaseMapperUser { // 继承BaseMapper就自动获得18个基础方法 Select(SELECT * FROM user WHERE age #{age}) ListUser selectByAge(Param(age) Integer age); }BaseMapper提供的Wrapper查询才是真正的生产力工具QueryWrapperUser wrapper new QueryWrapper(); wrapper.select(id,name) .like(name,张) .between(age,18,30) .orderByDesc(create_time); ListUser users userDao.selectList(wrapper);3.2 分页查询的正确姿势需要在配置类注册分页插件Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor interceptor new PaginationInterceptor(); interceptor.setOverflow(true); // 超出总页数时返回第一页 return interceptor; }使用时配合Page对象PageUser page new Page(1, 10); // 当前页,每页条数 IPageUser userPage userDao.selectPage(page, wrapper);4. 控制层业务逻辑的交通指挥Controller设计最容易被忽视的是参数校验。推荐使用Hibernate ValidatorPostMapping(/users) public R createUser(Valid RequestBody UserDTO dto) { // 自动校验DTO中的NotBlank等注解 } // 配合全局异常处理器 ExceptionHandler(MethodArgumentNotValidException.class) public R handleValidException(MethodArgumentNotValidException e) { String message e.getBindingResult().getAllErrors() .stream().map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(;)); return R.error(400, message); }接口版本控制是另一个实用技巧。当API需要升级时可以通过路径或header实现多版本共存GetMapping(/v1/users) public R getUsersV1() {...} GetMapping(/v2/users) public R getUsersV2() {...}5. 配置体系项目的神经中枢application.yml的配置项就像项目的控制面板这些配置经验值得收藏5.1 多环境配置技巧# application-dev.yml spring: datasource: url: jdbc:mysql://dev.db.com:3306/db --- # application-prod.yml spring: datasource: url: jdbc:mysql://prod.db.com:3306/db通过启动参数激活环境--spring.profiles.activeprod5.2 MyBatis-Plus黄金配置mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml # 支持多模块扫描 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发时开启SQL日志 default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段 logic-not-delete-value: 0 logic-delete-value: 16. 拦截器与注解打造系统防护网6.1 权限拦截器实战Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 放行OPTIONS请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } // 2. 检查是否需要跳过验证 if (handler instanceof HandlerMethod) { HandlerMethod hm (HandlerMethod) handler; if (hm.hasMethodAnnotation(IgnoreAuth.class)) { return true; } } // 3. Token验证逻辑 String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BusinessException(无效token, 401); } // ...验证token有效性 } }6.2 自定义注解开发创建RequiresRoles注解实现角色控制Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequiresRoles { String[] value() default {}; Logical logical() default Logical.AND; // AND表示需要同时具备 }通过AOP实现注解解析Aspect Component public class RoleAspect { Before(annotation(requiresRoles)) public void checkRole(RequiresRoles requiresRoles) { String[] roles requiresRoles.value(); // 获取当前用户角色并验证... } }7. 异常处理系统的安全气囊全局异常处理是项目健壮性的关键RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(Exception.class) public R handleException(Exception e) { log.error(系统异常, e); return R.error(500, 系统繁忙); } ExceptionHandler(BusinessException.class) public R handleBusinessException(BusinessException e) { return R.error(e.getCode(), e.getMessage()); } }自定义业务异常类public class BusinessException extends RuntimeException { private int code; public BusinessException(String msg, int code) { super(msg); this.code code; } // getter... }8. 实用配置类集锦8.1 跨域配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(*) .allowedMethods(*) .maxAge(3600); } }8.2 日期格式化Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder - { builder.simpleDateFormat(yyyy-MM-dd HH:mm:ss); builder.timeZone(TimeZone.getTimeZone(Asia/Shanghai)); }; }8.3 静态资源处理Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/uploads/**) .addResourceLocations(file:/opt/uploads/); }在真实项目开发中我习惯把Swagger文档配置也放在config包下。这样当新人接手项目时通过访问/swagger-ui.html就能立即了解所有接口定义比写文档高效得多。