SpringBoot 项目模板:摆脱步步搭建
阅读本文大概需要 16 分钟。
git clone https://github.com/e-commerce-sample/order-backend
git checkout a443dace
从写好README开始
项目简介:用一两句话简单描述该项目所实现的业务功能;
技术选型:列出项目的技术栈,包括语言、框架和中间件等;
本地构建:列出本地开发过程中所用到的工具命令;
领域模型:核心的领域概念,比如对于示例电商系统来说有Order、Product等;
测试策略:自动化测试如何分类,哪些必须写测试,哪些没有必要写测试;
技术架构:技术架构图;
部署架构:部署架构图;
外部依赖:项目运行时所依赖的外部集成方,比如订单系统会依赖于会员系统;
环境信息:各个环境的访问方式,数据库连接等;
编码实践:统一的编码实践,比如异常处理原则、分页封装等;
FAQ:开发过程中常见问题的解答。
https://www.thoughtworks.com/radar/techniques/lightweight-architecture-decision-records
一键式本地构建
生成IDE工程:idea.sh,生成IntelliJ工程文件并自动打开IntelliJ
本地运行:run.sh,本地启动项目,自动启动本地数据库,监听调试端口5005
本地构建:local-build.sh,只有本地构建成功才能提交代码
拉取代码;
运行idea.sh,自动打开IntelliJ;
编写代码,包含业务代码和自动化测试;
运行run.sh,进行本地调试或必要的手动测试(本步骤不是必需);
运行local-build.sh,完成本地构建;
再次拉取代码,保证local-build.sh成功,提交代码。
#!/usr/bin/env bash./gradlew clean bootRun
目录结构
└── order-backend├── gradle // 文件夹,用于放置所有Gradle配置├── src // 文件夹,Java源代码├── idea.sh //生成IntelliJ工程├── local-build.sh // 提交之前的本地构建└── run.sh // 本地运行
├── gradle│ ├── checkstyle│ │ ├── checkstyle.gradle│ │ └── checkstyle.xml
基于业务分包
├── order│ ├── OrderApplicationService.java│ ├── OrderController.java│ ├── OrderNotFoundException.java│ ├── OrderRepository.java│ ├── OrderService.java│ └── model│ ├── Order.java│ ├── OrderFactory.java│ ├── OrderId.java│ ├── OrderItem.java│ └── OrderStatus.java
└── product├── Product.java├── ProductApplicationService.java├── ProductController.java├── ProductId.java└── ProductRepository.java
└── common├── configuration├── exception├── loggin└── utils
自动化测试分类
单元测试:核心的领域模型,包括领域对象(比如Order类),Factory类,领域服务类等;
组件测试:不适合写单元测试但是又必须测试的类,比如Repository类,在有些项目中,这种类型测试也被称为集成测试;
API测试:模拟客户端测试各个API接口,需要启动程序。
sourceSets {componentTest {compileClasspath += sourceSets.main.output + sourceSets.test.outputruntimeClasspath += sourceSets.main.output + sourceSets.test.output}apiTest {compileClasspath += sourceSets.main.output + sourceSets.test.outputruntimeClasspath += sourceSets.main.output + sourceSets.test.output}}
单元测试:src/test/java
组件测试:src/componentTest/java
API测试:src/apiTest/java
apply plugin: 'docker-compose'dockerCompose {useComposeFiles = ['docker/mysql/docker-compose.yml']}bootRun.dependsOn composeUpcomponentTest.dependsOn composeUpapiTest.dependsOn composeUp
https://www.cnblogs.com/CloudTeng/p/3417762.html
日志处理
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain)throws ServletException, IOException {//request id in header may come from Gateway, eg. NginxString headerRequestId = request.getHeader(HEADER_X_REQUEST_ID);MDC.put(REQUEST_ID, isNullOrEmpty(headerRequestId) ? newUuid() : headerRequestId);try {filterChain.doFilter(request, response);} finally {clearMdc();}}
ecommerce-order-backend-${ACTIVE_PROFILE} elk.yourdomain.com 6379 whatever ecommerce-ordder-log true redis
异常处理
向客户端提供格式统一的异常返回
异常信息中应该包含足够多的上下文信息,最好是结构化的数据以便于客户端解析
不同类型的异常应该包含唯一标识,以便客户端精确识别
public abstract class AppException extends RuntimeException {private final ErrorCode code;private final Mapdata = newHashMap(); }
public class OrderNotFoundException extends AppException {public OrderNotFoundException(OrderId orderId) {super(ErrorCode.ORDER_NOT_FOUND, ImmutableMap.of("orderId", orderId.toString()));}}
public final class ErrorDetail {private final ErrorCode code;private final int status;private final String message;private final String path;private final Instant timestamp;private final Mapdata = newHashMap(); }
{requestId: "d008ef46bb4f4cf19c9081ad50df33bd",error: {code: "ORDER_NOT_FOUND",status: 404,message: "没有找到订单",path: "/order",timestamp: 1555031270087,data: {orderId: "123456789"}}}
后台任务与分布式锁
@Configuration@EnableAsync@EnableSchedulingpublic class SchedulingConfiguration implements SchedulingConfigurer {@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.setScheduler(newScheduledThreadPool(10));}@Bean(destroyMethod = "shutdown")@Primarypublic TaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2);executor.setMaxPoolSize(5);executor.setQueueCapacity(10);executor.setTaskDecorator(new LogbackMdcTaskDecorator());executor.initialize();return executor;}}
@Configuration@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")public class DistributedLockConfiguration {@Beanpublic LockProvider lockProvider(DataSource dataSource) {return new JdbcTemplateLockProvider(dataSource);}@Beanpublic DistributedLockExecutor distributedLockExecutor(LockProvider lockProvider) {return new DistributedLockExecutor(lockProvider);}}
@Scheduled(cron = "0 0/1 * * * ?")@SchedulerLock(name = "scheduledTask", lockAtMostFor = THIRTY_MIN, lockAtLeastFor = ONE_MIN)public void run() {logger.info("Run scheduled task.");}为了支持代码直接调用分布式锁,基于Shedlock的LockProvider创建DistributedLockExecutor:public class DistributedLockExecutor {private final LockProvider lockProvider;public DistributedLockExecutor(LockProvider lockProvider) {this.lockProvider = lockProvider;}publicT executeWithLock(Supplier supplier, LockConfiguration configuration) { Optionallock = lockProvider.lock(configuration); if (!lock.isPresent()) {throw new LockAlreadyOccupiedException(configuration.getName());}try {return supplier.get();} finally {lock.get().unlock();}}}
public String doBusiness() {return distributedLockExecutor.executeWithLock(() -> "Hello World.",new LockConfiguration("key", Instant.now().plusSeconds(60)));}
统一代码风格
客户端的请求数据类统一使用相同后缀,比如Command
返回给客户端的数据统一使用相同后缀,比如Represetation
统一对请求处理的流程框架,比如采用传统的3层架构或者DDD战术模式
提供一致的异常返回(请参考“异常处理”小节)
提供统一的分页结构类
明确测试分类以及统一的测试基础类(请参考“自动化测试分类”小节)
静态代码检查
Checkstyle:用于检查代码格式,规范编码风格
Spotbugs:Findbugs的继承者
Dependency check:OWASP提供的Java类库安全性检查
Sonar:用于代码持续改进的跟踪
健康检查
我们希望初步检查程序是否运行正常
有些负载均衡软件会通过一个健康检查URL判断节点的可达性
./run.sh
{requestId: "698c8d29add54e24a3d435e2c749ea00",buildNumber: "unknown",buildTime: "unknown",deployTime: "2019-04-11T13:05:46.901+08:00[Asia/Shanghai]",gitRevision: "unknown",gitBranch: "unknown",environment: "[local]"}
API文档
@Configuration@EnableSwagger2@Profile(value = {"local", "dev"})public class SwaggerConfiguration {@Beanpublic Docket api() {return new Docket(SWAGGER_2).select().apis(basePackage("com.ecommerce.order")).paths(any()).build();}}

数据库迁移
resources/├── db│ └── migration│ ├── V1__init.sql│ └── V2__create_product_table.sql
多环境构建
local:用于开发者本地开发
ci:用于持续集成
dev:用于前端开发联调
qa:用于测试人员
uat:类生产环境,用于功能验收(有时也称为staging环境)
prod:正式的生产环境
CORS
@Configurationpublic class CorsConfiguration {@Beanpublic WebMvcConfigurer corsConfigurer() {return new WebMvcConfigurer() {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**");}};}}
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {protected void configure(HttpSecurity http) throws Exception {http// by default uses a Bean by the name of corsConfigurationSource.cors().and()...}CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(Arrays.asList("https://example.com"));configuration.setAllowedMethods(Arrays.asList("GET","POST"));UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}}
Guava:来自Google的常用类库
Apache Commons:来自Apache的常用类库
Mockito:主要用于单元测试的mock
DBUnit:测试中管理数据库测试数据
Rest Assured:用于Rest API测试
Jackson 2:Json数据的序列化和反序列化
jjwt:Jwt token认证
Lombok:自动生成常见Java代码,比如equals()方法,getter和setter等;
Feign:声明式Rest客户端
Tika:用于准确检测文件类型
itext:生成Pdf文件等
zxing:生成二维码
Xstream:比Jaxb更轻量级的XML处理库
总结
推荐阅读:
想接私活时薪再翻一倍,建议根据这几个开源的SpringBoot项目(含小程序)改改~
微信扫描二维码,关注我的公众号
朕已阅 

