概述
文章目录
- 官方文档
- 一、编程规约
- 命名风格
- 通用
- 包名
- 类名
- 接口
- 方法名、参数名、成员变量、局部变量
- 数组
- 枚举
- 总结
- 常量
- 命名
- 定义
- POJO
- 定义
- 命名
- 初始化
- 代码格式
- OOP规约
- 方法
- 数据类型
- 初始化
- 比较
- 日期时间
- 集合处理
- 并发处理
- 初始化与销毁
- 加解锁方式
- 锁的选型
- 线程安全
- 控制语句
- 注释规约
- 前后端规约
- 定义
- 传输
- 限制
- 其他
- 二、异常日志
- 错误码
- 异常处理
- finally
- 日志规约
- 三、单元测试
- 四、安全规约
- 五、MySQL 数据库
- ORM映射
- 六、工程结构
- 服务器
- 七、设计规约
- 无障碍产品设计
- 专有名词
- 总结
官方文档
- github
一、编程规约
命名风格
通用
- 不能以
_
或$
开始及结束。 - 使用正确的英文拼写和语法,禁止不规范的缩写,禁止使用拼音。
- 禁止歧视性词语:
- blackList → to →blockList
- whiteList → to →allowList
- slave → to →replica
- master → to →main/default
在美国各州的BlackLivesMatter(黑人的命同等重要)运动中,Google浏览器Chrome为了表示对反种族歧视运动的支持,未来将不再使用“Blacklist”(黑名单)、“Whitelist”(白名单)等词,改为“Blocklist”(禁止名单)与“Allowlist”(通行名单),以避开含有种族歧视的暗示。Google Chrome的开发团队在官方代码样式指南中加入《如何编写种族中立代码》的条文,提醒Chrome的开发人员应避免使用“Blacklist”、“Whitelist”等词,改用中性词汇如“Blocklist”和“Allowlist”。团队指出,Blacklist、Whitelist这类词汇长期使用下来,会让用户产生「黑=坏」、「白=好」的观念,增强种族歧视的概念。这项变动甚至涉及2000处的内部代码。
包名
使用小写,.
之间仅有一个自然语义的英语单词,使用单数形式(此规则是参考spring的框架结构)。
例:com.alibaba.app.util.MessageUtils
。
类名
- 使用UpperCamelCase(大驼峰命名法),BO、PO、VO、DTO等领域模型例外。
- 抽象类使用Abstract或Base开头,异常类使用Exception结尾,测试类使用“它要测试的类的名称+Test”。
- 可用复数形式。
接口
- 方法和属性不加任何修饰符号,保持代码的简洁性。
- 基于SOA的理念的Service和DAO类,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别,例如:
- 若是形容能力的接口名称,取对应的形容词为接口名,通常是以able为词根的形容词,例如:AbstractTranslator实现Translatable接口。
方法名、参数名、成员变量、局部变量
- 使用lowerCamelCase(小驼峰命名法)。
- 禁止子父类的成员变量之间或不同代码块的局部变量之间采用完全相同的命名,这会使可读性降低,构造函数及“getter/setter”除外:
反例:
public class ConfusingName {
public int stock;
public void get(String alibaba) {
if (condition) {
final int money = 666;
}
for (int i = 0; i < 10; i++) {
// 反例1
final int money = 15978;
}
}
}
class Son extends ConfusingName {
// 反例2
public int stock;
}
数组
用类型与中括号紧挨表示。
- 正例:
int[] arry = {1, 2};
- 反例:
int arry[] = {1, 2};
枚举
类名带上Enum
后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
总结
望文生义
常量
命名
- (
final static
)全部大写,单词间用下划线隔开。
正例:private final Contract delegate;
public final static Map<String, MethodMetadata> METADATA_MAP = new HashMap<>();
- 常量与变量命名时,表示类型的名词放在词尾以提升辨识度:
- QueueOfWork → to →workQueue
- listName → to →nameList
- COUNT_TERMINATED_THREAD → to →TERMINATED_THREAD_COUNT
定义
- 禁止魔法值。
- 按功能进行归类。
- 复用层次:
- 跨应用共享常量
- 应用内共享常量:子模块的constant目录下
- 子工程内共享常量:子工程的constant目录下
- 包内共享常量:包的constant目录下
- 类内共享常量
POJO
定义
- POJO:在本规约中专指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等。
- DO(Data Object):阿里巴巴专指数据库表一一对应的POJO类。此对象与数据库表结构一一对应,通过DAO层向上传输数据源对象。
- DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
- BO(Business Object):业务对象,可以由Service层输出的封装业务逻辑的对象。
- Query:数据查询对象,用于各层接收上层的查询请求。有超过2个参数的查询封装时禁止使用Map类来传输。
- VO(View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
- AO(Application Object):阿里巴巴专指Application Object,即在Service层上,极为贴近业务的复用代码。
命名
布尔类型的变量禁止加is
前缀,否则部分框架解析会引起序列化错误。
初始化
- 禁止设定任何属性的默认值。
- 必须写toString方法(勿忘加
super.toString
)。 - 禁止同时存在对应属性xxx的isXxx()和getXxx()方法。
代码格式
- 单个方法的行数(除注释)不超过80行。
- 不同的逻辑、语义、业务的代码之间可插入一个空行分隔开来以提升可读性,但任何情形下都没必要插入多个空行进行分隔。
OOP规约
Object Oriented Programming(面向对象编程)
方法
- 重写方法必须加
@Override
:在编译期就可判断是否覆盖成功。 - 不允许修改外部正在调用的接口的方法签名,避免对接口调用方产生影响,若接口过时则必须加
@Deprecated
,并说明采用的新接口或新服务。 - 禁止使用过时的类或方法。
数据类型
- 所有货币金额均以以最小货币单位的整型来进行存储。
- 定义数据对象类(DO)时,属性类型要与数据库字段类型相匹配,如bigint必须对应Long,若bigint unsigned对应Integer,则可能会溢出成负数。
- POJO类的属性、RPC方法的返回值和参数都必须使用包装数据类型。
- 所有的局部变量使用基本数据类型。
初始化
- 禁止在构造方法里加入业务逻辑,初始化逻辑可放在init方法中。
比较
- Object的equals方法容易引起NPE,应使用常量或确定有值的对象来调用equals,推荐使用
java.util.Objects#equals
。 - 有整型包装类:全部使用equals,例如在-128至127之间取值的Integer对象是通过
IntegerCache.cache
产生,会复用已有对象,虽然此区间内的Integer可使用==
进行判断,但此区间外的所有数据都会在堆上产生,并不会复用已有对象。 - 浮点数:
- 反例
float a = 1.0F - 0.9F; float b = 0.9F - 0.8F; if (a == b) { // false } Float x = Float.valueOf(a); Float y = Float.valueOf(b); if (x.equals(y)) { // false }
- 正例
- 指定误差范围
float a = 1.0F - 0.9F; float b = 0.9F - 0.8F; float diff = 1e-6F; if (Math.abs(a - b) < diff) { // true }
- 使用BigDecimal
- BigDecimal:equals()会比较值和精度(
1.0
与1.00
的返回结果为false),而 compareTo()则忽略精度。
日期时间
- pattern:
- yyyy表示当天所在的年,而YYYY表示week in which year(一周从周日开始,周六结束,只要本周跨年,返回的就是下一年)。
- H表示24小时制,h表示12小时值。
System.currentTimeMillis()
代替new Date().getTime()
(效率问题),纳秒级时间值则使用System.nanoTime
。- 推荐使用Instant、LocalDate、LocalTime和LocalDateTime,禁止使用
java.sql.Date
、java.sql.Time
、java.sql.Timestamp
。 - 硬编码:禁止在程序中将一年为写死为365天。
- SimpleDateFormat是线程不安全的类,一般不要定义为static变量,若定义为static则必须加锁。推荐使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方解释:simple beautiful strong immutable
thread-safe。
正例:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
集合处理
- 只要重写equals就必须重写hashCode,因为Set存储的对象、Map的键都依据hashCode和equals进行判断,而String已经重写了这两个方法。
- 使用isEmpty()代替
size()==0
判空,因为在某些集合中,前者的时间复杂度为O(1),而且可读性更好。 - 禁止在foreach循环里进行remove/add。remove需使用Iterator方式,并发操作则要对Iterator对象加锁。
// 正例:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("1".equals(item)) {
iterator.remove();
}
}
// 反例:
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
- 集合泛型定义时使用diamond语法(菱形泛型)或全省略:
HashMap<String, String> userCache = new HashMap<>(16);
ArrayList<User> users = new ArrayList(10);
- 集合初始化时指定集合初始值大小。
- 利用Set可快速对一个集合进行去重操作,避免使用List的contains()进行遍历去重或判断包含操作。
并发处理
初始化与销毁
- 创建线程或线程池时需指定有意义的线程名称。
- 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
- 线程池禁止用Executors去创建,而是通过ThreadPoolExecutor的方式,这会更加明确线程池的运行规则。
Executors返回的线程池对象的弊端:- FixedThreadPool和SingleThreadPool:允许的请求队列长度为
Integer.MAX_VALUE
,可能会堆积大量的请求而导致OOM。 - CachedThreadPool:允许的创建线程数量为
Integer.MAX_VALUE
,可能会创建大量的线程而导致OOM。
- FixedThreadPool和SingleThreadPool:允许的请求队列长度为
- 必须回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,若不清理自定义的ThreadLocal变量,可能会影响后续业务逻辑。尽量在代理中使用try-finally块进行回收:
objectThreadLocal.set(userInfo);
try {
// ...
} finally {
objectThreadLocal.remove();
}
- 使加锁的代码块的工作量尽可能的小:
1. 能用无锁数据结构的就不要用锁。
2. 能锁区块的就不要锁整个方法体。
3. 能用对象锁的就不要用类锁。
加解锁方式
- 阻塞等待获取锁的方式:
- 反例:
-
Lock lock = new XxxLock(); lock.lock(); // 若此处抛出异常则无法解锁,使得其他线程无法成功获取锁。 doSomething(); try { doOthers(); } finally { lock.unlock(); }
-
Lock lock = new XxxLock(); try { // 若此处抛出异常会使得在finally代码块中对未加锁的对象解锁。 doSomething(); lock.lock(); doOthers(); } finally { // 若当前线程不持有锁则抛出IllegalMonitorStateException。 lock.unlock(); }
-
- 正例:
Lock lock = new XxxLock(); doSomething(); lock.lock(); try { doOthers(); } finally { lock.unlock(); } ```
- 反例:
- 尝试机制来获取锁的方式:
Lock lock = new XxxLock();
doSomething();
boolean isLocked = lock.tryLock();
// 进入业务代码块之前必须先判断当前线程是否持有锁。
if (isLocked) {
try {
doOthers();
} finally {
lock.unlock();
}
}
锁的选型
- 若每次访问冲突概率小于20%则用乐观锁,否则用悲观锁。乐观锁的重试次数需大于3次。
- 多线程并行处理定时任务时,若使用Timer运行多个TimeTask,只要其中之一没有捕获抛出的异常,其他任务便会终止运行,但使用ScheduledExecutorService则没有这个问题。
- 资金相关的金融敏感信息使用悲观锁策略:因为乐观锁在获得锁的同时已完成了更新操作,校验逻辑容易出现漏洞,而且乐观锁对冲突的解决策略有较复杂的要求,处理不当则容易造成系统压力或数据异常,所以资金相关的金融敏感信息不建议使用乐观
锁更新。 - 悲观锁遵循一锁、二判、三更新、四释放的原则。
线程安全
- 避免Random实例被多线程使用:虽然共享该实例是线程安全的,但会因竞争同一seed而导致性能下降,所以要保证每个线
程持有一个单独的Random实例,推荐使用ThreadLocalRandom。 - 禁止HashMap在多线程下put,因为resize时可能会由于高并发而出现死链,进而导致CPU飙升。
控制语句
- 在一个 switch 块内,都必须包含一个default语句并放在最后,即使它没有代码。
- switch括号内的变量类型为String并且此变量为外部参数时,必须先进行null判断。
- 在if/else/for/while/do语句中,即使只有一行代码也要使用大括号。
- 不要在条件判断中执行复杂的语句,而是将复杂的逻辑判断的结果赋值给一个有意义的布尔变量名以提高可读性。
- 不要在其它表达式中,插入赋值语句。
反例:public Lock getLock(boolean fair) { // 算术表达式中出现赋值操作,容易忽略count的值已经被改变 threshold = (count = Integer.MAX_VALUE) - 1; // 条件表达式中出现赋值操作,容易误认为是sync==fair return (sync = fair) ? new FairSync() : new NonfairSync(); }
注释规约
- 类、类属性、类方法的注释必须使用Javadoc规范,使用
/**内容*/
格式,不得使用// xxx
方式,这使得在IDE中调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。 - 所有的类都必须添加创建者和创建日期,日期格式为
yyyy/MM/dd
。 - 方法注释要包含返回值、参数、异常说明,而抽象方法(包括接口中的方法)还必须指出该方法做什么事情、实现什么功能、对子类的实现要求或调用的注意事项。
- 方法内部的单行注释用
//
,在被注释语句的上方另起一行。方法内部的多行注释用/* */
,注意与代码对齐。 - 所有的枚举类型字段都要有注释,说明每个数据项的用途。
- 代码与注释要同步更新。
前后端规约
定义
前后端交互的API需明确协议、域名、路径、请求方法、请求内容、状态码、响应体。
- 协议:生产环境必须使用 HTTPS。
- 路径:
- 代表一种资源,只能为名词,推荐使用复数,不能为动词,因为请求方法已经表达动作意义。
- URL路径不能使用大写,使用
_
分隔单词。 - 禁止携带表示请求内容类型的后缀,比如
.json
、.xml
等,可通过accept头表达。
- 请求方法:是对具体操作的定义。常见的请求方法如下:
- GET:从服务器取出资源。
- POST:在服务器新建一个资源。
- PUT:在服务器更新资源。
- DELETE:从服务器删除资源。
- 请求内容:URL带的参数必须无敏感信息或符合安全要求;body里带参数时必须设置Content-Type。
- 响应体:响应体可放置多种数据类型,由Content-Type来确定。
传输
- 服务端使用JSON格式返回数据:虽然HTTP支持使用不同的输出格式,例如纯文本、CSV、XML、RSS、HTML等,但我
们使用的面向用户的服务应该选择JSON作为通信中标准的数据交换格式,包括请求和响应,因为application/JSON是一种通用的MIME类型(Multipurpose Internet Mail Extensions,即多用途互联网邮件扩展类型,是一个互联网标准),具有实用、精简、易读的特点。 - 前后端交互的JSON中,所有的key必须用lowerCamelCase风格命名,且要符合英文表达习惯、表意完整。
反例:ERRORCODE / ERROR_CODE / error_message / error-message / errormessage / ErrorMessage / msg - 若数据列表相关的接口返回为空则用空数组
[]
或空集合{}
,可减少前端很多琐碎的null判断。 - 对要使用超大整数的场景,服务端一律用String返回,禁止用Long类型:
服务端若返回Long整型的数据给前端,JS会将其自动转换为Number类型(此类型为双精度浮点数,表示原理与取值范围等同于Java中的Double)。Long类型能表示的最大值是 2 63 − 1 2^{63}-1 263−1,但超过 2 53 2^{53} 253(9007199254740992)的数值转化为JS的Number时,有些数值会有精度损失。在Long取值范围内,任何2的指数次整数都绝对不会存在精度损失,所以说精度损失是一个概率问题,若浮点数的尾数位与指数位空间不限,则可精确表示任何整数,但双精度浮点数的尾数位只有52位。 - 在翻页场景中,参数小于1则返回第一页,参数大于总页数则返回最后一页。
- 服务器返回信息必须被标记是否可缓存,如果缓存,客户端可能会重用之前的请求结果。
http 1.1中用s-maxage通知服务器进行缓存,时间单位为秒:response.setHeader("Cache-Control", "s-maxage=" + cacheSeconds);
- 统一用GMT(Greenwich Mean Time,格林尼治时间,即Universal Time(世界时)),时间格式为
yyyy-MM-dd HH:mm:ss
。
限制
- HTTP请求通过URL传递参数时不能超过2048字节,因为不同浏览器对URL的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异(比如截断),2048字节是取所有浏览器的最小值。
- HTTP请求通过body传递内容时必须控制长度:nginx默认限制是1MB;tomcat默认限制是2MB。
其他
- 正则表达式需要利用好其预编译功能:
static final Pattern
- 禁止用Apache Beanutils进行属性的copy,因为性能较差,可用Spring BeanUtils或Cglib BeanCopier,但均是浅拷贝。
- 不要在视图模板中加入任何复杂的逻辑,因为根据MVC理论,视图的职责是展示,不要抢模型和控制器的活。
- 任何数据结构的构造或初始化都应指定大小,避免数据结构无限增长而吃光内存。
二、异常日志
错误码
- 为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号。
- 错误产生来源
- A:用户
- B:当前系统
- C:第三方服务
- 四位数字编号:从0001到9999,大类之间的步长间距预留100。
- 错误产生来源
- 错误码以不断追加的方式进行兼容,所以错误码不体现版本号和错误等级信息,错误等级由日志和错误码本身的释义来决定。
- 错误码不能直接输出给用户作为提示信息使用。
异常处理
- Java类库中定义的可以通过预检查方式规避的RuntimeException异常禁止通过catch的方式来处理,比如NullPointerException、IndexOutOfBoundsException等:
- 正例:
if (obj != null) {...}
- 反例:
try { obj.method(); } catch (NullPointerException e) {…}
- 正例:
- catch尽可能将异常类型区分,再做对应的异常处理:对大段代码进行try-catch,不仅无法使程序根据不同异常做出不同的应激反应,也不利于定位问题。
- 禁止捕获异常却什么都不处理。
- 禁止在finally中使用return,因为try中的return语句执行成功后并不马上返回,而是继续执行finally中的语句。
- 在调用RPC、二方包或动态生成类的相关方法时,捕捉异常要用Throwable来拦截,因为通过反射机制来调用方法,若找不到方法则会抛出NoSuchMethodException,比如在字节码修改框架(ASM)动态创建或修改类时修改了相应的方法签名,此情况下,即使代码编译期是正确的,但在代码运行期时也会抛出NoSuchMethodError。
- 方法的返回值可以为null,但必须添加注释说明什么情况下会返回null。
- 防止NPE:推荐使用Optional
注意NPE产生的场景:- 数据库的查询结果可能为null。
- 集合里的取出的数据元素也可能为null。
- 远程调用返回对象时,一律要求进行空指针判断。
- Session中获取的数据建议进行NPE检查。
- 级联调用
obj.getA().getB().getC()
易产生NPE。
- 定义时要区分unchecked/checked异常,避免直接抛出new RuntimeException(),禁止抛出Exception或Throwable,应使用有业务含义的自定义异常,推荐使用业界已定义过的自定义异常,如DAOException、ServiceException等。
finally
- finally块必须对资源对象、流对象进行关闭,推荐使用
try-with-resources
方式。 - 禁止在finally中使用return。
日志规约
- 不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架(SLF4J)中的API,使用门面模式的日志框架有利于维护,使得各个类的日志处理方式统一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
- 根据国家法律,网络运行状态、网络安全事件、个人敏感信息操作等相关记录留存的日志不少于六个月,并要进行网络多机备份。
- 对trace/debug/info级别的日志输出必须进行日志级别的开关判断,避免无谓的开销。
正例:
if (logger.isDebugEnabled()) {
// 若不加开关,日志的输出等级即使是info也会调用getName。
logger.debug("Current ID is: {} and name is: {}", id, getName());
}
- 生产环境禁止用
System.out
、System.err
、e.printStackTrace()
- 异常信息必须包括两类信息:案发现场信息和异常堆栈信息。
logger.error("inputParams:{} and errorMessage:{}", 各类参数或者对象 toString(), e.getMessage(), e);
- 日志打印时禁止直接用JSON工具将对象转换成String,因为存在抛出异常的情况,可能会因为打印日志而影响正常业务流
程的执行,所以打印日志时仅打印出业务相关属性值或调用其对象的toString()方法。 - 生产环境禁止输出debug日志,info日志则有选择地输出。
- 可以使用warn日志级别来记录用户输入参数错误的情况,但不要在此场景用error级别,避免频繁报警。error级别只记录系统逻辑出错、异常或重要的错误信息。
- 尽量用英文来描述日志错误信息,除非用英文描述不清的情况下可用中文。
国际化团队或海外部署的服务器由于字符集问题,使用全英文来注释和描述日志错误信息。
三、单元测试
- 遵守AIR原则:
- Automatic(自动化):是全自动执行的,而非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。禁止用
System.out
来进行人肉验证,必须用assert
验证。 - Independent(独立性):为了保证单元测试稳定且便于维护,单元测试用例之间禁止互相调用,也不能依赖执行的先后次序。
- Repeatable(可重复):不能受到外界环境的影响。
- Automatic(自动化):是全自动执行的,而非交互式的。测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。禁止用
- 禁止用
System.out
来进行人肉验证,必须用assert
验证。 - 要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别。单测不负责检查跨类或跨系统的交互逻辑,那是集成测试的领域。
- 新增代码要及时补充单元测试并确保单元测试通过,若新增代码影响了原有的单元测试要及时修正。
- 必须写在
src/test/java
下:源码编译时会跳过此目录,而单元测试框架默认是扫描此目录。 - 对不可测的代码要做必要的重构,使代码变得可测,避免为达到测试要求而书写不规范的测试代码。
四、安全规约
- 隶属于用户个人的页面或功能必须进行权限控制校验。
- 用户敏感数据禁止直接展示,必须对展示数据进行脱敏,例如:中国大陆个人手机号码显示
139****1219
,隐藏中间4位,防止隐私泄露。 - 用户输入的SQL参数必须使用参数绑定,防止SQL注入,禁止字符串拼接SQL访问数据库。
- 用户请求传入的任何参数必须做有效性验证。
- 在使用平台资源(如短信、邮件、电话、下单、支付)时必须实现正确的防重放机制,如数量限制、疲劳度控制、验证码校验等,避免被滥刷而导致资损。
例:注册时发送验证码到手机,若没有限制次数和频率,则可利用此功能骚扰到其他用户并造成短信平台的资源浪费。 - 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略。
五、MySQL 数据库
ORM映射
- 禁止用
*
作为查询的字段列表- 增加查询分析器解析成本。
- 增减字段容易与resultMap配置不一致。
- 无用字段会增加网络消耗,尤其是text类型的字段。
- POJO类的布尔属性不能加is,而数据库字段必须加
is_
,在resultMap中进行字段与属性之间的映射。 - 不要用resultClass当返回参数,即使所有类属性名与数据库字段一一对应也要定义
<resultMap>
,即每张表都必然有一个<resultMap>
与之对应:配置映射关系使字段与DO类解耦,方便维护。 - 使用
#
代替$
,防止SQL注入。 - 禁止用HashMap与Hashtable作为查询结果集的输出。
- 更新数据表记录时,必须同时更新记录对应的update_time。
- 不要写一个大而全的数据更新接口。传入为POJO类,不管是不是目标更新字段都进行
update table set c1=value1,c2=value2,c3=value3;
,那么执行SQL时会更新无改动的字段,不仅易出错、效率低,还增加binlog存储。 @Transactional
事务不要滥用:事务会影响数据库的QPS,而且使用事务的地方还要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
六、工程结构
服务器
- 高并发服务器建议调小TCP协议的time_wait超时时间:操作系统默认240秒后才会关闭处于time_wait状态的连接,在高并发访问下,服务器会因为处于time_wait的连接数太多导致无法建立新的连接。
在linux服务器上可通过/etc/sysctl.conf
去修改该缺省值(秒):net.ipv4.tcp_fin_timeout = 30
。 - 调大服务器所支持的最大文件句柄数(File Descriptor,即fd):主流操作系统的设计是将TCP/UDP连接采用与文件一样的方式去管理,即一个连接对应于一个fd。主流的linux服务器默认支持的最大fd数量为1024,当并发连接数很大时容易因为fd不足而出现“open too many files”错误,会导致新的连接无法建立。建议将linux服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。
- JVM环境参数设置
-XX:+HeapDumpOnOutOfMemoryError
参数,让JVM碰到OOM场景时输出dump信息。 - JVM的Xms和Xmx设置一样大小的内存容量,避免在GC后调整堆大小带来的压力。
七、设计规约
- 类在设计与实现时要符合单一原则。单一原是最易理解但最难实现的一条规则,随着系统演进,很多时候都忘记了类设计的初衷。
- 谨慎使用继承的方式来进行扩展,优先使用聚合/组合的方式来实现。若使用继承的方式,则要符合里氏代换原则,即父类能出现的地方子类一定能出现。
- 系统设计阶段,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护。低层次模块依赖于高层次模块的抽象,方便系统间的解耦。
- 系统设计阶段,注意对扩展开放,对修改闭合,因为极端情况下,交付的代码是不可修改的,同一业务域内的需求变化要通过模块或类的扩展来实现。
- 共性业务或公共行为要抽取共性方法或抽象公共类,甚至是组件化,使得在系统中不出现重复代码,即DRY原则(Don’t Repeat Yourself)。
- 敏捷开发是快速交付迭代可用的系统,省略多余的设计方案,摒弃传统的审批流程,但仍需要核心关键点的必要设计和文档沉淀。
- 可扩展性的本质是找到系统的变化点并隔离变化点。世间众多设计模式其实就是一种设计模式,即隔离变化点的模式。极致扩展性的标志就是在需求的新增时也不会在原有交付的代码上进行任何形式的修改。
无障碍产品设计
- 要求:
- 所有可交互的控件元素必须能被tab键聚焦,并且焦点顺序要符合自然操作逻辑。
- 用于登录校验和请求拦截的验证码均需提供图形验证以外的其他方式。
- 自定义的控件类型要明确交互方式。
- 正例:
用户登录场景中,输入框的按钮都要考虑tab键聚焦,且符合自然逻辑的操作顺序如下:“输入用户名>输入密码>输入验证码>点击登录”,其中验证码实现语音验证方式,若有自定义标签实现的控件,可使用role属性设置控件类型。
专有名词
- GAV(GroupId、ArtifactId、Version):Maven坐标,是用来唯一标识jar包。
- CAS(Compare And Swap):解决多线程并行情况下使用锁造成性能损耗的一种机制,这是硬件实现的原子操作。CAS操作包含三个操作数:内存位置、预期原值和新值。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值,否则处理器不做任何操作。
- AQS(AbstractQueuedSynchronizer):利用先进先出队列实现的底层同步工具类,它是很多上层同步实现类的基础,比如ReentrantLock、CountDownLatch、Semaphore等,它们通过继承AQS实现其模版方法,然后将AQS子类作为同步组件的内部类,通常命名为Sync。
- ORM(Object Relation Mapping):对象关系映射,即对象领域模型与底层数据之间的转换,本文泛指iBATIS、mybatis等框架。
- NPE(java.lang.NullPointerException):空指针异常。
- OOM(java.lang.OutOfMemoryError):当JVM没有足够内存来为对象分配空间并且垃圾回收器也无法回收空间时系统出现的严重状况。
- 一方库:本工程内部子项目模块依赖的库(jar包)。
- 二方库:公司内部发布到中央仓库,可供公司内部其它应用依赖的库(jar包)。
- 三方库:公司之外的开源库(jar包)。
总结
阅读经典的项目,借鉴他们的分层架构
推荐:spring-cloud-alibaba
最后
以上就是辛勤龙猫为你收集整理的【Java】阿里巴巴Java开发手册官方文档一、编程规约二、异常日志三、单元测试四、安全规约五、MySQL 数据库六、工程结构七、设计规约专有名词总结的全部内容,希望文章能够帮你解决【Java】阿里巴巴Java开发手册官方文档一、编程规约二、异常日志三、单元测试四、安全规约五、MySQL 数据库六、工程结构七、设计规约专有名词总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复