Alibaba_java Development Specification Manual Detailed Explanation

1. Naming style

1.1. The naming in the code cannot start with an underscore or a dollar sign, nor can it end with an underscore or a dollar sign.

Counterexample:
_name, $name, __name

1.2. It is strictly forbidden to use pinyin and English in the name of the code, and it is not allowed to use Chinese directly.

Note: Correct English spelling and grammar can make it easy for readers to understand and avoid ambiguity. Note that the pure pinyin naming method should be avoided.
Positive example: international common names such as renminbi / alibaba / taobao / youku / hangzhou can be regarded as the same as English.
Counter example: DaZhePromotion [discount] / getPingfenByName() [score] / int variable = 3

1.3. Use the UpperCamelCase style for class names, except for the following cases: DO / BO / DTO / VO / AO / PO / UID, etc.

Positive example: JavaServerlessPlatform / UserDO / XmlService / TcpUdpDeal / TaPromotion
Negative example: javaserverlessplatform / UserDo / XMLService / TCPUDPDeal / TAPromotion

1.4. Method names, parameter names, member variables, and local variables all use the lowerCamelCase style, and must follow the hump form.

正例:localValue / getHttpMessage() / inputUserId

1.5. The names of constants are all capitalized, and the words are separated by underscores. We strive to express complete and clear semantics, and don't think the names are too long.

Positive example: MAX_STOCK_COUNT / CACHE_EXPIRED_TIME
Negative example: MAX_COUNT / EXPIRED_TIME

1.6. The name of an abstract class starts with Abstract or Base; the name of an abnormal class ends with Exception; the name of a test class starts with the name of the class to be tested and ends with Test.

1.7. The type and the square brackets are closely connected to indicate an array.

Positive example: define an integer array int[] arrayDemo;
negative example: use String args[] to define in the main parameter.

1.8. Boolean variables in POJO classes should not be prefixed with is, otherwise some frame parsing will cause serialization errors.

Explanation: In the first article of the table creation agreement in the MySQL specification in this article, the value expressing yes or no adopts the naming method of is_xxx, so the mapping relationship from is_xxx to xxx needs to be set.
Counter-example: An attribute defined as the basic data type Boolean isDeleted, whose method is also isDeleted(), when the RPC framework reversely parses, "mistakenly" the corresponding attribute name is deleted, resulting in the attribute being unobtainable and throwing an exception .

1.9. The package name is uniformly lowercase, and there is only one English word with natural semantics between dot separators. The package name is uniformly used in singular form, but if the class name has a plural meaning, the class name can use plural form.

Positive example: the application tool class package name is com.alibaba.ai.util, and the class name is MessageUtils (this rule refers to the framework structure of spring)

1.10. Avoid using exactly the same name between member variables of child and parent classes, or between local variables of different code blocks, which reduces readability.

Note: The subclass and parent class member variables have the same name, even public variables can be compiled, and it is legal for local variables to have the same name in different code blocks in the same method, but it should be avoided. For non-setter/getter parameter names, avoid the same name as member variables.
Counter example:

public class ConfusingName {
    
     
            public int age; // 非 setter/getter 的参数名称,不允许与本类成员变量同名
            public void getData(String alibaba) {
    
     
                    if(condition) {
    
     
                          final int money = 531; // ... 
                    } 
                    for (int i = 0; i < 10; i++) {
    
     
                      // 在同一方法体中,不允许与其它代码块中的 money 命名相同 
                      final int money = 615; // ... 
                      }
             }
 } 
  
class Son extends ConfusingName {
    
     
        // 不允许与父类的成员变量名称相同 public int age;

}

1.11. Put an end to completely non-standard abbreviations and avoid ignorance of the meaning.

Counter-example: The "abbreviation" of AbstractClass is named AbsClass; the "abbreviation" of condition is named condi. Such random abbreviations seriously reduce the readability of the code.

1.12. In order to achieve the goal of code self-explanation, when naming any custom programming elements, use as complete a word combination as possible to express its meaning.

Positive example: In JDK, the class name expressing atomic update is: AtomicReferenceFieldUpdater.
Counterexample: Arbitrary naming of int a.

1.13. When naming constants and variables, nouns indicating types are placed at the end of the word to improve recognition.

正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT
反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD

1.14. If a module, interface, class, or method uses a design pattern, it must reflect the specific pattern when naming it.

Explanation: Embodying the design pattern in the name will help readers quickly understand the architectural design concept.
Positive example:
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;

1.15. Do not add any modifiers (not public) to the methods and properties in the interface class, keep the code concise, and add effective Javadoc comments. Try not to define variables in the interface. If you must define variables, it must be related to the interface method and is the basic constant of the entire application.

Positive example:
interface method signature void commit();
interface basic constant String COMPANY = "alibaba";
negative example: interface method definition public abstract void f();
Note: JDK8 allows interfaces to have default implementations, so this default method is right All implementing classes have a default implementation of the value.

1.16. There are two sets of rules for naming interfaces and implementation classes:

For the Service and DAO classes, based on the concept of SOA, the exposed services must be interfaces, and the internal implementation classes are distinguished from the interfaces with the suffix of Impl.
Positive example: CacheServiceImpl implements the CacheService interface.

1.17. If it is an interface name that describes capabilities, take the corresponding adjective as the interface name (usually an adjective of –able).

Positive example: AbstractTranslator implements the Translatable interface.

1.18. Enumeration class names should be suffixed with Enum, enumeration member names should be all capitalized, and words should be separated by underscores.

Explanation: An enumeration is actually a special class, the field members are all constants, and the constructor is forced to be private by default.
Positive example: Member name of enumeration name ProcessStatusEnum: SUCCESS / UNKNOWN_REASON.

1.19. Naming conventions for each layer:

Service/DAO layer method naming convention
The method of obtaining a single object is prefixed with get.
The method of obtaining multiple objects is prefixed with list, and the plural form ends like: listObjects.
Methods for obtaining statistical values ​​are prefixed with count.
Insert methods are prefixed with save/insert.
Delete methods are prefixed with remove/delete.
Modified methods are prefixed with update.

1.19. Domain Model Naming Convention

Data object: xxxDO, where xxx is the name of the data table.
Data transfer object: xxxDTO, where xxx is the name related to the business domain.
Display object: xxxVO, where xxx is generally the name of the web page.
POJO is the collective name of DO/DTO/BO/VO, and it is forbidden to name it as xxxPOJO.

2. Constant definition

2.1. Any magic values ​​(that is, undefined constants) are not allowed to appear directly in the code.

Counter-example:
String key = “Id#taobao_” + tradeId;cache.put(key, value);//When the cache gets, the underscore is missed when the code is copied, resulting in a problem with the cache breakdown

2.2. When assigning a long or Long value, use an uppercase L after the value instead of a lowercase l. The lowercase is easily confused with the number 1, causing misunderstanding.

Explanation: Long a = 2l; What is written is the number 21, or the Long type 2

2.3. Do not use a constant class to maintain all constants, but classify them according to their functions and maintain them separately.

Explanation: The large and comprehensive constant class is disorganized, and the modified constant can only be located by using the search function, which is not conducive to understanding and maintenance.
Positive example: Cache-related constants are placed under the class CacheConsts; system configuration-related constants are placed under the class ConfigConsts.

2.4. There are five levels of constant reuse: cross-application shared constants, intra-application shared constants, intra-subproject shared constants, intra-package shared constants, and intra-class shared constants.

Cross-application shared constants: placed in the second-party library, usually under the constant directory in client.jar.
In-app shared constants: Placed in a library, usually under the constant directory in a submodule.
Counter-example: Easy-to-understand variables should also be uniformly defined as shared constants within the application. Two engineers have defined "YES" variables in two classes: in class A:
public static final String YES = "yes";
in class B: public static final String YES = “y”;
A.YES.equals(B.YES), expected to be true, but actually returns false, causing online problems.

Shared constants within the sub-project: in the constant directory of the current sub-project.
Shared constants in the package: that is, in the separate constant directory under the current package.
Intra-class shared constants: Defined directly in private static final within the class.

2.5. If the variable value only changes within a fixed range, use the enum type to define it.

Note: If there is an extended attribute other than the name, the enum type should be used. The number in the positive example below is the extended information, indicating the season of the year.
Positive example:

public enum SeasonEnum {
    
     
     SPRING(1), 
     SUMMER(2), 
     AUTUMN(3), 
     WINTER(4); 
    private int seq; 
    SeasonEnum(int seq) {
    
     
        this.seq = seq; 
    } 
    public int getSeq() {
    
     
        return seq; 
    }
   } 
 }

3. Code format

3.1. If the curly braces are empty, simply write {}, and there is no need for newlines and spaces between the curly braces; if it is a non-empty code block:

No newline before the opening curly brace.
Newline after opening curly brace.
Newline before closing curly brace.
After the closing curly brace, there are codes such as else that do not wrap; after the terminating closing brace, a new line must be followed.

3.2. There is no space between the left parenthesis and the character; similarly, there is no space between the right parenthesis and the character; and a space is required before the left brace. For details, please refer to the regular prompt under Article 5.

Negative example: if (space a == b space)

3.3 Spaces must be added between reserved words such as if/for/while/switch/do and brackets.

3.4. A space needs to be added on the left and right sides of any binary and ternary operators.

Note: Operators include assignment operator =, logical operator &&, addition, subtraction, multiplication, and division symbols, etc.

3.5. Use 4 spaces for indentation and prohibit the use of tab characters.

Note: If you use tab indentation, you must set 1 tab to 4 spaces. When IDEA sets the tab to 4 spaces, please do not check Use tab character; in eclipse, you must check insert spaces for tabs.

Positive example: (involves 1-5 points)

public static void main(String args[]) {
    
    
  // 缩进 4个空格
  String say = "hello";
  // 运算符的左右必须有一个空格
  int flag = 0;
  // 关键词 if与括号之间必须有一个空格,括号内 f与左括号,1与右括号不需要空格
  if (flag == 0) {
    
    
    System.out.println(say);
  }
  // 左大括号前加空格且不换行;左大括号后换行
  if (flag == 1) {
    
    
    System.out.println("world");
    // 右大括号前换行,右大括号后有 else,不用换行
  } else {
    
    
    System.out.println("ok");
    // 右大括号做为结束,必须换行
  }
}

3.6. There is only one space between the double slashes in the comment and the content of the comment.

Positive example:
// This is a sample comment, note there is a space after the double slash
String param = new String();

3.7. When performing type coercion, there is no need for any space to separate the right bracket and the coercion value.

Positive example:

 first = 1000000000000L;
 int second = (int)first + 2; 

3.8. The number of characters in a single line shall not exceed 120, and the following principles shall be followed when line breaks are required:

1) The second line is indented 4 spaces relative to the first line, and no further indentation is given from the third line for reference examples.
2) Operators are line-wrapped with the following.
3) The dot symbol of the method call is wrapped with the following.
4) When multiple parameters are too long, a line break is performed after the comma.
5) Do not break the line before the parentheses, see counterexample.
Positive example:

StringBuffer sb = new StringBuffer();
//超过 120个字符的情况下,换行缩进 4个空格,并且方法前的点符号一起换行
sb.append("zi").append("xin")…
  .append("huang");</pre>
反例:
StringBuffer sb = new StringBuffer();
//超过120个字符的情况下,不要在括号前换行
sb.append("zi").append("xin")...append
("huang");

//参数很多的方法调用可能超过120个字符,不要在逗号前换行
method(args1, args2, args3, ...
, argsX);

3.9. When defining and passing method parameters, a space must be added after commas for multiple parameters.

Positive example: args1 of the actual parameter in the following example must be followed by a space.

method(args1, args2, args3); 

3.10. The text file encoding of the IDE is set to UTF-8; the line break of the file in the IDE should use the Unix format, not the Windows format.

3.11. The total number of lines of a single method shall not exceed 80 lines.

Note: The total number of lines of method signatures, left and right braces, codes in methods, blank lines, carriage returns, and any invisible characters other than comments should not exceed 80 lines.
Positive example: the code logic distinguishes between red flowers and green leaves, individuality and commonality, and the green leaf logic is separated out as an additional method to make the main code clearer; the commonality logic is extracted into a common method, which is easy to reuse and maintain.

3.12. It is not necessary to add several spaces to align the variable assignment equal sign with the equal sign in the corresponding position on the previous line.

Positive example:

int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();

Note: Add the variable sb. If alignment is required, add a few spaces to one, two, and three. It is very cumbersome when there are many variables.

3.13. Insert a blank line between codes with different logic, different semantics, and different businesses to separate them to improve readability.

Note: In any case, there is no need to insert multiple blank lines for separation.

4. OOP protocol

4.1. Avoid accessing static variables or static methods of this type through object references of a class, which will increase the cost of compiler parsing unnecessarily, just use the class name to access

4.2. All override methods must be annotated with @Override.

Explanation: The problem of getObject() and get0bject(). One is the O of the letter, and the other is the 0 of the number. Adding @Override can accurately determine whether the coverage is successful. In addition, if the method signature is modified in the abstract class, the implementation class will immediately compile and report an error.

4.3. The same parameter type and the same business meaning can use Java variable parameters, avoid using Object.

Note: Variable parameters must be placed at the end of the parameter list. (Students are encouraged not to program with variable parameters as much as possible)
Positive example:

public List<User> listUsers(String type, Long... ids) {
    
    ...}

4.4. It is not allowed to modify the method signature of the interface that is being called externally or that the second-party library depends on, so as to avoid affecting the interface caller. The interface must be annotated with @Deprecated when it is outdated, and clearly explain what the new interface or new service is.

4.5. Obsolete classes or methods cannot be used.

Explanation: The method decode(String encodeStr) in java.net.URLDecoder is outdated, and the two-parameter decode(String source, String encode) should be used. Since the interface provider is clearly an obsolete interface, it is obliged to provide a new interface at the same time; as the caller, it is obliged to verify what the new implementation of the obsolete method is.

4.6. The equals method of Object is easy to throw a null pointer exception, and you should use a constant or an object with a certain value to call equals.

Positive example: "test".equals(object);
Negative example: object.equals("test");
Explanation: It is recommended to use java.util.Objects#equals (a tool class introduced by JDK7).

4.7. The comparison of values ​​between all integer wrapper objects shall use the equals method for comparison.

Explanation: For the assignment of Integer var = ? in the range of -128 to 127, the Integer object is generated in IntegerCache.cache, and the existing object will be reused. The Integer value in this range can be judged directly using ==, but this range All data other than , will be generated on the heap, and existing objects will not be reused. This is a big pit, and it is recommended to use the equals method for judgment.

4.8 For the equivalence judgment between floating-point numbers, the basic data types cannot be compared with ==, and the packaged data types cannot be judged with equals.

Explanation: The floating-point number adopts the encoding method of "mantissa + exponent", which is similar to the expression method of "significant digit + exponent" in scientific notation. Binary cannot accurately represent most of the decimal numbers. For details, please refer to "Code Out Efficient".
Counter example:

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f; 
if (a == b) {
    
     
      // 预期进入此代码快,执行其它业务逻辑 
      // 但事实上 a==b 的结果为 false
 } 
Float x = Float.valueOf(a);
Float y = Float.valueOf(b);
if (x.equals(y)) {
    
     
// 预期进入此代码快,执行其它业务逻辑 
// 但事实上 equals 的结果为 false
}

Positive example:
Specify an error range, and the difference between two floating-point numbers is considered equal if it is within this range.

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e-6f; 
if (Math.abs(a - b) < diff) {
    
     
    System.out.println("true");
 }

Use BigDecimal to define the value, and then perform floating-point arithmetic operations.

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8"); 
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c); 
if (x.equals(y)) {
    
     
    System.out.println("true");
}

4.9. When defining the data object DO class, the attribute type must match the database field type.

Positive example: the bigint of the database field must correspond to the Long type of the class attribute.
Counter-example: In a certain case, the id field of the database table defines the type bigint unsigned, and the actual class object attribute is Integer. As the id becomes larger and larger, it overflows and becomes a negative number beyond the representation range of Integer.

4.10. In order to prevent the loss of precision, it is forbidden to use the construction method BigDecimal(double) to convert the double value into a BigDecimal object.

Note: BigDecimal (double) has the risk of loss of precision, which may cause business logic exceptions in precise calculation or value comparison scenarios.
Such as: BigDecimal g = new BigDecimal(0.1f); The actual storage value is: 0.10000000149
Positive example: The construction method with String as the input parameter is preferred, or the valueOf method of BigDecimal is used. This method actually executes the toString of Double internally, and Double's toString truncates the mantissa according to the precision that double can actually express.

BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.valueOf(0.1);

The usage standards for basic data types and wrapper data types are as follows:

4.11. All POJO class attributes must use wrapper data types.

4.12. The return value and parameters of RPC methods must use wrapper data types.

4.13. All local variables use basic data types.

Note: The POJO class attribute has no initial value to remind users that they must explicitly assign values ​​when they need to use them. Any NPE problems or storage inspections are guaranteed by users.
Positive example: The query result of the database may be null, because of automatic unboxing, there is a risk of NPE when receiving basic data types.
Counter-example: For example, display the rise and fall of the total transaction amount, that is, plus or minus x%, where x is the basic data type. When the RPC service called is unsuccessful, the default value is returned, and the page displays 0%, which is unreasonable. Should be displayed as a dash. Therefore, the null value of the packaging data type can represent additional information, such as: remote call failure, abnormal exit.

4.14. When defining POJO classes such as DO/DTO/VO, do not set any attribute default value.

Counter-example: The default value of createTime of POJO class is new Date(), but this property does not put a specific value when data is extracted, and this field is updated when other fields are updated, resulting in the creation time being modified to the current time.

4.15. When adding new attributes to the serialization class, please do not modify the serialVersionUID field to avoid deserialization failure; if it is completely incompatible with upgrades to avoid deserialization confusion, then please modify the serialVersionUID value.

Note: Note that if the serialVersionUID is inconsistent, a serialization runtime exception will be thrown.

4.16. It is forbidden to add any business logic in the construction method. If there is initialization logic, please put it in the init method.

4.17. POJO class must write toString method. When using the tool in the IDE: source> generate toString, if you inherit another POJO class, pay attention to add super.toString in front.

Note: When an exception is thrown during method execution, you can directly call the POJO's toString() method to print its attribute value, which is convenient for troubleshooting.

4.18. It is forbidden to have both isXxx() and getXxx() methods corresponding to attribute xxx in POJO class.

Note: When the framework calls the extraction method of attribute xxx, it cannot determine which method must be called first.

4.19. When using an index to access the array obtained by the split method of String, it is necessary to check whether there is any content after the last delimiter, otherwise there will be a risk of throwing IndexOutOfBoundsException.

illustrate:

 String str = "a,b,c,,"; String[] ary = str.split(","); 

// Expected to be greater than 3, the result is 3System.out.println(ary.length);

4.20. When a class has multiple constructors, or multiple methods with the same name, these methods should be placed together in order for easy reading. This rule takes precedence over the next one.

4.21. The order of method definitions within a class is: public method or protected method > private method > getter/setter method.

Explanation: The public method is the method that the caller and maintainer of the class are most concerned about, and the first screen display is the best; although the protected method is only concerned by the subclass, it may also be the core method under the "template design mode"; and the private method is generally not external. Need special attention, it is a black-box implementation; because the value of the carried information is low, all getter/setter methods of Service and DAO are placed at the end of the class body.

4.22. In the setter method, the parameter name is consistent with the class member variable name, this. member name = parameter name. In the getter/setter method, do not add business logic to increase the difficulty of troubleshooting.

Counter example:

public Integer getData() {
    
    
     if (condition) {
    
     
         return this.data + 100; 
     } else {
    
     
         return this.data - 100; 
     }
 } 

4.23. In the body of the loop, the string connection method is extended by using the append method of StringBuilder.

Explanation: In the following example, the decompiled bytecode file shows that a StringBuilder object will be newly generated every cycle, then append operation will be performed, and finally the String object will be returned through the toString method, resulting in waste of memory resources.
Counter example:

String str = "start"; 
for (
        int i = 0; i < 100; i++) {
    
     
        str = str + "hello";
 } 

4.24. final can declare classes, member variables, methods, and local variables. Use the final keyword in the following situations:

Classes that are not allowed to be inherited, such as: String class.
The referenced domain object is not allowed to be modified.
Methods that are not allowed to be overridden, such as: setter methods of POJO classes.
Local variables that are not allowed to be reassigned at runtime.
To avoid reusing a variable in the context, using final can force a variable to be redefined for better refactoring.

4.25. Use the clone method of Object with caution to copy objects.

Note: The object clone method defaults to a shallow copy. If you want to implement a deep copy, you need to override the clone method to implement a deep traversal copy of the domain object.

4.25. Strict access control of class members and methods:

If the outside is not allowed to create objects directly through new, then the constructor must be private.
Utility classes are not allowed to have public or default constructors.
Class non-static member variables and shared with subclasses must be protected.
Class non-static member variables and used only in this class must be private.
Class static member variables must be private if they are only used in this class.
If it is a static member variable, consider whether it is final.
Class member methods can only be called inside the class and must be private.
Class member methods are only exposed to inherited classes, so the restriction is protected.

Description: Any class, method, parameter, variable, strictly control the scope of access. Too broad access scope is not conducive to module decoupling. Thinking: If it is a private method, delete it if you want to delete it, but if you delete a public service member method or member variable, won't your palms sweat a little? Variables are like your own children, try to keep them within your sight, if the scope of variables is too large, and they run around without restriction, then you will worry.

5. Collection processing

5.1, processing of hashCode and equals

Follow the following rules:
Whenever equals is overridden, hashCode must be overridden.
Because the Set stores non-repeated objects, it is judged based on hashCode and equals, so the objects stored in the Set must override these two methods.
If a custom object is used as the key of the Map, hashCode and equals must be overridden.
Explanation: String has overridden the hashCode and equals methods, so we can happily use String objects as keys.

5.2. The subList of ArrayList cannot be forcibly converted into ArrayList

Otherwise, a ClassCastException will be thrown, namely java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.
Explanation: what subList returns is the internal class SubList of ArrayList, not ArrayList but a view of ArrayList, and all operations on the sublist of SubList will eventually be reflected on the original list.

5.3. When the method keySet()/values()/entrySet() of Map returns the collection object

When the method keySet()/values()/entrySet() of Map is used to return the collection object, it is not allowed to add elements to it, otherwise UnsupportedOperationException will be thrown.

5.4. Objects returned by the Collections class, such as: emptyList()/singletonList(), etc. are immutable lists, which cannot be added or deleted.

If the query has no results, the Collections.emptyList() empty collection object will be returned. Once the caller performs the operation of adding elements, an UnsupportedOperationException will be triggered.

5.5. In the subList scene, pay attention to adding or deleting elements of the original collection, which will cause ConcurrentModificationException when traversing, adding, and deleting sublists.

5.6. To convert a collection to an array, you must use the collection's toArray(T[] array), and the input is an empty array with exactly the same type and a length of 0.

Counter-example: There is a problem in directly using the toArray method without parameters. The return value of this method can only be the Object[] class. If you force other types of arrays, a ClassCastException error will occur.
Positive example:

List<String> list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = list.toArray(new String[0]);

Explanation: Use the toArray method with parameters, the length of the array space size:
equal to 0, dynamically create an array with the same size, and the performance is the best.
If it is greater than 0 but less than size, recreate an array whose size is equal to size, increasing the burden on GC.
It is equal to size. In the case of high concurrency, when the size is getting larger after the array is created, the negative impact is the same as above.
If it is larger than size, space is wasted, and a null value is inserted at size, which may cause NPE.

5.7. When using the addAll() method of any implementation class of the Collection interface, NPE judgment must be performed on the input collection parameters.

Explanation: The first line of code in the ArrayList#addAll method is Object[] a = c.toArray(); where c is the input set parameter, if it is null, an exception will be thrown directly.

5.8. When using the tool class Arrays.asList() to convert an array into a collection, you cannot use it to modify collection-related methods, and its add/remove/clear methods will throw UnsupportedOperationException.

Explanation: The return object of asList is an Arrays internal class, which does not implement the modification method of the collection. Arrays.asList embodies the adapter mode, which only converts the interface, and the data in the background is still an array.

String[] str = new String[] {
    
     "yang", "hao" };
List list = Arrays.asList(str);

The first case: list.add(“yangguanbao”); Runtime exception.
The second case: str[0] = "changed"; will also be modified accordingly, and vice versa.

5.9. The generic wildcard <? extends T> is used to receive the returned data. The generic collection of this writing method cannot use the add method, and <? super T> cannot use the get method, which is prone to errors when calling and assigning values ​​as interfaces.

Explanation: Let me expand on the principles of PECS (Producer Extends Consumer Super):
First, for those who read content frequently, it is suitable to use <? extends T>.
Second, for those that are often inserted, it is suitable to use <? super T>

insert image description here

5.10. When assigning a set without generic restriction definition to a collection with generic restriction, when using the collection elements, instanceof judgment is required to avoid throwing ClassCastException.

Explanation: After all, generics only appeared after JDK5. Considering forward compatibility, the compiler allows non-generic collections and generic collections to assign values ​​to each other.
Counter example:

List<String> generics = null;
List notGenerics = new ArrayList(10);
notGenerics.add(new Object());
notGenerics.add(new Integer(1)); 
generics = notGenerics;
// 此处抛出 ClassCastException 异常String string = generics.get(0);

5.11. Do not perform remove/add operations on elements in the foreach loop. Please use the Iterator method to remove elements. If the operation is concurrent, you need to lock the Iterator object.

Positive example:

List<String> list = new ArrayList<>(); 
list.add("1"); 
list.add("2"); 
Iterator<String> iterator = list.iterator(); 
while (iterator.hasNext()) {
    
     
    String item = iterator.next(); 
    if (删除元素的条件) {
    
    
         iterator.remove(); 
     } 
 }

Counter example:

for (String item : list) {
    
    
     if ("1".equals(item)) {
    
     
         list.remove(item); 
     } 
 }

Explanation: The execution result of the above code will definitely be beyond everyone's expectations, so try replacing "1" with "2", will the result be the same?

5.12. In JDK7 version and above, the Comparator implementation class must meet the following three conditions, otherwise Arrays.sort and Collections.sort will throw IllegalArgumentException.

Explanation: The three conditions are as follows:
the comparison result of x, y is opposite to the comparison result of y, x.
x>y, y>z, then x>z.
x=y, then the comparison result of x, z is the same as the comparison result of y, z.
Counter-example: In the following example, the case of equality is not handled. The judgment result of exchanging two objects is not mutually inverse. It does not meet the first condition, and an exception may occur in actual use.

new Comparator<Student>() {
    
    
     @Override public int compare(Student o1, Student o2) {
    
     
         return o1.getId() > o2.getId() ? 1 : -1;
          } 
      };

5.13. When defining collection generics, in JDK7 and above, use diamond syntax or omit all.

Explanation: The diamond-shaped generic type, that is diamond, directly uses <> to refer to the type that has been specified before.
Positive example:

diamond 方式,即<>
HashMap<String, String> userCache = new HashMap<>(16);
// 全省略方式ArrayList<User> users = new ArrayList(10); 

5.14. When the collection is initialized, specify the initial value of the collection.

Description: HashMap is initialized with HashMap(int initialCapacity).
Positive example: initialCapacity = (number of elements to be stored / load factor) + 1. Note that the load factor (ie loader factor) defaults to 0.75, if the initial value cannot be determined temporarily, please set it to 16 (ie the default value).
Counter-example: HashMap needs to place 1024 elements. Since the initial size of the capacity is not set, as the elements continue to increase, the capacity is forced to expand 7 times. Resize needs to rebuild the hash table, which seriously affects performance.

5.15. Use entrySet to traverse the Map class collection KV instead of keySet.

Explanation: The keySet is actually traversed twice, one is to convert it into an Iterator object, and the other is to get the value corresponding to the key from the hashMap. The entrySet only traverses once and puts the key and value in the entry, which is more efficient. If it is JDK8, use the Map.forEach method.
Positive example: values() returns the V value collection, which is a list collection object; keySet() returns the K value collection, which is a Set collection object; entrySet() returns the KV value combination collection.

5.16. Pay close attention to whether the Map class collection K/V can store null values, as shown in the following table:

Description of collection class
Key
Value
Super

Hashtable
not allowed to be null
not allowed to be null
Dictionary
thread safe

ConcurrentHashMap
is not allowed to be null
is not allowed to be null
AbstractMap
lock segmentation technology (JDK8: CAS)

TreeMap
does not allow null
Allows null
AbstractMap
thread unsafe

HashMap
allows null
Allows null
AbstractMap
thread unsafe

Counter-example: Due to the interference of HashMap, many people think that ConcurrentHashMap can put a null value, but in fact, an NPE exception will be thrown when storing a null value.

5.17 Make good use of the orderliness (sort) and stability (order) of the collection to avoid the negative impact of the disorder (unsort) and instability (unorder) of the collection.

Explanation: The orderliness means that the results of the traversal are arranged in order according to a certain comparison rule. Stability means that the order of elements in each traversal of the collection is certain. Such as: ArrayList is order/unsort; HashMap is order/unsort; TreeSet is order/sort.

5.18 Using the unique feature of the Set element, you can quickly perform deduplication operations on a collection, avoiding the use of the contains method of List to perform traversal, comparison, and deduplication operations.

6. Concurrent processing

6.1. Obtaining a singleton object needs to ensure thread safety, and the methods in it must also ensure thread safety.

Note: Resource-driven classes, tool classes, and singleton factory classes all need attention.

6.2. When creating a thread or thread pool, please specify a meaningful thread name to facilitate backtracking when errors occur.

Positive example: customize the thread factory and group it according to external characteristics, such as computer room information.

public class UserThreadFactory implements ThreadFactory {
    
     
    private final String name Prefix;
    private final AtomicInteger nextId = new AtomicInteger(1);
     // 定义线程组名称,在 jstack 问题排查时,非常有帮助
     UserThreadFactory(String whatFeaturOfGroup) {
    
     
          namePrefix = "From UserThreadFactory's " + whatFeaturOfGroup + "-Worker-";
          }
           
          @Override
          public Thread newThread(Runnable task) {
    
     
              String name = namePrefix + nextId.getAndIncrement(); 
              Thread thread = new Thread(null, task, name, 0, false); 
              System.out.println(thread.getName()); 
              return thread;
               } 
           }

6.3. Thread resources must be provided through the thread pool, and it is not allowed to explicitly create threads in the application.

Description: The advantage of thread pool is to reduce the time spent on creating and destroying threads and the overhead of system resources, and solve the problem of insufficient resources. If the thread pool is not used, it may cause the system to create a large number of threads of the same type, resulting in memory consumption or "over-switching".

6.4. It is not allowed to use Executors to create thread pools, but to use ThreadPoolExecutor. This processing method makes the writing students more clear about the running rules of thread pools and avoids the risk of resource exhaustion.

Note: The disadvantages of the thread pool objects returned by Executors are as follows:
FixedThreadPool and SingleThreadPool:
The allowed request queue length is Integer.MAX_VALUE, which may accumulate a large number of requests, resulting in OOM.
CachedThreadPool:
The number of threads allowed to be created is Integer.MAX_VALUE, which may create a large number of threads, resulting in OOM.

6.5. SimpleDateFormat is a thread-unsafe class. Generally, it should not be defined as a static variable. If it is defined as static, it must be locked, or use the DateUtils tool class.

Positive example: pay attention to thread safety, use DateUtils. The following treatment is also recommended:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
    
     
    @Override 
    protected DateFormat initialValue() {
    
     
        return new SimpleDateFormat("yyyy-MM-dd"); 
        } 
   };

Note: If it is a JDK8 application, you can use Instant instead of Date, LocalDateTime instead of Calendar, and DateTimeFormatter instead of SimpleDateFormat. The official explanation: simple beautiful strong immutable thread-safe.

6.6. Custom ThreadLocal variables must be recycled, especially in thread pool scenarios where threads are often reused. If custom ThreadLocal variables are not cleaned up, subsequent business logic may be affected and memory leaks may occur. Try to use try-finally blocks in proxies for recycling.

Positive example:

ThreadLocal.set(userInfo);
try {
    
     
    // ...
} finally {
    
     
     objectThreadLocal.remove();
}

6.7. When there is high concurrency, synchronous calls should consider the performance loss of locks. If you can use lock-free data structures, don't use locks; if you can lock blocks, don't lock the entire method body; if you can use object locks, don't use class locks.

Note: Try to make the workload of the locked code block as small as possible, and avoid calling the RPC method in the locked code block.

6.8. When locking multiple resources, database tables, and objects at the same time, it is necessary to maintain a consistent locking sequence, otherwise deadlock may occur.

Explanation: Thread 1 needs to lock tables A, B, and C in turn before performing update operations. Then thread 2 must also lock in the order of A, B, and C, otherwise deadlock may occur.

6.9. In the method of blocking and waiting to acquire the lock, it must be outside the try code block, and there is no method call that may throw an exception between the lock method and the try code block, so as to avoid the lock in the finally Unable to unlock.

Explanation 1: If the method call between the lock method and the try code block throws an exception, it cannot be unlocked, causing other threads to fail to acquire the lock successfully.
Explanation 2: If the lock method is within the try code block, it may be caused by other methods throwing an exception, so in the finally code block, unlock unlocks the unlocked object, and it will call the tryRelease method of AQS (depending on the specific implementation class ), an IllegalMonitorStateException is thrown.
Explanation 3: An unchecked exception may be thrown in the implementation of the lock method of the Lock object, and the result is the same as that of Explanation 2.
Positive example:

Lock lock = new XxxLock();
// ...lock.lock();
try {
    
     
    doSomething(); 
    doOthers();
} finally {
    
     
    lock.unlock();
  }

Counter example:

Lock lock = new XxxLock();
// ...
try {
    
     
   // 如果此处抛出异常,则直接执行 finally 代码块 
   doSomething(); 
   // 无论加锁是否成功,
   finally 代码块都会执行 
   lock.lock(); 
   doOthers();
} finally {
    
    
   lock.unlock();   
}

6.10. In the way of using the trial mechanism to acquire the lock, before entering the business code block, it must first determine whether the current thread holds the lock. The release rules of the lock are the same as the blocking and waiting mode of the lock.

Note: When the unlock method of the Lock object is executed, it will call the tryRelease method of AQS (depending on the specific implementation class). If the current thread does not hold the lock, an IllegalMonitorStateException will be thrown.
Positive example:

Lock lock = new XxxLock();
// ...boolean isLocked = lock.tryLock();
if (isLocked) {
    
     
try {
    
     
   doSomething(); 
   doOthers(); 
  } finally {
    
     
     lock.unlock(); 
   } 
}

6.11 When modifying the same record concurrently, to avoid loss of updates, locks are required. Either lock at the application layer, or lock at the cache, or use optimistic locking at the database layer, and use version as the update basis.

Note: If the probability of each access conflict is less than 20%, it is recommended to use optimistic locking, otherwise pessimistic locking is used. The number of retries for optimistic locking should not be less than 3 times.

6.11. When threads process timed tasks in parallel, when Timer runs multiple TimeTasks, as long as one of them does not catch the thrown exception, other tasks will automatically terminate. If you use ScheduledExecutorService when processing timed tasks, there is no such problem.

6.12. For financial sensitive information related to funds, use a pessimistic locking strategy.

Explanation: Optimistic lock has completed the update operation while acquiring the lock, and the verification logic is prone to loopholes. In addition, optimistic lock has complex requirements for conflict resolution strategies. Improper handling may easily cause system pressure or data abnormalities, so funds are related It is not recommended to use optimistic lock update for financial sensitive information.

6.13. Use CountDownLatch to perform asynchronous to synchronous operations. Each thread must call the countDown method before exiting. The thread execution agent pays attention to the catch exception to ensure that the countDown method is executed to prevent the main thread from being unable to execute the await method and return the result until the timeout.

Note: Note that the exception stack thrown by the child thread cannot be caught in the main thread try-catch.

6.14. The Random instance is used by multiple threads. Although sharing the instance is thread-safe, it will cause performance degradation due to competition for the same seed.

说明:Random 实例包括 java.util.Random 的实例或者 Math.random()的方式。
正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom,而在 JDK7 之前,需要编码保证每个线程持有一个实例。

6.15、在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化问题隐患(可参考 The “Double-Checked Locking is Broken” Declaration),推荐解决方案中较为简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型。

反例:

 class LazyInitDemo {
    
     
     private Helper helper = null; 
     public Helper getHelper() {
    
     
         if (helper == null) 
         synchronized(this) {
    
     
             if (helper == null) 
                 helper = new Helper(); 
             } 
             return helper; 
           } 
           // other methods and fields... 
    }

6.16、volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但

是如果多写,同样无法解决线程安全问题。
说明:如果是 count++操作,使用如下类实现:
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观
锁的重试次数)。

6.17、HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在

开发过程中可以使用其它数据结构或加锁来规避此风险。

6.17、ThreadLocal 对象使用 static 修饰,ThreadLocal 无法解决共享对象的更新问题。

Description: This variable is shared for all operations in a thread, so it is set as a static variable, and all such instances share this static
variable when the class is loaded for the first time, only a piece of storage space is allocated, and all Objects of this class (as long as they are defined in this thread
) can manipulate this variable.

7. Control statement

7.1. In a switch block, each case is terminated by continue/break/return, etc., or a comment indicates which case the program will continue to execute; in a switch block, a default statement must be included and placed in In the end, even it has no code whatsoever.

Note: Note that break is to exit the switch statement block, and return is to exit the method body.

7.2. When the variable type in the brackets of the switch is String and the variable is an external parameter, null judgment must be performed first.

Counterexample: Guess what the output of the following code is?

public class SwitchString {
    
    
     public static void main(String[] args) {
    
     
         method(null); 
    } 
   
    public static void method(String param) {
    
     
        switch (param) {
    
     
         // 肯定不是进入这里 
         case "sth": System.out.println("it's sth"); 
             break; 
         // 也不是进入这里 
         case "null": System.out.println("it's null"); 
             break; 
         // 也不是进入这里 
         default: 
             System.out.println("default"); 
         } 
         }
    }

7.3. Braces must be used in if/else/for/while/do statements.

Note: Even if there is only one line of code, avoid single-line coding: if (condition) statements;

7.4. In high-concurrency scenarios, avoid using "equal to" judgments as interrupt or exit conditions.

Note: If the concurrency control is not handled well, it is easy to cause the situation that the equivalence judgment is "broken down", and the interval judgment condition of greater than or less than is used instead.
Counter-example: When it is judged that the remaining number of prizes is equal to 0, the distribution of prizes is terminated, but due to a concurrent processing error, the number of prizes instantly becomes negative. In this case, the activity cannot be terminated.

7.5. When expressing abnormal branches, use less if-else methods, which can be rewritten as:

if (condition) {
    
     
    ... return obj; 
} 

// Then write the business logic code of else;
Note: If you do not use if()...else if()...else... to express logic, to avoid difficulties in subsequent code maintenance, please do not exceed 3 layers.
Positive example: if-else logic judgment codes with more than 3 layers can be implemented using guard statements, strategy patterns, state patterns, etc., where guard statements or code logic first considers failures, exceptions, interruptions, exits, etc. The way of multiple exits of the method solves the problem of judging branch nesting in the code, which is the embodiment of reverse thinking.
Examples are as follows:

public void findBoyfriend(Man man) {
    
     
    if (man.isUgly()) {
    
     
        System.out.println("本姑娘是外貌协会的资深会员"); 
        return; 
    } 
if (man.isPoor()) {
    
     
    System.out.println("贫贱夫妻百事哀"); 
    return; 
} 

if (man.isBadTemper()) {
    
     
    System.out.println("银河有多远,你就给我滚多远"); 
    return; 
}

 System.out.println("可以先交往一段时间看看");
 }

7.6. Except for common methods (such as getXxx/isXxx), do not execute other complex statements in the conditional judgment, and assign the result of complex logic judgment to a meaningful Boolean variable name to improve readability.

Explanation: The logical expressions in many if statements are quite complicated, and the mixed operations of AND, OR, and negation, and even in-depth calls of various methods, the cost of understanding is very high. It's a delightful thing to assign a well-understood Boolean variable name.
Positive example:

// The pseudo code is as follows

final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
    
    
 ...
 }

Counter example:

public final void acquire(long arg) {
    
    
     if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
    
     
         selfInterrupt(); 
     } 
 }

7.7. Do not insert assignment statements in other expressions (especially conditional expressions).

Note: The assignment point is similar to the acupuncture points of the human body, which is crucial to the understanding of the code, so the assignment statement needs to be clearly separated into a line.
Counterexample:
public Lock getLock(boolean fair) { // Assignment operation appears in arithmetic expression, easy to ignore count value has been changed threshold = (count = Integer.MAX_VALUE) - 1; // Assignment operation appears in conditional expression, easy mistaken for


 sync==fair
 return (sync = fair) ? new FairSync() : new NonfairSync();
}

7.8. The statements in the loop body should consider performance. The following operations should be moved outside the loop as much as possible, such as defining objects, variables, obtaining database connections, and performing unnecessary try-catch operations (whether this try-catch can be moved outside the loop)

7.9. Avoid using negated logical operators.

Explanation: Negative logic is not conducive to quick understanding, and there must be a corresponding positive logic writing for negating logic.
Positive example: use if (x < 628) to express that x is less than 628.
Counterexample: Use if (!(x >= 628)) to express that x is less than 628.

7.10. Interface input parameter protection, which is commonly used as an interface for batch operations.

Parameter verification is required in the following situations:
methods with low calling frequency.
Methods that are expensive to execute. In this case, the parameter verification time is almost negligible, but if the intermediate execution rolls back or is wrong due to a parameter error, the loss outweighs the gain.
Methods that require extreme stability and availability.
The open interface provided externally, no matter it is RPC/API/HTTP interface.
Sensitive permission entry.

7.11. In the following situations, parameter verification is not required

Methods that are most likely to be called cyclically. However, the external parameter checking requirements must be indicated in the method description.
A method with a relatively high frequency of underlying calls. After all, it is like the last step of pure water filtration, and it is unlikely that the wrong parameters will expose the problem to the bottom layer. Generally, both the DAO layer and the Service layer are in the same application and deployed on the same server, so the parameter verification of DAO can be omitted.
A method that is declared as private will only be called by its own code. If you can be sure that the parameters passed in by the code calling the method have been checked or there will be no problem, you don’t need to check the parameters at this time.

8. Annotation protocol

8.1. Comments on classes, class attributes, and class methods must use Javadoc specifications, use /* content /format, and do not use // xxx.

Note: In the IDE editing window, the Javadoc method will prompt related comments, and the generated Javadoc can correctly output the corresponding comments; in the IDE, when the project calls a method, the meaning of the method, parameters, and return value can be suspended without entering the method to improve reading efficiency.

8.2. All abstract methods (including methods in interfaces) must be annotated with Javadoc. In addition to the return value, parameters, and exception descriptions, it must also indicate what the method does and what functions it implements.

Note: Please also explain the implementation requirements for subclasses, or call precautions.

8.3. All classes must add creator and creation date

8.4. For single-line comments inside the method, start a new line above the commented statement and use // comments. Multi-line comments inside the method use /* */ comments, pay attention to align with the code

8.5. All enumeration type fields must have comments, explaining the purpose of each data item

8.6. It is better to use Chinese annotations to explain the problem clearly than "half-handed" English annotations. Proper nouns and keywords can be kept in the original English text.

Counter-example: "TCP connection timeout" is interpreted as "Transmission Control Protocol connection timeout", but it takes brains to understand.

8.7. At the same time as the code is modified, the comments should also be modified accordingly, especially the modification of parameters, return values, exceptions, core logic, etc.

Explanation: The update of the code and comments is not synchronized, just like the update of the road network and the navigation software are not synchronized. If the navigation software lags seriously, the meaning of navigation will be lost.

8.8. Carefully comment out the code. Elaborate above, rather than simply commenting out. Delete if useless.

Explanation: There are two possibilities for the code to be commented out:
the logic of this code will be restored later.
Never use
the former. If there is no remark information, it is difficult to know the motivation of the comment. The latter is recommended to be deleted directly (the code warehouse has already saved the historical code).

8.9. Requirements for Notes

First, it can accurately reflect the design ideas and code logic;
second, it can describe the business meaning, so that other programmers can quickly understand the information behind the code. A large piece of code without comments at all is like a book to the reader. The comments are for yourself. Even after a long time, you can clearly understand the thinking at that time; the comments are also for your successors, so that they can quickly take over. Work.

8.10. Good naming and code structure are self-explanatory, and comments strive to be concise, accurate and expressive. Avoid an extreme of comments: too many and excessive comments, once the logic of the code is modified, it is quite a burden to modify the comments.

反例:
put elephant into fridge put(elephant, fridge);
方法名 put,加上两个有意义的变量名 elephant 和 fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。

8.11、特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。

待办事宜(TODO):(标记人,标记时间,[预计处理时间])
表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。

9、 其它

1)在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。

说明:不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);

2)【强制】velocity 调用 POJO 类的属性时,直接使用属性名取值即可,模板引擎会自动按规范调用 POJO 的 getXxx(),如果是 boolean 基本数据类型变量(boolean 命名不需要加 is 前 缀),会自动调用 isXxx()方法。
说明:注意如果是 Boolean 包装类对象,优先调用 getXxx()的方法。

3)【强制】后台输送给页面的变量必须加$!{var}——中间的感叹号。

说明:如果 var 等于 null 或者不存在,那么${var}会直接显示在页面上。

4) [Mandatory] Note that the Math.random() method returns a double type, and the value range is 0≤x<1 (can get zero value, pay attention to the division by zero exception). If you want to get a random number of integer type, Do not enlarge x by several times of 10 and then round it up, use the nextInt or nextLong method of the Random object directly.

5) [Mandatory] Get the current milliseconds System.currentTimeMillis(); instead of new Date().getTime();

Note: If you want to obtain a more precise nanosecond time value, use the System.nanoTime() method. In JDK8, it is recommended to use the Instant class for scenarios such as statistical time.

6) [Mandatory] When formatting the date, use the lowercase y in the passed pattern to indicate the year.

Explanation: When formatting a date, yyyy represents the year of the day, and the uppercase YYYY represents week in which year (a concept introduced after JDK7), which means the year the week of the day belongs to. A week starts on Sunday and Saturday At the end, as long as this week crosses the new year, the returned YYYY is the next year. Also note:

The uppercase M for the month is the lowercase m
for the minute, the uppercase H
for the 24-hour format, and the lowercase h
for the 12-hour format

Positive example: the format for representing date and time is as follows:
new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);

7) [Recommendation] Do not add any complex logic to the view template.

Explanation: According to the MVC theory, the responsibility of the view is to display, not to grab the work of the model and the controller.

8) [Recommendation] When constructing or initializing any data structure, the size should be specified to avoid the infinite growth of the data structure from eating up the memory.

9) [Recommendation] Clean up unused code segments or configuration information in time.

Note: For junk code or outdated configuration, resolutely clean up to avoid excessive program bloat and redundant code.
Positive example: For code fragments that are temporarily commented out and may be restored later, above the commented code, three slashes (///) are uniformly stipulated to explain the reason for commenting out the code.

10. Abnormal log

1) Exception handling

[Mandatory] The RuntimeException defined in the Java class library that can be avoided by pre-checking should not be handled by catch, such as: NullPointerException, IndexOutOfBoundsException, etc.
Note: Except for exceptions that cannot pass the pre-check, for example, when parsing a number in the form of a string, there may be a number format error, which has to be implemented by catching NumberFormatException.
Positive example: if (obj != null) {…}
Negative example: try { obj.method(); } catch (NullPointerException e) {…}

2) [Mandatory] Do not use exceptions for process control and condition control.

Explanation: The original intention of exception design is to solve various unexpected situations during program operation, and the processing efficiency of exceptions is much lower than that of conditional judgment.

3) [Mandatory] Please distinguish between stable code and unstable code when catching. Stable code refers to code that will not go wrong anyway.

For the catch of unstable code, try to distinguish the exception type as much as possible, and then do the corresponding exception handling.

Explanation: Try-catch a large piece of code, so that the program cannot make a correct stress response according to different exceptions, and it is not conducive to locating the problem. This is an irresponsible performance.
Positive example: In the scenario of user registration, if the user enters illegal characters, or the user name already exists, or the user enters the password too easily, the program will make a classification judgment and prompt the user.

4) [Mandatory] Catching an exception is to handle it, don't catch it but throw it away without processing anything, if you don't want to handle it, please throw the exception to its caller. The outermost business user must handle exceptions and convert them into content that users can understand.

5) [Mandatory] A try block is placed in the transaction code. After the catch exception, if you need to roll back the transaction, you must pay attention to manually roll back the transaction.

6) [Mandatory] The finally block must close the resource object and the stream object, and try-catch if there is an exception.

Note: If JDK7 and above, you can use the try-with-resources method.

7) [Mandatory] Do not use return in finally block.

Explanation: After the return statement in the try block is successfully executed, it does not return immediately, but continues to execute the statement in the finally block. If there is a return statement here, it returns directly here, and the return point in the try block is ruthlessly discarded.
Counter example:

private int x = 0;
public int checkReturn() {
    
     
    try {
    
     
        // x 等于 1,此处不返回 
        return ++x; 
    } finally {
    
    
         // 返回的结果是 2 
         return ++x; 
         } 
    }

8) [Mandatory] The caught exception and the thrown exception must be an exact match, or the caught exception is the parent class of the thrown exception.

Explanation: If the opponent is expected to throw a hydrangea but actually receives a shot put, an accident will occur.

9) [Mandatory] When calling RPC, second-party packages, or related methods of dynamically generated classes, you must use the Throwable class to intercept exceptions.

Description: Call the method through the reflection mechanism. If the method cannot be found, NoSuchMethodException will be thrown. Under what circumstances will NoSuchMethodError be thrown? When the two-party package conflicts in the class, the arbitration mechanism may lead to the introduction of an unexpected version to make the method signature of the class mismatch, or when the bytecode modification framework (such as: ASM) dynamically creates or modifies the class, the corresponding method signature is modified. . In these cases, even if the code is correct during compilation, NoSuchMethodError will be thrown when the code is running.

10) [Recommendation] The return value of the method can be null, and it is not mandatory to return an empty collection or empty object, etc. An annotation must be added to fully explain under what circumstances a null value will be returned.

Note: This manual makes it clear that preventing NPE is the responsibility of the caller. Even if the called method returns an empty collection or an empty object, it is not a peace of mind for the caller, and must consider the situation of returning null in scenarios such as remote call failure, serialization failure, and runtime exception.

10.11. Preventing NPE is the basic cultivation of programmers. Pay attention to the scenarios where NPE occurs:

The return type is a basic data type. When return wraps an object of a data type, automatic unboxing may generate NPE.
Counter example:

public int f() {
    
     
       return Integer 对象
 }

If it is null, automatically unbox and throw NPE.

The query result of the database may be null.
Even if the elements in the collection are NotEmpty, the retrieved data elements may be null.
When a remote call returns an object, a null pointer judgment is required to prevent NPE.
For the data obtained in the Session, it is recommended to perform NPE checks to avoid null pointers.
Cascading calls to obj.getA().getB().getC(); a series of calls are prone to NPE.

Positive example: Use JDK8's Optional class to prevent NPE problems.

10.12. Distinguish between unchecked and checked exceptions when defining, avoid throwing new RuntimeException() directly, let alone throwing Exception or Throwable, and use custom exceptions with business meaning. Recommend custom exceptions that have been defined in the industry, such as: DAOException / ServiceException, etc.

10.13. The "error code" must be used for the http/api open interface outside the company; it is recommended to throw exceptions inside the application; the RPC call between applications is given priority to use the Result method, and encapsulates the isSuccess() method, "error code", "error Brief information".

Explanation: The reason for using the Result method for the return method of the RPC method:
If the return method of throwing an exception is used, if the caller does not catch it, a runtime error will occur.
If you don't add stack information, just create a new custom exception and add your own error message, it won't help the caller to solve the problem much. If stack information is added, in the case of frequent call errors, the performance loss of data serialization and transmission is also a problem.

10.14. Avoid repeated codes (Don't Repeat Yourself), that is, the DRY principle.

Note: Copying and pasting the code at will will inevitably lead to duplication of the code. When you need to modify it in the future, you need to modify all copies, which is easy to miss. Extract common methods when necessary, or abstract public classes, or even componentize.
Positive example: There are multiple public methods in a class, and several lines of the same parameter verification operation are required. At this time, please extract:

private boolean checkParam(DTO dto) {
    
    ...}

11. Log protocol

11.1. The application cannot directly use the API in the log system (Log4j, Logback), but should rely on the API in the log framework SLF4J, and use the log framework of the facade mode, which is conducive to maintenance and unification of the log processing methods of each class.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class); 

11.2. All log files are kept for at least 15 days, because some abnormalities have the characteristics of "weekly" occurrence frequency. Network operation status, security-related information, system monitoring, management background operations, and user-sensitive operations need to retain relevant network logs for no less than 6 months.

11.3. The naming method of extended logs in the application (such as RBI, temporary monitoring, access logs, etc.):

appName_logType_logName.log. logType: log type, such as stats/monitor/access, etc.; logName: log description. The advantage of this kind of naming: through the file name, you can know what application, type, and purpose the log file belongs to, and it is also conducive to classification and search.

Note: It is recommended to classify the logs, such as storing error logs and business logs separately, which is convenient for developers to view and monitor the system in time through logs.
Positive example: Monitor time zone conversion exceptions separately in force-web applications, such as: force_web_timeZoneConvert.log

11.4. When outputting logs, the splicing between string variables uses placeholders.

Note: Because the concatenation of String strings will use the append() method of StringBuilder, there is a certain performance loss. Using placeholders is only a replacement action, which can effectively improve performance.
Positive example: logger.debug("Processing trade with id: {} and symbol: {}", id, symbol);

11.5. For the log output of trace/debug/info level, the switch judgment of the log level must be carried out.

Explanation: Although the first line of code isDisabled(Level.DEBUG_INT) in the method body of debug(parameter) is true (the common implementations of Slf4j, Log4j and Logback), it returns directly, but the parameter may be concatenated with strings. In addition, if there is a getName() method call in the parameter such as debug(getName()), it is unnecessary to waste the overhead of the method call.

Positive example:
if the judgment is true, then you can output trace and debug level logs if (logger.isDebugEnabled()) { logger.debug("Current ID is: {} and name is: {}", id, getName() );}

11.6. To avoid repeatedly printing logs and wasting disk space, be sure to set additivity=false in log4j.xml.

Positive example:

11.7. Abnormal information should include two types of information: crime scene information and abnormal stack information. If it is not processed, it will be thrown up through the keyword throws.

Positive example: logger.error(various parameters or objects toString() + “_” + e.getMessage(), e);

11.8. Log carefully. It is forbidden to output debug logs in the production environment; selectively output info logs; if you use warn to record business behavior information when you first go online, you must pay attention to the problem of log output to avoid bursting the server disk, and remember to delete these observation logs in time .

Explanation: Outputting a large number of invalid logs is not conducive to improving system performance and is not conducive to quickly locating error points. When recording logs, please think: Are these logs really read? What can you do when you see this log? Could it be beneficial for troubleshooting?

11.9. You can use the warn log level to record the wrong input parameters of the user, so as to avoid confusion when the user complains. If it is not necessary, please do not enter the error level in this scene to avoid frequent alarms.

Note: Pay attention to the level of log output, the error level only records system logic errors, exceptions or important error messages.

11.10. Try to use English to describe the log error information. If the error information in the log is not clearly described in English, use Chinese description, otherwise it is easy to cause ambiguity.

Due to character set problems, internationalized teams or servers deployed overseas use full English to annotate and describe log error messages.

12. Unit testing

1) [Mandatory] A good unit test must comply with the AIR principles.

Explanation: When the unit test is running online, it feels like air (AIR) does not exist, but it is very critical to ensure the quality of the test. Macroscopically speaking, a good unit test has the characteristics of automation, independence, and repeatability.

A: Automatic (automation)
I: Independent (independence)
R: Repeatable (repeatable)

2) [Mandatory] Unit tests should be executed fully automatically and non-interactively. Test cases are usually executed on a regular basis, and the execution process must be fully automated to make sense. A test whose output requires human inspection is not a good unit test. System.out is not allowed to be used for human verification in unit tests, and assert must be used for verification.

3) [Mandatory] Keep unit tests independent. In order to ensure that unit tests are stable, reliable and easy to maintain, unit test cases must not call each other, nor can they rely on the order of execution.

Counter-example: method2 needs to rely on the execution of method1, and uses the execution result as the input of method2.

4) [Mandatory] Unit tests can be executed repeatedly and cannot be affected by the external environment.

Description: Unit tests are usually placed in continuous integration, and unit tests will be executed every time there is a code check in. If the unit test depends on the external environment (network, service, middleware, etc.), it will easily lead to the unavailability of the continuous integration mechanism.
Positive example: In order not to be affected by the external environment, it is required to change the dependency of the SUT to injection when designing the code, and use a DI framework such as spring to inject a local (memory) implementation or Mock implementation during testing.

5) [Mandatory] For unit tests, ensure that the test granularity is small enough to help pinpoint problems. Single measurement granularity is at most class level, usually method level.

Note: Only when the test granularity is small can the error location be located as soon as possible when an error occurs. Unit tests are not responsible for checking cross-class or cross-system interaction logic, which is the domain of integration testing.

6) [Mandatory] Incremental codes for core business, core applications, and core modules ensure that unit tests pass.

Note: Add new code to supplement the unit test in time. If the new code affects the original unit test, please correct it in time.

7) [Mandatory] The unit test code must be written in the following project directory: src/test/java, and it is not allowed to be written in the business code directory.

Note: This directory will be skipped during source code compilation, and the unit test framework scans this directory by default.

8) [Recommendation] The basic goal of unit testing: the statement coverage rate reaches 70%; the statement coverage rate and branch coverage rate of the core module must reach 100%

Explanation: The DAO layer, Manager layer, and highly reusable Service mentioned in the application layer of the project specification should all be unit tested.

9) [Recommendation] Write unit test codes to comply with the BCDE principle to ensure the delivery quality of the tested module.

B: Border, boundary value test, including loop boundaries, special values, special time points, data order, etc.
C: Correct, correct input, and get the expected result.
D: Design, combined with design documents to write unit tests.
E: Error, force the input of error information (such as: illegal data, abnormal process, business permission, etc.), and get the expected result.

10) [Recommendation] For database-related query, update, delete and other operations, you cannot assume that the data in the database exists, or directly operate the database to insert data into it. Please use the program to insert or import data to prepare the data.

Counter-example: In the unit test for deleting a certain row of data, in the database, first manually add a row as the deletion target, but the newly added data in this row does not conform to the business insertion rules, resulting in abnormal test results.

11) [Recommendation] For unit tests related to the database, an automatic rollback mechanism can be set to avoid dirty data to the database. Or have a clear prefix and suffix identification for the data generated by the unit test.

Positive example: In the internal unit test of the Enterprise Intelligence Division, the prefix ENTERPRISE_INTELLIGENCE _UNIT_TEST_ is used to identify the unit test-related code.

12)【推荐】对于不可测的代码在适当的时机做必要的重构,使代码变得可测,避免为了达到测试要求而书写不规范测试代码。

13)【推荐】在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例。

14)【推荐】单元测试作为一种质量保障手段,在项目提测前完成单元测试,不建议项目发布后补充单元测试用例。

15)【参考】为了更方便地进行单元测试,业务代码应避免以下情况:

构造方法中做的事情过多。
存在过多的全局变量和静态方法。
存在过多的外部依赖。
存在过多的条件语句。

说明:多层条件语句建议使用卫语句、策略模式、状态模式等方式重构。

16)【参考】不要对单元测试存在如下误解:

那是测试同学干的事情。本文是开发手册,凡是本文内容都是与开发同学强相关的。
单元测试代码是多余的。系统的整体功能与各单元部件的测试正常与否是强相关的。
单元测试代码不需要维护。一年半载后,那么单元测试几乎处于废弃状态。
单元测试与线上故障没有辩证关系。好的单元测试能够最大限度地规避线上故障。

13、安全规约

13.1、隶属于用户个人的页面或者功能必须进行权限控制校验。

说明:防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容、修改他人的订单。

13.2、用户敏感数据禁止直接展示,必须对展示数据进行脱敏。

说明:中国大陆个人手机号码显示为:137****0969,隐藏中间 4 位,防止隐私泄露。

13.3、用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库。

13.4、用户请求传入的任何参数必须做有效性验证。

说明:忽略参数校验可能导致:
page size 过大导致内存溢出
恶意 order by 导致数据库慢查询
任意重定向
SQL 注入
反序列化注入
正则输入源串拒绝服务 ReDoS
说明:Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。

13.5、禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。

13.6、表单、AJAX 提交必须执行 CSRF 安全验证。

说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在 CSRF 漏洞的应用/网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户不知情的情况下对数据库中用户参数进行相应修改。

13.7、在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放的 机制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。

说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。

13.8、发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略。

14、MySQL 数据库

14.1、建表规约

表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在设置从 is_xxx 到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。

14.2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。

14.3、 说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。

正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name

14.4、 表名不使用复数名词。

说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合表达习惯。

14.5、禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。

14.6、主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。

说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。

14.7、小数类型为 decimal,禁止使用 float 和 double。

Explanation: When storing, both float and double have the problem of loss of precision, and it is very likely that incorrect results will be obtained when comparing values. If the range of stored data exceeds the range of decimal, it is recommended to split the data into integers and decimals and store them separately.

8) [Mandatory] If the stored strings are almost equal in length, use the char fixed-length string type.

9) [Mandatory] varchar is a variable-length string, does not pre-allocate storage space, and the length should not exceed 5000. If the storage length is greater than this value, define the field type as text, create an independent table, and use the primary key to correspond to avoid impact Other field index efficiency.

10) [Mandatory] The table must have three fields: id, create_time, update_time.

Note: The id must be the primary key, the type is bigint unsigned, auto-increment for a single table, and the step size is 1. The types of create_time and update_time are both datetime types.

11) [Recommendation] The naming of the table should preferably follow the "business name_function of the table".

正例:alipay_task / force_project / trade_config

12) [Recommendation] The library name should be as consistent as possible with the application name.

13) [Recommendation] If you modify the meaning of the field or add the state represented by the field, you need to update the field comment in time.

14) [Recommendation] Fields allow appropriate redundancy to improve query performance, but data consistency must be considered. Redundant fields should follow:

Not a frequently modified field.
It is not a varchar super long field, let alone a text field.
Fields that are not uniquely indexed.

Positive example: The product category name is used frequently, the field length is short, and the name is basically unchanged. The category name can be stored redundantly in the associated table to avoid associated queries.

15) [Recommendation] Only when the number of rows in a single table exceeds 5 million rows or the capacity of a single table exceeds 2GB, it is recommended to split databases and tables.
Note: If the expected data volume in three years will not reach this level at all, please do not divide the database into tables when creating tables.

16) [Reference] Appropriate character storage length not only saves database table space and index storage, but more importantly, improves retrieval speed.

Positive example: the following table, in which unsigned values ​​can avoid mistakenly storing negative numbers, and expand the range of representation.

Object Age Interval Type Byte Representation Range
Person within 150 years old tinyint unsigned 1 unsigned value: 0 to 255

turtle centuries smallint unsigned 2 unsigned value: 0 to 65535

Dinosaur fossil tens of millions of years int unsigned 4 unsigned value: 0 to about 4.29 billion

Sun about 5 billion years bigint unsigned 8 Unsigned value: 0 to about 10 to the 19th power

14.2, index specification

1) [Mandatory] Fields with unique characteristics in business, even if they are a combination of multiple fields, must be built into a unique index.

Note: Don’t think that the unique index affects the insert speed. This speed loss can be ignored, but it is obvious to improve the search speed; in addition, even if a very complete verification control is done at the application layer, as long as there is no unique index, according to Murphy’s law, There must be dirty data generated.

2) [Mandatory] Joining is prohibited for more than three tables. The data types of the fields that need to be joined must be absolutely consistent; when multi-table association queries, ensure that the associated fields need to have indexes.

Note: Even if you join two tables, you should pay attention to table indexes and SQL performance.

3) [Mandatory] When creating an index on a varchar field, the index length must be specified. It is not necessary to create an index for all fields, and the index length can be determined according to the actual text differentiation.

Note: The length and discrimination of the index are a pair of contradictions. Generally, for string type data, the index with a length of 20 will have a discrimination of over 90%. You can use count(distinct left(column name, index length))/ count(*) to determine the degree of discrimination.

4) [Mandatory] It is strictly forbidden to search the page with left blur or full blur. If necessary, please use the search engine to solve it.

Explanation: The index file has the leftmost prefix matching characteristic of B-Tree, if the left value is not determined, then this index cannot be used.

5) [Recommendation] If there is an order by scene, please pay attention to the orderliness of the index. The last field of order by is part of the combined index and placed at the end of the index combination order to avoid file_sort and affect query performance.

正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。

6)【推荐】利用覆盖索引来进行查询操作,避免回表。

说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效果,用 explain 的结果,extra 列会出现:using index。

7)【推荐】利用延迟关联或者子查询优化超多分页场景。

说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
正例:先快速定位需要获取的 id 段,然后再关联:

8)SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。

说明:

There is at most one matching row (primary key or unique index) in a consts single table, and the data can be read during the optimization phase.
ref refers to using a normal index.
range Performs a range search on the index.
Counter-example: In the result of the explain table, type=index, full scan of the index physical file, the speed is very slow, the index level is lower than the range, and it is nothing compared to the full table scan.

9) [Recommendation] When building a combined index, the one with the highest degree of discrimination is on the far left.
Positive example: if where a=? and b=? , if column a is almost unique, you only need to build the idx_a index.

Note: When there is a mixture of non-equal signs and equal signs, please prepend the columns of the equal sign conditions when building an index. For example: where c>? and d=? Even if c has a higher degree of discrimination, d must be placed at the forefront of the index, that is, the index idx_d_c.

10) [Recommendation] Prevent the implicit conversion caused by different field types from causing the index to fail.

[Reference] Avoid the following extreme misunderstandings when creating an index:

Rather abuse than lack. Think that a query needs to build an index.
Rather lack than abuse. It is believed that indexes consume space and seriously slow down the updating of records and the speed of adding new rows.
Resist unique indexes. It is believed that the uniqueness of the business needs to be solved in the application layer by "check first and insert later".

14.3. SQL statement

1) [Mandatory] Do not use count (column name) or count (constant) to replace count(). count() is a standard syntax for counting rows defined by SQL92, and has nothing to do with the database, NULL or non-NULL.

Explanation: count(*) will count rows whose value is NULL, but count (column name) will not count rows whose value is NULL.

2) [Mandatory] count(distinct col) calculates the number of unique rows except NULL in this column, pay attention to count(distinct col1, col2) If one of the columns is all NULL, even if the other column has a different value, it will return as 0.

3) [Mandatory] When the values ​​of a column are all NULL, the return result of count(col) is 0, but the return result of sum(col) is NULL, so pay attention to the NPE problem when using sum().

Positive example: Use the following method to avoid the NPE problem of sum: SELECT IFNULL(SUM(column), 0) FROM table;

4) [Mandatory] Use ISNULL() to determine whether it is a NULL value.

Explanation: A direct comparison of NULL to any value is NULL.

NULL<>NULL returns NULL, not false.
NULL=NULL returns NULL, not true.
NULL<>1 returns NULL, not true.

5) [Mandatory] When writing the paging query logic in the code, if the count is 0, it should return directly to avoid executing the subsequent paging statement.

[Mandatory] Foreign keys and cascading are not allowed, and all foreign key concepts must be resolved at the application layer.

Explanation: Taking the relationship between students and grades as an example, the student_id in the student table is the primary key, and the student_id in the grade table is the foreign key. If the student_id in the student table is updated and the student_id update in the grade table is triggered at the same time, it is a cascading update. Foreign keys and cascading updates are suitable for single machines with low concurrency, but not for distributed, high-concurrency clusters; cascading updates are strongly blocked, and there is a risk of database update storms; foreign keys affect the insertion speed of the database.

6) [Mandatory] The use of stored procedures is prohibited. Stored procedures are difficult to debug and expand, and they are not portable.

7) [Mandatory] When correcting data (especially deleting and modifying record operations), you must select first to avoid accidental deletion, and the update statement can only be executed after confirming that it is correct.

8)【推荐】in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。

9)【参考】如果有国际化需要,所有的字符存储与表示,均以 utf-8 编码,注意字符统计函数的区别。

说明:
SELECT LENGTH(“轻松工作”); 返回为 12
SELECT CHARACTER_LENGTH(“轻松工作”); 返回为 4
如果需要存储表情,那么选择 utf8mb4 来进行存储,注意它与 utf-8 编码的区别。

10)【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句。

说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

14.4、ORM 映射

1)【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

说明:

增加查询分析器解析成本。
增减字段容易与 resultMap 配置不一致。
无用字段增加网络消耗,尤其是 text 类型的字段。

2)【强制】POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性之间的映射。

说明:参见定义 POJO 类以及数据库字段定义规定,在中增加映射,是必须的。在 MyBatis Generator 生成的代码中,需要进行对应的修改。

3)【强制】不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义;反过来,每一个表也必然有一个 POJO 类与之对应。

说明:配置映射关系,使字段与 DO 类解耦,方便维护。

4) [Mandatory] Use of sql.xml configuration parameters: #{}, #param# Do not use ${} This way is prone to SQL injection.

[Mandatory] The queryForList(String statementName, int start, int size) that comes with iBATIS is not recommended.

Explanation: Its implementation is to get all the records of the SQL statement corresponding to statementName in the database, and then get the sub-set of start and size through subList.
Positive example:
Map<String, Object> map = new HashMap<>();
map.put(“start”, start);
map.put(“size”, size);

5) [Mandatory] It is not allowed to directly use HashMap and Hashtable as the output of the query result set.

Explanation: resultClass="Hashtable", the field name and attribute value will be placed, but the type of the value is uncontrollable.

6) [Mandatory] When updating a data table record, the value of the gmt_modified field corresponding to the record must be updated at the same time as the current time.

[Recommendation] Do not write a large and comprehensive data update interface. If it is passed in as a POJO class, no matter whether it is your own target update field, update table set c1=value1,c2=value2,c3=value3; This is wrong. When executing SQL, do not update fields that have not been changed. First, it is error-prone; second, it is inefficient; third, it increases binlog storage.

7) [Reference] Do not abuse @Transactional transactions. Transactions will affect the QPS of the database. In addition, where transactions are used, various rollback schemes need to be considered, including cache rollback, search engine rollback, message compensation, and statistical correction.

8) The compareValue in [Reference] is a constant compared with the attribute value, usually a number, which means that the condition is carried when it is equal; it means that it is not empty and not null; it means that it is not a null value.

15. Engineering structure

15.1, application layering

1) [Recommendation] In the figure, the upper layer depends on the lower layer by default, and the arrow relationship indicates that it can be directly relied on. For example, the open interface layer can depend on the Web layer or the Service layer directly, and so on:

Open interface layer: It can directly encapsulate the Service method and expose it as an RPC interface; encapsulate it as an http interface through the Web; perform gateway security control, flow control, etc.
Terminal display layer: The template rendering and display layer of each terminal. Currently, it is mainly velocity rendering, JS rendering, JSP rendering, mobile terminal display, etc.
Web layer: mainly for forwarding of access control, verification of various basic parameters, or simple processing of non-reusable services, etc.
Service layer: relatively specific business logic service layer.

Manager layer: general business processing layer, which has the following characteristics:

For the layer encapsulated by the third-party platform, preprocess the returned result and convert the abnormal information.
The sinking of the general capabilities of the Service layer, such as caching solutions and general processing of middleware.
Interact with the DAO layer and reuse the combination of multiple DAOs.

DAO layer: Data access layer, which interacts with the underlying MySQL, Oracle, Hbase, etc. for data.
External interfaces or third-party platforms: including RPC open interfaces of other departments, basic platforms, and HTTP interfaces of other companies.

2) [Reference] (Hierarchical Exception Handling Protocol) In the DAO layer, there are many types of exceptions that cannot be caught with fine-grained exceptions. Use the catch(Exception e) method and throw new DAOException(e), which is not required Print the log, because the log must be captured and printed to the log file at the Manager/Service layer. If the log is printed on the same server, performance and storage will be wasted. When an exception occurs at the Service layer, the error log must be recorded to the disk, and the parameter information should be carried as much as possible, which is equivalent to protecting the crime scene. If the Manager layer and Service are deployed on the same machine, the log processing method is consistent with that of the DAO layer. If it is deployed separately, the processing method is consistent with that of Service. The web layer should never continue to throw exceptions, because it is already at the top layer. If you realize that this exception will cause the page to fail to render normally, you should jump directly to a friendly error page with error messages that are easy for users to understand. The open interface layer should handle exceptions as error codes
and error messages.

3) [Reference] Hierarchical Domain Model Specification:

DO (Data Object): This object corresponds to the database table structure one by one, and the data source object is transmitted upward through the DAO layer.
DTO (Data Transfer Object): data transfer object, the object that Service or Manager transfers out.
BO (Business Object): Business object, an object that encapsulates business logic output by the Service layer.
AO (Application Object): Application object, an abstract reuse object model between the Web layer and the Service layer, very close to the presentation layer, and the degree of reuse is not high.
VO (View Object): Display layer object, usually the object transmitted from the Web to the template rendering engine layer.
Query: data query object, each layer receives the query request from the upper layer. Pay attention to the query encapsulation with more than 2 parameters, it is forbidden to use the Map class to transfer.

15.2. Second-party library dependencies

1) [Mandatory] Define GAV to comply with the following rules:

GroupID format: com.{Company/BU}.Business Line[.Sub-Business Line], up to 4 levels.

Description: {company/BU} For example: alibaba/taobao/tmall/aliexpress and other BU level; sub-business line is optional.
Positive example: com.taobao.jstorm or com.alibaba.dubbo.register

ArtifactID format: product line name-module name. Semantics are not repeated or omitted, first go to the central warehouse to check.

正例:dubbo-client / fastjson-api / jstorm-tool

Version: Refer to the following for detailed regulations.

2) [Mandatory] Second-party library version number naming method: major version number. minor version number. revision number

Major version number: product direction changes, or large-scale API incompatibility, or architecture incompatible upgrade.
Minor version number: maintain relative compatibility, add major functional features, and make API incompatible changes with minimal impact.
Revision number: Maintain full compatibility, fix bugs, add minor features, etc.

Note: Note that the initial version number must be: 1.0.0, not 0.0.1. The officially released class library must first go to the central warehouse for verification, so that the version number has continuity, and the official version number is not allowed to be overwritten and upgraded. If the current version: 1.3.3, then the next reasonable version number: 1.3.4 or 1.4.0 or 2.0.0

3) [Mandatory] Online applications do not depend on the SNAPSHOT version (except security packages).

Note: Not relying on the SNAPSHOT version is to ensure the idempotency of the application release. In addition, it can also speed up the packaging and building at compile time.

4) [Mandatory] When adding or upgrading the second-party library, keep the arbitration results of other jar packages except function points unchanged. If there is a change, it must be explicitly assessed and verified.

Note: During the upgrade, compare the information before and after dependency:resolve. If the arbitration results are completely inconsistent, use the dependency:tree command to find out the difference and exclude the jar package.

5)【强制】二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。

6)【强制】依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。

说明:依赖 springframework-core,-context,-beans,它们都是同一个版本,可以定义一个变量来保存版本:${spring.version},定义依赖的时候,引用该版本。

7)【强制】禁止在子项目的 pom 依赖中出现相同的 GroupId,相同的 ArtifactId,但是不同的 Version。

说明:在本地调试时会使用各子项目指定的版本号,但是合并成一个 war,只能有一个版本号出现在最后的 lib 目录中。可能出现线下调试是正确的,发布到线上却出故障的问题。

8)【推荐】底层基础技术框架、核心数据管理平台、或近硬件端系统谨慎引入第三方实现。

9)【推荐】所有 pom 文件中的依赖声明放在语句块中,所有版本仲裁放在语句块中。

说明:里只是声明版本,并不实现引入,因此子项目需要显式的声明依赖,version 和 scope 都读取自父 pom。而所有声明在主 pom 的里的依赖都会自动引入,并默认被所有的子项目继承。

10)【推荐】二方库不要有配置项,最低限度不要再增加配置项

11)【参考】为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则:

精简可控原则。移除一切不必要的 API 和依赖,只包含 Service API、必要的领域模型对象、Utils 类、常量、枚举等。如果依赖其它二方库,尽量是 provided 引入,让二方库使用者去依赖具体版本号; 无 log 具体实现,只依赖日志框架。

稳定可追溯原则。每个版本的变化应该被记录,二方库由谁维护,源码在哪里,都需要能方便查到。除非用户主动升级版本,否则公共二方库的行为不应该发生变化。

16、服务器

16.1、高并发服务器建议调小 TCP 协议的 time_wait 超时时间。

Note: The operating system will close the connection in the time_wait state after 240 seconds by default. Under high concurrent access, the server may not be able to establish a new connection because there are too many connections in the time_wait state, so it needs to be adjusted on the server. wait value.
Positive example: on the linux server, please modify the default value (seconds) by changing the /etc/sysctl.conf file:
net.ipv4.tcp_fin_timeout = 30

16.2. Increase the maximum number of file handles supported by the server (File Descriptor, abbreviated as fd).

Note: The mainstream operating system is designed to manage TCP/UDP connections in the same way as files, that is, one connection corresponds to one fd. The default maximum number of fds supported by mainstream linux servers is 1024. When the number of concurrent connections is large, it is easy to cause "open too many files" error due to insufficient fds, resulting in the failure to establish new connections. It is recommended to increase the maximum number of handles supported by the linux server several times (related to the amount of memory in the server).

16.3. Set the -XX:+HeapDumpOnOutOfMemoryError parameter to the JVM environment parameter, so that the JVM will output dump information when encountering an OOM scene.

Explanation: The occurrence of OOM is probable, and even one case occurs only after a few months. The information in the heap when the error occurs is very helpful to solve the problem.

16.4. In the online production environment, the Xms and Xmx of the JVM are set to the same memory capacity to avoid the pressure caused by adjusting the heap size after GC.

16.5. [Reference] The internal redirection of the server uses forward; the external redirection address uses the URL assembly tool class to generate, otherwise it will bring the problem of inconsistent URL maintenance and potential security risks.

17. Design specification

17.1. The storage scheme and the design of the underlying data structure have been unanimously approved by the review and deposited into a document.

Explanation: Defective underlying data structures can easily lead to increased system risk, reduced scalability, and a steep increase in reconstruction costs due to historical data migration and smooth system transition. Therefore, storage solutions and data structures need to be carefully designed and reviewed , after the production environment is submitted for execution, a double check is required.
Positive example: The content of the review includes the selection of storage media, whether the table structure design can meet the technical solution, whether the access performance and storage space can meet the business development, the dialectical relationship between tables or fields, field names, field types, indexes, etc.; Data structure changes (such as adding new fields in the original table) also need to be reviewed and approved before going online.

17.2. In the requirement analysis stage, if there are more than one category of User interacting with the system and more than five related User Cases, use a use case diagram to express clearer structured requirements.

17.3. If there are more than 3 states of a certain business object, use the state diagram to express and clarify each trigger condition of the state change.

Explanation: The core of the state diagram is the object state. First, specify how many states the object has, and then specify whether there is a direct transition relationship between the two states, and then specify the conditions that trigger the state transition.
Positive example: Taobao order status includes order placed, pending payment, paid, pending shipment, shipped, received, etc. For example, it is impossible to have a direct conversion relationship between the two states of order placed and goods received.

17.4. If there are more than 3 objects involved in the calling link of a certain function in the system, use a sequence diagram to express and clarify the input and output of each calling link.

Explanation: The sequence diagram reflects the interaction and collaboration relationship between a series of objects, and clearly and three-dimensionally reflects the invocation depth link of the system.

17.5. If there are more than 5 model classes in the system and there are complex dependencies, use class diagrams to express and clarify the relationship between classes.

Explanation: The construction drawings in the field of image-like architecture may not be needed if you build a bungalow, but if you build an ant Z space building, you will definitely need detailed construction drawings.

17.6. If there is a collaborative relationship between more than 2 objects in the system, and complex processing procedures need to be represented, use activity diagrams to represent them.

Explanation: The activity diagram is an extension of the flow chart, which adds an object swimlane that can reflect the collaborative relationship, and supports the representation of concurrency, etc.

17.7. Requirements analysis and system design While considering the main function, it is necessary to fully evaluate the abnormal process and business boundary.

Counter-example: During the payment process of Taobao, the bank deducts the payment successfully and sends the user a successful deduction SMS, but when Alipay deposits the payment due to an abnormality due to a network disconnection drill, the Taobao order page still shows that the payment has not been made, causing the user to complain.

17.8. The design and implementation of classes should conform to the single principle.

Explanation: The single principle is the easiest to understand but the most difficult to implement. As the system evolves, many times, the original intention of the class design is forgotten.

17.9. Use inheritance carefully for extension, and use aggregation/combination first.

Explanation: If you have to use inheritance as a last resort, you must comply with the Liskov substitution principle. This principle says that subclasses must appear where the parent class can appear, for example, "hand over the money", and the subclasses of money, such as dollars, euros, and renminbi, can all appear. can appear.

17.10. When designing the system, according to the principle of dependency inversion, try to rely on abstract classes and interfaces, which is conducive to expansion and maintenance.

Description: Low-level modules rely on the abstraction of high-level modules to facilitate decoupling between systems.

17.11. When designing the system, pay attention to opening for expansion and closing for modification.

Explanation: In extreme cases, the code delivered to the online production environment cannot be modified, and changes in requirements within the same business domain are realized through module or class extensions.

17.12. In the system design stage, public modules, public configurations, public classes, public methods, etc. are extracted from common services or public behaviors to avoid duplication of code or configuration.

Explanation: As the number of repetitions of the code continues to increase, the maintenance cost increases exponentially.

17.13. Avoid the following misconceptions: agile development = storytelling + coding + release.

Explanation: Agile development is to quickly deliver an iteratively available system, omitting redundant design solutions, and abandoning the traditional approval process, but the necessary design and document accumulation on the core key points are required.

Counter-example: For a team to develop rapidly, agility has become an excuse for the product manager to push the progress. The system is barely runnable but noodle-like code, with extremely poor maintainability and scalability. After one year, it has to be implemented. Large-scale refactoring is not worth the candle.

17.14. The main purpose of system design is to clarify requirements, straighten out logic, and maintain later, and the secondary purpose is to guide coding.

Note: Avoid designing for the sake of design. System design documents are helpful for later system maintenance and reconstruction, so the design results need to be classified and archived.

17.15. The essence of design is to identify and express system difficulties, find system change points, and isolate change points.

Explanation: The purpose of many design patterns in the world is the same, that is, to isolate system change points.

17.16. Purpose of system architecture design:

Determine system boundaries. Determine the technical do's and don'ts of the system.
Determine the relationships between modules within the system. Determine the dependencies between modules and the macro input and output of modules.
Identify principles to guide subsequent design and evolution. Make the subsequent subsystem or module design continue to evolve within the prescribed framework.
Identify non-functional requirements. Non-functional requirements refer to security, usability, scalability, etc.

17.17. When designing barrier-free products, it is necessary to consider:

All interactive control elements must be able to be focused by the tab key, and the focus order must conform to the natural operation logic.
Verification codes used for login verification and request interception must provide methods other than graphic verification.
The custom control type needs to specify the interaction method.

17.18. Explanation of proper terms

POJO (Plain Ordinary Java Object): In this manual, POJO refers to simple classes with only setter / getter / toString, including DO/DTO/BO/VO, etc.
GAV (GroupId, ArtifactctId, Version): Maven coordinates, which are used to uniquely identify the jar package.
OOP (Object Oriented Programming): This manual generally refers to the programming methods of classes and objects.
ORM (Object Relation Mapping): object relational mapping, the conversion between the object domain model and the underlying data, this article refers to iBATIS, mybatis and other frameworks.
NPE (java.lang.NullPointerException): Null pointer exception.
SOA (Service-Oriented Architecture): Service-oriented architecture, which can distribute, combine and use loosely coupled coarse-grained application components through the network according to requirements, which is conducive to improving component reusability and maintainability.
IDE (Integrated Development Environment): An application program used to provide a program development environment, generally including tools such as code editor, compiler, debugger, and graphical user interface. This "Manual" generally refers to IntelliJ IDEA and eclipse.
OOM (Out Of Memory): Originated from java.lang.OutOfMemoryError, when the JVM does not have enough memory to allocate space for the object and the garbage collector cannot reclaim the space, a serious situation occurs in the system.
One-party library: the library (jar package) that the internal sub-project modules of this project depend on.
Second-party library: The library (jar package) that is released to the central warehouse within the company and can be relied upon by other applications within the company.
Three-party library: open source library (jar package) outside the company.

Guess you like

Origin blog.csdn.net/chuige2013/article/details/127193250