preface

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. If you are planning a similar project, you can try to clone it. The project address & use documentation: github.com/lihengming/… . 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



    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}Copy the code
    /** * 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; }}Copy the code
    Public class ResultGenerator {private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; 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; }}}); }}Copy the code
    /** * A simple signature authentication, rules: * 1. Request parameters sorted by ASCII * 2. Joining together for a = value&b = value... Such a string (excluding sign) * 3. Mixed key (secret) for MD5 to obtain 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

Technical selection & documentation

  • Spring Boot (see the Spring Boot Learning & Using Guide)
  • MyBatis
  • MyBatisb universal Mapper plugin
  • MyBatis PageHelper plugin for pagination
  • Druid Spring Boot Starter Druid Spring Boot Starter
  • Fastjson (see official Chinese documentation)

After the speech

Thanks for your support, have never thought a simple project summary to share in a short span of two days to get the attention of so many people, also on the lot Trending list, a little flattered, saying now domestic technology community atmosphere is really getting better and better, I hope you have the time to share can participate in the open source, share knowledge, happy coding, ‘.