Recently, I have made several small and medium-sized API projects with Spring Boot, MyBatis, general Mapper plug-in and PageHelper paging plug-in. After doing this, I feel that this framework and tools are really very comfortable to develop such projects, and the team also has good response. In the project construction and development process also summed up some small experience, to share with you.

Before developing an API project, the basics of building the project, introducing dependencies, and configuring the framework need not be mentioned, but some common classes and tools need to be packaged to speed up the development of the project. For example, unified response result encapsulation, unified exception handling, interface signature authentication, basic add, delete, change method encapsulation, basic code generation tools, and so on.

However, the next time you do a similar project, you may have to go through the above steps again, which is usually a waste of time. So, can make use of the thought of object-oriented abstraction, encapsulation, extraction of this kind of project in common packaging has become a seed project (estimated the seeds of most companies will have a lot of similar project), so the next time to develop a similar project directly on the seed project iteration, reduce meaningless repetition of work.

After the project went live, I took some time to refine the seed project and have shared it on GitHub so you can clone it if you are working on a similar project.

Project address & Use documentation: Forward + focus scan

If you find any problems or have any good suggestions, please make issue or PR to improve it.

Features & Offers

Best practice project structure, configuration files, streamlined POM

! [](https://p26-tt.byteimg.com/large/pgc-image/8f78fc7121114ab5a8a3803275230b67)

Note: Model, DAO, Service, Web, and other packages are created when code is generated using a code generator.

Unified response result encapsulation and generation tools

/** * public class Result {private int code; private String message; private Object data; public Result setCode(ResultCode resultCode) { this.code = resultCode.code; return this; } // omit getter, setter methods} /** * response code enumeration, */ public enum ResultCode {SUCCESS(200),// FAIL(400),// FAIL to UNAUTHORIZED(401),// Not authorized (signature error) NOT_FOUND(404),// Interface not found INTERNAL_SERVER_ERROR(500); // Server internal error public int code; ResultCode(int code) { this.code = code; }} /** * Public class ResultGenerator {private static Final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; public static Result genSuccessResult() { return new Result() .setCode(ResultCode.SUCCESS) .setMessage(DEFAULT_SUCCESS_MESSAGE); } public static Result genSuccessResult(Object data) { return new Result() .setCode(ResultCode.SUCCESS) .setMessage(DEFAULT_SUCCESS_MESSAGE) .setData(data); } public static Result genFailResult(String message) { return new Result() .setCode(ResultCode.FAIL) .setMessage(message); }}Copy the code

Unified Exception Handling

public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { exceptionResolvers.add(new HandlerExceptionResolver() { public ModelAndView resolveException(HttpServletRequest request,  HttpServletResponse response, Object handler, Exception e) { Result result = new Result(); Result.setcode (resultcode.fail).setMessage(LLDB etMessage()); if (e instanceof ServiceException) {// Service failure exceptions, such as resultcode.fail).setMessage(LLDB etMessage()); logger.info(e.getMessage()); } else if (e instanceof NoHandlerFoundException) {result.setcode (resultcode.not_found).setMessage(" interface [" + Request. GetRequestURI () + "] does not exist "); } else if (e instanceof ServletException) { result.setCode(ResultCode.FAIL).setMessage(e.getMessage()); } else {result.setcode (resultcode.internal_server_error).setMessage(" interface [" + request.getrequesturi () + "] internal error, Please contact the administrator "); String message; if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Format (" error [%s] : %s.%s, error: %s. %s", request.getRequestURI(), handlerMethod.getBean().getClass().getName(), handlerMethod.getMethod().getName(), e.getMessage()); } else { message = e.getMessage(); } logger.error(message, e); } responseResult(response, result); return new ModelAndView(); }}); }Copy the code

The common base approach is abstract encapsulation

public interface Service<T> { void save(T model); // Persist void save(List<T> models); // Batch persistence void deleteById(Integer ID); Void deleteByIds(String ids); Eg: ids -> "1,2,3,4" void update(T model); // Update T findById(Integer id); Throws TooManyResultsException; // Find T findBy(String fieldName, Object value); List<T> findByIds(String ids) List<T> findByIds(String ids); // ids -> "1,2,3,4" List<T> findByCondition(Condition Condition); List<T> findAll(); // get all}Copy the code

Provide code generators to generate base code

public abstract class CodeGenerator { ... Public static void main(String[] args) {genCode(" enter table name "); } public static void genCode(String... TableNames) {for (String tableName: tableNames) {// Generate as required. genModelAndMapper(tableName); genService(tableName); genController(tableName); }}... }Copy the code

CodeGenerator can generate corresponding Model, Mapper, MapperXML, Service, ServiceImpl, and Controller (POST and RESTful Controller templates are provided by default. Select as needed in the genController(tableName) method, default is pure POST), and the code template can be customized to the needs of the actual project to minimize rework.

Because every company’s business is different, a few simple generic method templates are provided, mainly to provide an idea to reduce repetitive code writing. In the actual use of our company, we actually write a lot of code templates based on business abstractions.

Provides simple interface signature authentication

Public void addInterceptors(InterceptorRegistry registry) {// The interface signature authenticates the interceptor. This signature authentication is relatively simple and can be replaced by a Json Web Token or other better methods in actual projects. if (!" Dev. "equals (env)) {/ / development environment to ignore signature certification registry. AddInterceptor (new HandlerInterceptorAdapter () {@ Override public Boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object Handler) throws Exception {// Validate signature Boolean pass = validateSign(request); if (pass) { return true; } else {logger.warn(" Signature authentication failed, request interface: {}, request IP: {}, request parameters: {}", request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap())); Result result = new Result(); Result.setcode (resultcode.unauthorized).setmessage (" Signature authentication failed "); responseResult(response, result); return false; }}}); }} /** * A simple signature authentication rule: * 1\. Sort request parameters by ASCII code * 2\. Joining together for a = value&b = value... Such a string (excluding sign) * 3\. Mixed key (secret) for MD5 to obtain the signature, Compare with the request signature */ private Boolean validateSign(HttpServletRequest Request) {String requestSign = request.getParameter("sign"); / / get the request signature, such as sign = 19 e907700db7ad91318424a97c54ed57 if (StringUtils. IsEmpty (requestSign)) {return false. } List<String> keys = new ArrayList<String>(request.getParameterMap().keySet()); keys.remove("sign"); // Exclude the sign parameter collections.sort (keys); StringBuilder sb = new StringBuilder(); for (String key : keys) { sb.append(key).append("=").append(request.getParameter(key)).append("&"); LinkString = sb.toString(); linkString = sb.toString(); linkString = StringUtils.substring(linkString, 0, linkString.length() - 1); // Remove the last '&' String secret = "Potato"; String sign = digestutils.md5HEX (linkString + secret); String sign = digestutils.md5hex (linkString + secret); Md5 return stringutils. equals(sign, requestSign); / / compare}Copy the code

Integrated MyBatis, general Mapper plug-in, PageHelper paging plug-in, to achieve single table business zero SQL

Use the Druid Spring Boot Starter to integrate Druid database connection pool and monitoring

Use FastJsonHttpMessageConverter, raising the speed of the JSON serialization