Building a Reusable Backend Base Project with Swagger, CodeGenerator, Pagination, Exception Handling, and Multi‑Environment Configuration
This article explains how to create a reusable backend foundation for new Spring Boot projects by assembling common utilities such as Swagger API documentation, MyBatis‑Plus code generation, unified response objects, pagination helpers, custom exception handling, multi‑environment Maven and Spring profiles, logback configuration, and a Jenkins pipeline.
When starting a new project or splitting a large legacy codebase, developers often repeat tasks like copying utility classes, common DTOs, and configuration files. Creating a ready‑to‑use base project eliminates this redundancy; a new service can be built by simply adding a few configurations on top of the foundation.
What the Base Project Should Contain
Swagger online API documentation.
CodeGenerator for automatic entity, service, mapper, and XML generation.
Unified response wrapper (ResultVo).
Common pagination objects (PageForm, PageVo).
Utility classes.
Global exception interceptor.
Error enum and custom exception classes.
Multi‑environment configuration files.
Maven multi‑profile settings.
Logback configuration.
Jenkinsfile for CI/CD.
Swagger
Swagger simplifies API documentation and testing. It should be enabled only in test or development environments.
Common Swagger Annotations
@Api– applied to a controller class. @ApiOperation – describes a controller method. @ApiResponses – defines possible response types. @ApiModel – marks a model class. @ApiModelProperty – documents a field.
Example
@RestController
@Api(tags = "User")
@AllArgsConstructor
@RequestMapping("/user")
public class UserController {
private IUserService userService;
/**
* Get user list
* @param listUserForm form data
* @return user list
*/
@ApiOperation("Get user list")
@GetMapping("/listUser")
@ApiResponses(@ApiResponse(code = 200, message = "Operation successful", response = UserVo.class))
public ResultVo listUser(@Validated ListUserForm listUserForm) {
return ResultVoUtil.success(userService.listUser(listUserForm));
}
}CodeGenerator
The MyBatis‑Plus code generator can automatically create entity, service, serviceImpl, mapper, and mapper.xml files, saving the effort of writing boilerplate code. The full configuration can be found at the linked Gitee file.
Common Wrappers
Unified Response – ResultVo
@Data
@ApiModel("Fixed response format")
public class ResultVo {
@ApiModelProperty("Error code")
private Integer code;
@ApiModelProperty("Message")
private String message;
@ApiModelProperty("Response data")
private Object data;
}Abstract Form – BaseForm
public abstract class BaseForm<T> {
/**
* Build the corresponding entity.
*/
public abstract T buildEntity();
}Using BaseForm allows services to work with generic forms without caring about the conversion logic.
Pagination Objects
PageForm
@Data
@ApiModel(value = "Pagination data", description = "Form data for pagination")
public class PageForm<T extends PageForm<?>> {
@ApiModelProperty(value = "Page number, starting from 1")
@Min(value = 1, message = "Invalid page number")
private Integer current;
@ApiModelProperty(value = "Page size, range 1~100")
@Range(min = 1, max = 100, message = "Invalid page size")
private Integer size;
@ApiModelProperty(hidden = true)
public T calcCurrent() {
current = (current - 1) * size;
return (T) this;
}
}PageVo
@Data
public class PageVo<T> {
@ApiModelProperty("Records")
private List<T> records;
@ApiModelProperty("Total count")
private Integer total;
@ApiModelProperty("Total pages")
private Integer pages;
@ApiModelProperty("Current page")
private Integer current;
@ApiModelProperty("Page size")
private Integer size;
@ApiModelProperty(hidden = true)
public PageVo<T> setCurrentAndSize(PageForm<?> pageForm) {
BeanUtils.copyProperties(pageForm, this);
return this;
}
@ApiModelProperty(hidden = true)
public void setTotal(Integer total) {
this.total = total;
this.pages = total % size > 0 ? total / size + 1 : total / size;
}
}When querying the second page with size=10, calcCurrent() converts current=2 to the correct offset 10.
Exception Handling
Custom Exception
@Data
@EqualsAndHashCode(callSuper = false)
public class CustomException extends RuntimeException {
private final Integer code;
private final String method;
public CustomException(ResultEnum resultEnum, String method) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
this.method = method;
}
public CustomException(Integer code, String message, String method) {
super(message);
this.code = code;
this.method = method;
}
}Error Enum
@Getter
public enum ResultEnum {
UNKNOWN_EXCEPTION(100, "Unknown exception"),
ADD_ERROR(103, "Add failed"),
UPDATE_ERROR(104, "Update failed"),
DELETE_ERROR(105, "Delete failed"),
GET_ERROR(106, "Get failed");
private final Integer code;
private final String msg;
ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public static ResultEnum getByCode(int code) {
for (ResultEnum e : values()) {
if (e.code == code) return e;
}
return null;
}
}Global Exception Interceptor
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandling {
@ExceptionHandler(CustomException.class)
public ResultVo processException(CustomException e) {
log.error("Location:{} -> Error:{}", e.getMethod(), e.getLocalizedMessage());
return ResultVoUtil.error(Objects.requireNonNull(ResultEnum.getByCode(e.getCode())));
}
@ResponseStatus(HttpStatus.OK)
@ExceptionHandler(Exception.class)
public ResultVo exception(Exception e) {
e.printStackTrace();
return ResultVoUtil.error(ResultEnum.UNKNOWN_EXCEPTION);
}
}Multi‑Environment Configuration
Spring Boot Profiles
Typical profiles are dev, test, pre, and prod. Activate a profile at runtime with --spring.profiles.active=prod.
Maven Profiles
<!-- Configure environments -->
<profiles>
<profile>
<id>dev</id>
<activation><activeByDefault>true</activeByDefault></activation>
<properties><activatedProperties>dev</activatedProperties></properties>
</profile>
<profile>
<id>test</id>
<properties><activatedProperties>test</activatedProperties></properties>
</profile>
<profile>
<id>pre</id>
<properties><activatedProperties>pre</activatedProperties></properties>
</profile>
<profile>
<id>prod</id>
<properties><activatedProperties>prod</activatedProperties></properties>
</profile>
</profiles>In application.yml use spring.profiles.active=@activatedProperties@ so the placeholder is replaced during the Maven build.
Build Commands
mvn clean package -P prod
mvn clean package -P pre
mvn clean package -P testLog Configuration
Logback is used for logging; the full configuration can be found at the linked Gitee file.
Jenkinsfile
The Jenkinsfile defines the CI/CD pipeline for building and deploying the project to different environments. Refer to the repository for the exact script.
Code Repository
All source code is hosted at https://gitee.com/huangxunhui/basic_project.git .
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
