JAVA background development specification

  • This specification is based on the development manuals of Alibaba and Huawei. Thanks to the experience and dedication of our predecessors, we can have the opportunity to stand on the shoulders of giants and look at the stars and the sea.
  • Norms are not to restrict and imprison everyone's creativity, but to help everyone avoid stepping on pits and deviation as much as possible on the right road.
  • Norms can make us handy when walking alone or with others.
  • Norms can make us more elegant and calm in the face of increasingly abnormal needs and when we are the code receiver.
  • Rules are not perfect, and may affect code implementation by restricting and prohibiting features in specific situations.
  • But the purpose of making rules: in order to get more benefits, if the team believes that a rule cannot be followed or there is a better practice in the actual operation of the team, I hope everyone can jointly improve the specification.

1, Programming specification

1. Principles of good code

We refer to Kent Beck's four simple design principles to guide us how to write excellent code and how to effectively judge whether our code is excellent.

  • Passes its tests: the emphasis is on external requirements, which is the most important part of code implementation
  • Minimize duplication: the modular architecture design of the code ensures the orthogonality of the code and easier modification of the code
  • Maximize clarity: the readability of the code ensures that the code is easy to read
  • Has fewer elements: ensure that the code is concise. Between simplicity and expressiveness, we pay more attention to expressiveness

The importance of the above four principles decreases in turn. This set of definitions is called simple design principles.

2. Project naming specification

All are in lowercase and separated by a middle dash.

Positive example: mall management system / order service client / user API

Counterexample: mall_management-system / mallManagementSystem / orderServiceClient

3. TODO/FIXME specification

TODO/TBD(to be determined) notes are generally used to describe the known modification points to be improved and supplemented, and add the author's name.
FIXME annotations are generally used to describe known defects. They should have a unified style to facilitate the unified processing of text search. For example:

// Todo < author name >: Supplement XX processing
// Fixme < author name >: XX defect

4. Method parameter specification

Whether it's controller, service, manager, dao or other code, each method can have up to 3 parameters. If more than 3 parameters, they should be encapsulated into javabean objects.

  1. It is convenient for others to call and reduce the probability of error. Especially when the parameters are of the same type and only depend on the order, the slightest carelessness will be disastrous, and the troubleshooting is extremely disgusting.
  2. Keep the code clean and clear. When methods are filled with piles of parameters, no matter how strong people are, they will be physically and mentally exhausted.

Counterexample:

/**
* Method of using certificate to encrypt data tool
*
* @param param
* @param password Encryption password
* @param priCert Private key
* @param pubCert Public key
* @return Returns the encrypted string
*/
public String signEnvelop(JdRequestParam param, String password, String priCert, String pubCert){}

5. Annotation specification

5-1. Comments are as important as code

Notes are the traces and lessons we left when we climbed over this mountain through hardships. These valuable knowledge not only proves that we once existed, but also reminds later people to learn from the past and forge ahead.

In addition to explaining the function and logic. There is also a very important reason: when the business logic is too complex and the code is too large, annotations become road signs to beautify the environment and separate and sort out logical ideas. This is a very important point. It can effectively help us avoid getting bogged down in code and business logic.

Positive example:

/**
* Start lottery method
* Save winning information, reward user points, etc
* @param luckDrawDTO
* @return ResponseDTO Return winning information
*/
public ResponseDTO<String> startLuckDraw(LuckDrawDTO luckDrawDTO) {

    // --------------1. Verify the basic information of lottery activities------------------------
    xxx Pseudo code one meal operation

    // --------------2. Add lottery record-------------------------------
    xxx Pseudo code one meal operation

    // --------------3. If points need to be consumed, deduct steel points-------------
    xxx Pseudo code one meal operation

    // --------------4. Get the prize information and start rolling--------------------
    xxx Pseudo code one meal operation

    return ResponseDTO.succ(luckDrawPrizeVO);
}

5-2. Consistency of comments and codes

The more comments are not the better. When there are too many comments, it is necessary to maintain comments while maintaining the code, which not only becomes a burden, but also runs counter to our original intention of adding comments.

First of all: we should improve code readability through clear logical architecture and good variable naming; Notes shall be supplemented when necessary. Annotation is to help readers quickly understand the code, so it should be annotated on demand from the perspective of readers. The content of notes should be concise, clear, unambiguous, comprehensive and non redundant.

Secondly: whether modifying or copying the code, carefully check whether the comments are correct. It is an uncivilized behavior to only change the code without changing the annotation, which destroys the consistency between the code and the annotation, and will confuse, confuse and even misunderstand the reader.

Counterexample:

// Query Department
EmployeeDTO employee = employeeDao.listByDeptId(deptId);

5-3. Method notes

The method should try to explain itself through the method name. Do not write useless and redundant method headers, and do not write empty and formatted method header comments.

The content of method header comments is optional, but not limited to: function description, return value, usage, algorithm implementation, etc. In particular, the annotation of the external method interface declaration should clearly express the important and useful information.

Positive example:

/**
 * Parse the conversion time string to the LocalDate time class
 * The string format must be verified before calling, otherwise it may cause the error exception of parsing failure
 *
 * @param dateStr Must be a string in yyyy MM DD format
 * @return LocalDate
 */
public static LocalDate parseYMD(String dateStr){}

Counterexample:

/**
 * Verification object
 *
 * @param t
 * @return String
 */
public static <T> String checkObj(T t);

Problems in the counterexample:

  • The method note does not specify the specific function and use.
  • Parameter, return value, empty format, no content. This is a very important point. Before calling any method, anyone needs to know the requirements of the method for parameters and what the return value is.

2, Project specification

1. Code directory structure

A unified directory structure is the foundation of all projects.

src                               Source directory
|-- common                            General class library of each project
|-- config                            Configuration information of the project
|-- constant                          Global common constant
|-- handler                           Global processor
|-- interceptor                       Global connector
|-- listener                          Global listener
|-- module                            Each business
|-- |--- employee                         Employee module
|-- |--- role                             Role module
|-- |--- login                            Login module
|-- third                             Third party services, such as redis, oss,WeChat sdk wait
|-- util                              Global tool class
|-- Application.java                  Startup class

2. common directory specification

The common directory is used to store items common to each project, but it can be modified according to the project.

src Source directory
|-- common General class library of each project
|-- |--- anno          General annotations, such as permissions, login, etc
|-- |--- constant      General constants, such as ResponseCodeConst
|-- |--- domain        Global javabean,such as BaseEntity,PageParamDTO etc.
|-- |--- exception     Global exceptions, such as BusinessException
|-- |--- json          json Class library, such as LongJsonDeserializer,LongJsonSerializer
|-- |--- swagger       swagger file
|-- |--- validator     General for each project validator,as CheckEnum,CheckBigDecimal etc.

3. config directory specification

The config directory is used to store items common to each project, but specific modifications can be made according to the project.

src                               Source directory
|-- config                            All configuration information of the project
|-- |--- MvcConfig                    mvc Relevant configurations of, such as interceptor,filter etc.
|-- |--- DataSourceConfig             Configuration of database connection pool
|-- |--- MybatisConfig                mybatis Configuration of
|-- |--- ....                         other

4. module directory specification

module directory is used to write various businesses of the project. Each business has an independent top-level folder, and mvc is divided in the file.
Among them, the domain package stores javabean objects such as entity, dto, vo and bo

src
|-- module                         All business modules
|-- |-- role                          Role module
|-- |-- |--RoleController.java              controller
|-- |-- |--RoleConst.java                   role Related constants
|-- |-- |--RoleService.java                 service
|-- |-- |--RoleDao.java                     dao
|-- |-- |--domain                           domain
|-- |-- |-- |-- RoleEntity.java                  Table corresponding entity
|-- |-- |-- |-- RoleDTO.java                     dto object
|-- |-- |-- |-- RoleVO.java                      Return object
|-- |-- employee                      Employee module
|-- |-- login                         Login module
|-- |-- email                         Mail module
|-- |-- ....                          other

5. Java bean naming convention in domain package

1) Overall requirements for JavaBeans:

  • There shall be no business logic or calculation
  • The basic data type must use the wrapper type (Integer, Double, Boolean, etc.)
  • No default values are allowed
  • Each attribute must be annotated and multiline comments must be used.
  • You must use lombok to simplify getter/setter methods
  • It is recommended to use @ Builder and @ NoArgsConstructor of lombok for objects, and use these two annotations at the same time to simplify the object construction method and set method.

Positive example:

@Builder
@NoArgsConstructor
@Data
public class DemoDTO {

    private String name;
    
    private Integer age;
}

// Use example:

DemoDTO demo = DemoDTO.builder()
                .name("yeqiu")
                .age(66)
                .build();

2) Data object; Xxxentity, requirements:

  • End with Entity (end with DO)
  • Xxxx is consistent with the database table name
  • The fields in the class should be consistent with the database fields, and cannot be missing or redundant
  • Add comments to each field in the class and keep consistent with the database comments
  • Combination is not allowed
  • The date types in the project must be unified, and Java is recommended util. Date,java.sql.Timestamp,java.time.LocalDateTime is only one of them.

3) Transmission object; Xdto, requirements:

  • Cannot inherit from Entity
  • DTO can inherit and combine other DTO, VO, BO and other objects
  • DTO can only be used for request parameters of front-end and RPC

3) View object; Xxxvo, requirements:

  • Cannot inherit from Entity
  • VO can inherit and combine other DTO, VO, BO and other objects
  • VO can only be used to return business data encapsulation objects of front-end and rpc

4) Business object BO, requirements:

  • Cannot inherit from Entity
  • BO objects can only be used in the service, manager and dao layers, not in the controller layer

3, MVC specification

1. Overall stratification

  • controller layer
  • service layer
  • manager layer
  • dao layer

2. controller layer specification

1) It is only allowed to add the RequestMapping annotation on the method, not on the class (for the convenience of finding the url, the url cannot be found at one time)

Positive example:

@RestController
public class DepartmentController {

    @GetMapping("/department/list")
    public ResponseDTO<List<DepartmentVO>> listDepartment() {
        return departmentService.listDepartment();
    }

Counterexample:

@RequestMapping ("/department")
public class DepartmentController {

    @GetMapping("/list")
    public ResponseDTO<List<DepartmentVO>> listDepartment() {
        return departmentService.listDepartment();
    }

2) It is not recommended to use rest to name URLs. You can only use the get/post method. The url naming specification is as follows:

Although the Rest method is good, sometimes we can't see what operation it is according to the url, so we chose the latter. There is no right or wrong, but which is more suitable for our team.

/Business module / sub module / action

Positive example:

GET  /department/get/{id}      Query the details of a department
POST /department/query         Complex query
POST /department/add           Add Department
POST /department/update        Update department
GET  /department/delete/{id}   Delete Department

3) Each method must add the swagger document annotation @ ApiOperation and fill in the interface description information. At the end of the description, the author information @ author Nezha must be added.

Positive example:

    @ApiOperation("Update department information @author nezha")
    @PostMapping("/department/update")
    public ResponseDTO<String> updateDepartment(@Valid @RequestBody DeptUpdateDTO deptUpdateDTO) {
        return departmentService.updateDepartment(deptUpdateDTO);
    }

4) Each business delegate and controller should be responsible for keeping the routing simple:

  • Do not do any business logic
  • No parameter or business verification is required. Only @ Valid annotation can be used for parameter verification
  • Do not do any data combination, assembly, assignment and other operations

Positive example:

    @ApiOperation("Add Department @author nezha")
    @PostMapping("/department/add")
    public ResponseDTO<String> addDepartment(@Valid @RequestBody DepartmentCreateDTO departmentCreateDTO) {
        return departmentService.addDepartment(departmentCreateDTO);
    }

5) The current requesting user can only be obtained in the controller layer and passed to the service layer.

Because the current request user is obtained from ThreadLocal, it is very likely that it is called by other non request threads in the service, manager and dao layers, which will be null. Try to avoid it

    @ApiOperation("Add employee @author yandanyang")
    @PostMapping("/employee/add")
    public ResponseDTO<String> addEmployee(@Valid @RequestBody EmployeeAddDTO employeeAddDTO) {
        LoginTokenBO requestToken = SmartRequestTokenUtil.getRequestUser();
        return employeeService.addEmployee(employeeAddDTO, requestToken);
    }

3. service layer specification

1) Reasonably split the service file. If the business is large, please split it into multiple services.

For example, if the order business is written into the OrderService, the file will be too large, so it needs to be split as follows:

  • OrderQueryService order query business
  • OrderCreateService order creation business
  • OrderDeliverService order delivery business
  • OrderValidatorService order validation business

2) Handle the use of @ Transactional annotation with caution. Don't simply add @ Transactional annotation to the service method and feel that everything is fine. Operations on the database should be consolidated to minimize the addition of business logic within the @ Transactional method.
@The rollbackFor value in the Transactional annotation must use the base class throwable class

For @ Transactional annotation, when spring encounters this annotation, it will automatically obtain the connection from the database connection pool and start the transaction, and then bind it to ThreadLocal. If the business does not enter the final operation database phase, it is not necessary to obtain the connection and start the transaction, and the connection should be directly returned to the database connection pool, For other use (it's difficult to explain clearly. If you don't understand, take the initiative to ask).

Counterexample:

    @Transactional(rollbackFor = Throwable.class)
    public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
        // Verification 1
        DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
        if (departmentEntity == null) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
        }
        // Verification 2
        DepartmentEntity swapEntity = departmentDao.selectById(swapId);
        if (swapEntity == null) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
        }
        // Verification 3
        Long count = employeeDao.countByDepartmentId(departmentId)
        if (count != null && count > 0) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
        }
        // Operation database 4
        Long departmentSort = departmentEntity.getSort();
        departmentEntity.setSort(swapEntity.getSort());
        departmentDao.updateById(departmentEntity);
        swapEntity.setSort(departmentSort);
        departmentDao.updateById(swapEntity);
        return ResponseDTO.succ();
    }

The first three steps of the above code use connection for verification. Because there is @ Transactional annotation on the method, these three verifications use the same connection.

For complex business and complex verification logic, the connection will always be occupied in the whole verification process, which may take a long time. The connection will not be returned to the database connection pool until the method is completed.

For the unpredictable situation of complex business, it is not a good thing to occupy the same connection for a long time, and the occupation time should be shortened as far as possible.

Positive example:

    DepartmentService.java

    public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
        DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
        if (departmentEntity == null) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
        }
        DepartmentEntity swapEntity = departmentDao.selectById(swapId);
        if (swapEntity == null) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
        }
        Long count = employeeDao.countByDepartmentId(departmentId)
        if (count != null && count > 0) {
            return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
        }
        departmentManager.upOrDown(departmentSort,swapEntity);
        return ResponseDTO.succ();
    }


    DepartmentManager.java

    @Transactional(rollbackFor = Throwable.class)
    public void upOrDown(DepartmentEntity departmentEntity ,DepartmentEntity swapEntity){
        Long departmentSort = departmentEntity.getSort();
        departmentEntity.setSort(swapEntity.getSort());
        departmentDao.updateById(departmentEntity);
        swapEntity.setSort(departmentSort);
        departmentDao.updateById(swapEntity);
    }

Prepare the data in the service layer, and then transfer it to the manager layer. The manager layer adds @ Transactional for database operation.

3) It should be noted that the annotation @ Transactional transaction will not take effect in the internal method call of the class

Counterexample: if an exception occurs, the transaction annotation on the saveData method will not work

@Service
public class OrderService{

    public void createOrder(OrderCreateDTO createDTO){
        this.saveData(createDTO);
    }

    @Transactional(rollbackFor = Throwable.class)
    public void saveData(OrderCreateDTO createDTO){
        orderDao.insert(createDTO);
    }
}

Spring uses dynamic proxy (AOP) to manage and slice bean s. It generates A proxy object for each class. Facet logic can only be triggered when calling between proxy objects. In the same class, method A calls method B, which calls the method of the original object instead of the proxy object. Therefore, spring cannot intercept this call, so it cannot guarantee the transaction through annotation. Simply put, method calls in the same class will not be intercepted by the method interceptor, so the transaction will not work.

Solution:

  1. You can put the method into another class, such as adding a new manager layer and injecting it through spring, which meets the conditions for calling between objects.
  2. Add @ EnableAspectJAutoProxy(exposeProxy = true) to the startup class, and use aopcontext in the method Currentproxy() gets the proxy class and uses transactions.
SpringBootApplication.java

@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {}

OrderService.java

public void createOrder(OrderCreateDTO createDTO){
    OrderService orderService = (OrderService)AopContext.currentProxy();
    orderService.saveData(createDTO);
}

4) Service is a specific business processing logic service layer. Try to avoid passing some parameters of the web layer to the service.

Counterexample:

public ResponseDTO<String> handlePinganRequest(HttpServletRequest request){
    InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream(), "GBK");
    BufferedReader reader = new BufferedReader(inputStreamReader);
    StringBuilder sb = new StringBuilder();
    String str;
    while ((str = reader.readLine()) != null) {
        sb.append(str);
    }
    if(!JSON.isValid(msg)){
      return ResponseDTO.wrap(ResponseCodeConst.ERROR_PARAM);
    }
    PinganMsgDTO PinganMsgDTO = JSON.parseObject(msg,PinganMsgDTO.class);
    // End of example
}

Problems in the counterexample:

  • In the counter example, the HttpServletRequest is passed to the service to obtain the character information in the Request stream, and then the real business processing. According to the original intention of layering: decouple the code and business logic. The correct approach should be that the handlePinganRequest method takes the String character as a parameter to directly process the business, and puts the operation of obtaining the character from the Request into the controller.
  • Another disadvantage is that it is not convenient to do unit testing. You have to create a new HttpServletRequest and create an InputStream. However, this can not simulate the real business scenario and data.

4. manager layer specification

The role of the manager layer (quoted from Alibaba java manual):

  • For the layer encapsulated by the third-party platform, preprocess the return results and transform exception information;
  • Sink the general ability of Service layer, such as caching scheme and middleware general processing;
  • Interact with the DAO layer and reuse the combination of multiple Daos.

5. dao layer specification

The mybatis plus framework is preferred. If you need multiple data source operations, you can choose to use the SmartDb framework.

1) All Dao inherited from BaseMapper

2) Prohibit using the Wrapper condition builder of mybatis plus

3) It is forbidden to write dead constants directly in mybatis xml, which should be passed into xml from dao

3) It is not recommended to use asterisk * instead of all fields

Positive example:

    NoticeDao.java

    Integer noticeCount(@Param("sendStatus") Integer sendStatus);
---------------------------------------------
    NoticeMapper.xml

    <select id="noticeCount" resultType="integer">
        select
        count(1)
        from t_notice
        where
        send_status = #{sendStatus}
    </select>

Counterexample:

    NoticeDao.java

    Integer noticeCount();
---------------------------------------------
    NoticeMapper.xml

    <select id="noticeCount" resultType="integer">
        select
        count(1)
        from t_notice
        where
        send_status = 0
    </select>

3) Naming specification of dao layer method

  • The method to get a single object is prefixed with get.
  • The method to get multiple objects is prefixed with list.
  • The method to obtain the statistical value is prefixed with count.
  • The insertion method is prefixed with save/insert.
  • The delete method is prefixed with remove/delete.
  • The modified method is prefixed with update.

Suggestion: the naming of dao layer methods should be based on sql semantics to avoid business association.

Positive example:

List<PerformanceDTO> listByMonthAndItemId(@Param("month") String month, @Param("itemId") Integer itemId);

Counterexample:

List<PerformanceDTO> getInternalData(@Param("month") String month, @Param("itemId") Integer itemId);

Nonstandard operation in counterexample:

  • get represents a single query, and batch queries should start with list.
  • Naming and business association limit the usage scenario and scope of dao methods, reduce the reusability of methods, and cause confusion and repetition of others.

6. Attribute naming conventions for boolean types

Do not add is to Boolean variables in the class, otherwise some framework parsing will cause serialization errors. The basic data type is defined as Boolean; Its method is isDeleted(). During reverse parsing, RPC "thinks" that the corresponding attribute name is deleted, resulting in the failure to obtain the attribute, and then throws an exception.

This is the original text of Alibaba's development manual. Our team stipulates that the class attributes and data table fields of boolean type all end with flag. Although isDeleted is used_ Deleted is more intuitive in literal and semantic terms, but the sacrifice is worth it compared to potential errors.

Positive example:

deletedFlag,deleted_flag,onlineFlag,online_flag

7,

4, Database specification

1. Table construction specification

Three required fields in the table: id, create_time, update_time

  • The id field is of Long type, and the self increment length of a single table is 1
  • create_ The time field is of datetime type, and the default value is CURRENT_TIMESTAMP
  • update_ The time field is of datetime type, and the default value is CURRENT_TIMESTAMP, On update CURRENT_TIMESTAMP

2. The field annotation of enumeration class table needs to annotate all enumeration meanings

When modifying or adding the status description of the field, the comments must be updated synchronously in time.
Sync in the following table_ Status field synchronization status 0 is not started. 1 synchronization is in progress. 2 synchronization is successful. 3 synchronization fails.

Positive example:

CREATE TABLE `t_change_data` (
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
	`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Synchronization status 0 is not started. 1 synchronization in progress. 2 synchronization succeeded. 3 synchronization failed',
	`sync_time` DATETIME NULL DEFAULT NULL COMMENT 'Synchronization time',
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (`change_data_id`)
)

Counterexample:

CREATE TABLE `t_change_data` (
	`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
	`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Synchronization status ',
	`sync_time` DATETIME NULL DEFAULT NULL COMMENT 'Synchronization time',
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
	PRIMARY KEY (`change_data_id`)
)

3. Add indexes and unique indexes to table fields in a reasonable combination of business

Please refer to the index specification of Alibaba Java development manual for specific index specifications

5, Other

1. Code submission specification

  • Before submission, you should calmly and carefully check to ensure that you do not forget to add version control or documents that should not be submitted.
  • Before submitting, you should compile once (ctrl+F9 in idea) to prevent errors in compilation.
  • Update the pull code before submission. Conflicts before submission are much easier to resolve than conflicts after submission.
  • Before submission, check whether the code is formatted, whether it meets the code specification, whether useless packages are introduced, whether variables are cleared, etc.
  • When submitting, check whether the notes accurately and concisely express the content of this submission.

2. maven project

  • pom prohibits dependent configurations with the same groupId and artifactId.
  • The project name should be consistent with artifact ID.
  • Regularly check the dependency of jar packages and eliminate the jar packages that resolve conflicts in time.

3. Keep the project clean and tidy

To use git, you must add gitignore ignores the configuration file.
Do not submit content files unrelated to the project: idea configuration, target package, etc.

Tags: Java

Posted by 9mm on Mon, 16 May 2022 12:01:32 +0300