一、编程规约
(一)命名风格
- 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。纯拼音命名方式也要避免采用
- 类名使用UpperCamelCase风格(首字母大写),但DO / BO / DTO / VO / AO / PO等除外。
- 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从驼峰形式。
- 常量命名全部大写
- 异常类命名使用Exception结尾
- POJO类中布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误
- 包名统一使用小写
- 接口类中的方法和属性不要加任何修饰符号,并加上有效的Javadoc注释。
- 对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别。
- 枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开
- Service/DAO层方法命名规范
- 获取单个对象的方法用get作前缀。
- 获取多个对象的方法用list作前缀。
- 获取统计值的方法用count作前缀。
- 插入的方法用save/insert作前缀。
- 删除的方法用remove/delete作前缀。
- 修改的方法用update作前缀。
- 领域模型命名规范
- 数据对象:xxxDO,xxx即为数据表名。
- 数据传输对象:xxxDTO,xxx为业务领域相关的名称。
- 展示对象:xxxVO,xxx一般为网页名称。
- POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。
(二)常量定义/代码格式
- long或者Long初始赋值时,使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。
- 不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在类CacheConsts下;系统配置相关常量放在类ConfigConsts下。 - 采用4个空格缩进,禁止使用tab字符。
说明: 如果使用tab缩进,必须设置1个tab为4个空格。IDEA设置tab为4个空格时,请勿勾选Use tab character
;而在eclipse中,必须勾选insert spaces for tabs
。 - IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式,不要使用Windows格式。
(三)OOP规约
- 避免通过一个类的对象引用访问此类的静态变量或静态方法,直接用类名来访问即可。
- 所有的覆写方法,必须加@Override注解
- Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
正例:“test”.equals(object);
反例:object.equals(“test”); - 所有的POJO类属性必须使用包装数据类型
- RPC方法的返回值和参数必须使用包装数据类型。
- 所有的局部变量使用基本数据类型
- 定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。
- POJO类必须写toString方法。
- 使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛IndexOutOfBoundsException的风险
- 循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展
- 慎用Object的clone方法来拷贝对象。
说明:对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。 - 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
- 工具类不允许有public或default构造方法
(四)集合处理
- 关于
hashCode
和equals
的处理,遵循如下规则:
- 只要重写
equals
,就必须重写hashCode
。 - 因为Set存储的是不重复的对象,依据
hashCode
和equals
进行判断,所以Set存储的对象必须重写这两个方法。 - 如果自定义对象作为Map的键,那么必须重写
hashCode
和equals
。
说明:String重写了hashCode和equals方法,所以我们可以非常愉快地使用String对象作为key来使用。
- 使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组
- 使用toArray带参方法,入参数组大小定义与集合元素个数一致
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
- 直接使用toArray无参方法存在问题,此方法返回值只能是Object[]类,若强转其它类型数组将出现ClassCastException错误。
- 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常
- asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
- 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
- 集合初始化时,指定集合初始值大小。
说明:HashMap使用HashMap(int initialCapacity) 初始化
- 注意负载因子(即loader factor)默认为0.75,如果暂时无法确定初始值大小,请设置为16(即默认值)。
- 使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。
- keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。
- **values()**返回的是V值集合,是一个list集合对象;keySet()返回的是K值集合,是一个Set集合对象;entrySet()返回的是K-V值组合集合
集合类 | Key | Value | Super | 说明 |
Hashtable | 不允许为null | 不允许为null | Dictionary | 线程安全 |
ConcurrentHashMap | 不允许为null | 不允许为null | AbstractMap | 锁分段技术(JDK8:CAS) |
TreeMap | 不允许为null | 允许为null | AbstractMap | 线程不安全 |
HashMap | 允许为null | 允许为null | AbstractMap | 线程不安全 |
(五)并发处理
- 获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
- 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
- 及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
- 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
- SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。
- 如果是JDK8的应用,可以使用
Instant
代替
Date
,
LocalDateTime
代替
Calendar
,
DateTimeFormatter
代替
SimpleDateFormat
- 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
- 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
- 并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据
- 如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次
- 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用
ScheduledExecutorService
则没有这个问题。 - 在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化问题隐患(可参考 The “Double-Checked Locking is Broken” Declaration),推荐解决方案中较为简单一种(适用于JDK5及以上版本),将目标属性声明为 volatile型。
- HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中可以使用其它数据结构或加锁来规避此风险。
(六)控制语句
- 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使空代码。
- 在if/else/for/while/do语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式
- 在高并发场景中,避免使用”等于”判断作为中断或退出的条件。
说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。 - 表达异常的分支时,少用if-else方式,这种方式可以改写成:
if (condition) {
...
return obj;
}
// 接着写else的业务逻辑代码;
- 超过3层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下:
public void today() {
if (isBusy()) {
System.out.println(“change time.”);
return;
}
if (isFree()) {
System.out.println(“go to travel.”);
return;
}
System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
return;
}
(七)注释规约
- 类、类属性、类方法的注释必须使用Javadoc规范,使用/*内容/格式,不得使用// xxx方式。
- 所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
- 所有的类都必须添加创建者和创建日期。
- 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
二、异常日志
(一) 异常处理
- Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。
说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,不得不通过catch NumberFormatException来实现。
if (obj != null) {...}
- 防止NPE,是程序员的基本修养,注意NPE产生的场景:
1)返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
反例:public int f() { return Integer对象}, 如果为null,自动解箱抛NPE。
2) 数据库的查询结果可能为null。
3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
4) 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
5) 对于Session中获取的数据,建议NPE检查,避免空指针。
6) 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
正例:使用JDK8的Optional类来防止NPE问题。
(二)日志规约
- 应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
(三) 其它
- 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则); - velocity调用POJO类的属性时,建议直接使用属性名取值即可,模板引擎会自动按规范调用POJO的getXxx(),如果是boolean基本数据类型变量(boolean命名不需要加is前缀),会自动调用isXxx()方法。
说明:注意如果是Boolean包装类对象,优先调用getXxx()的方法。 - 后台输送给页面的变量必须加{var}会直接显示在页面上。
- Math.random() 这个方法返回是double类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将x放大10的若干倍然后取整,直接使用Random对象的
nextInt
或者nextLong
方法。 - 获取当前毫秒数
System.currentTimeMillis();
而不是
new Date().getTime();
说明
:如果想获取更加精确的纳秒级时间值,使用
System.nanoTime()
的方式。在JDK8中,针对统计时间等场景,推荐使用
Instant
类。
三、单元测试
- 好的单元测试必须遵守AIR原则。
说明
:单元测试在线上运行时,感觉像空气(AIR)一样并不存在,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
- A:Automatic(自动化)
- I:Independent(独立性)
- R:Repeatable(可重复)
- 单元测试中不准使用System.out来进行人肉验证,必须使用assert来验证。
- 单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。
- 单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。
说明:源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。 - 在工程规约的应用分层中提到的DAO层,Manager层,可重用度高的Service,都应该进行单元测试。
- 对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的,或者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据。
- 在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例(UC)
- 单元测试作为一种质量保障手段,不建议项目发布后补充单元测试用例,建议在项目提测前完成单元测试。
四、安全规约
- 用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
说明:个人手机号码显示为:158****9119,隐藏中间4位,防止隐私泄露。 - 用户请求传入的任何参数必须做有效性验证。
说明
:忽略参数校验可能导致:
- page size过大导致内存溢出
- 恶意order by导致数据库慢查询
- 任意重定向
- SQL注入
- 反序列化注入
- 正则输入源串拒绝服务ReDoS
说明:Java代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。
- 表单、AJAX提交必须执行CSRF安全过滤。
说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在CSRF漏洞的应用/网站,攻击者可以事先构造好URL,只要受害者用户一访问,后台便在用户不知情情况下对数据库中用户参数进行相应修改。 - 如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。
- 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略。
五、MySQL数据库
- 表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint( 1表示是,0表示否)。
说明:任何字段如果为非负数,必须是unsigned
。
正例:表达逻辑删除的字段名is_deleted
,1表示删除,0表示未删除。 - 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
- 说明:MySQL在Windows下不区分大小写,但在Linux下默认是区分大小写。因此,数据库名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name - 表名不使用复数名词
- 小数类型为decimal,禁止使用float和double。
说明:float和double在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数分开存储。 - 如果存储的字符串长度几乎相等,使用char定长字符串类型。
- varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
- 表必备三字段:id, gmt_create, gmt_modified。
说明:其中id必为主键,类型为unsigned bigint、单表时自增、步长为1。gmt_create, gmt_modified的类型均为datetime类型,前者现在时表示主动创建,后者过去分词表示被动更新。 - 表的命名最好是加上“业务名称_表的作用”。
正例:alipay_task / force_project / trade_config - 合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
正例:如下表,其中无符号值可以避免误存负数,且扩大了表示范围。
对象 | 年龄区间 | 类型 | 字节 |
人 | 150岁之内 | unsigned tinyint | 1 |
龟 | 数百岁 | unsigned smallint | 2 |
恐龙化石 | 数千万岁 | unsigned int | 4 |
太阳 | 约50亿年 | unsigned bigint | 8 |
- 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
说明:不要以为唯一索引影响了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。 - 超过三个表禁止join。需要join的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
说明:即使双表join也要注意表索引、SQL性能。 - 不要使用count(列名)或count(常量)来替代count(),count()是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。 - 当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()时需注意NPE问题。
- 使用
ISNULL()
来判断是否为NULL值。 说明:NULL与任何值的直接比较都为NULL。
1)NULL<>NULL
的返回结果是NULL,而不是false
。
2)NULL=NULL
的返回结果是NULL,而不是true
。
3)NULL<>1
的返回结果是NULL,而不是true
。 - 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
- 不得使用外键与级联,一切外键概念必须在应用层解决。
- 说明:以学生和成绩的关系为例,学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
- 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
- 数据订正(特别是删除、修改记录操作)时,要先select,避免出现误删除,确认无误才能执行更新语句。
- in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内。
- 如果有全球化需要,所有的字符存储与表示,均以utf-8编码,注意字符统计函数的区别。
- 如果需要存储表情,那么选择utf8mb4来进行存储,注意它与utf-8编码的区别。
- 在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
- POJO类的布尔属性不能加is,而数据库字段必须加is_,要求在resultMap中进行字段与属性之间的映射。
说明:参见定义POJO类以及数据库字段定义规定,在中增加映射,是必须的。在MyBatis Generator生成的代码中,需要进行对应的修改。 - sql.xml配置参数使用:#{},#param# 不要使用${} 此种方式容易出现SQL注入
- 不允许直接拿HashMap与Hashtable作为查询结果集的输出。
- 更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间.
@Transactional
事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
六、工程结构
(一) 应用分层
- 图中默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于Web层,也可以直接依赖于Service层,依此类推: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g6WtkMf3-1586913409721)(https://1994.github.io/p3c/images/alibabaLevel.png)]
- 开放接口层:可直接封装Service方法暴露成RPC接口;通过Web封装成http接口;进行网关安全控制、流量控制等。
- 终端显示层:各个端的模板渲染并执行显示的层。当前主要是velocity渲染,JS渲染,JSP渲染,移动端展示等。
- Web层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
- Service层:相对具体的业务逻辑服务层。
- Manager层:通用业务处理层,它有如下特征:
1) 对第三方平台封装的层,预处理返回结果及转化异常信息;
2) 对Service层通用能力的下沉,如缓存方案、中间件通用处理;
3) 与DAO层交互,对多个DAO的组合复用。 - DAO层:数据访问层,与底层MySQL、Oracle、Hbase等进行数据交互。
- 外部接口或第三方平台:包括其它部门RPC开放接口,基础平台,其它公司的HTTP接口。
- 分层领域模型规约:
- DO(Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。
- DTO(Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
- BO(Business Object):业务对象。由Service层输出的封装业务逻辑的对象。
- AO(Application Object):应用对象。在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
- VO(View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
- Query:数据查询对象,各层接收上层的查询请求。注意超过2个参数的查询封装,禁止使用Map类来传输。
(二) 二方库依赖
- groupID格式:com.{公司/BU }.业务线.[子业务线],最多4级。
说明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress等BU一级;子业务线可选。 - artifactID格式:产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下
- 二方库版本号命名方式:主版本号.次版本号.修订号
1) 主版本号:产品方向改变,或者大规模API不兼容,或者架构不兼容升级。
2) 次版本号:保持相对兼容性,增加主要功能特性,影响范围极小的API不兼容修改。
3) 修订号:保持完全兼容性,修复BUG、新增次要功能特性等。 - 线上应用不要依赖SNAPSHOT版本(安全包除外)。
说明:不依赖SNAPSHOT版本是保证应用发布的幂等性。另外,也可以加快编译时的打包构建。 - 依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。
- 依赖springframework-core,-context,-beans,它们都是同一个版本,可以定义一个变量来保存版本${spring.version},定义依赖的时候,引用该版本。
- 禁止在子项目的pom依赖中出现相同的GroupId,相同的ArtifactId,但是不同的Version。
- 子项目需要显式的声明依赖,version和scope都读取自父pom。而
所有声明在主pom的
里的依赖都会自动引入,并默认被所有的子项目继承 - 为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则:
1)精简可控原则。移除一切不必要的API和依赖,只包含 Service API、必要的领域模型对象、Utils类、常量、枚举等。如果依赖其它二方库,尽量是provided引入,让二方库使用者去依赖具体版本号;无log具体实现,只依赖日志框架。
2)稳定可追溯原则。每个版本的变化应该被记录,二方库由谁维护,源码在哪里,都需要能方便查到。除非用户主动升级版本,否则公共二方库的行为不应该发生变化。
七、名词解析
- POJO(Plain Ordinary Java Object): POJO专指只有setter / getter / toString的简单类,包括DO/DTO/BO/VO等。
- GAV(GroupId、ArtifactctId、Version): Maven坐标,是用来唯一标识jar包。
- OOP(Object Oriented Programming): 泛指类、对象的编程处理方式。
- ORM(Object Relation Mapping): 对象关系映射,对象领域模型与底层数据之间的转换,泛指iBATIS, mybatis等框架。
- NPE(java.lang.NullPointerException): 空指针异常。
- SOA(Service-Oriented Architecture): 面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用,有利于提升组件可重用性,可维护性。
- 一方库: 本工程内部子项目模块依赖的库(jar包)。
- 二方库: 公司内部发布到中央仓库,可供公司内部其它应用依赖的库(jar包)。
- 三方库: 公司之外的开源库(jar包)。
rtifactctId、Version): Maven坐标,是用来唯一标识jar包。 - OOP(Object Oriented Programming): 泛指类、对象的编程处理方式。
- ORM(Object Relation Mapping): 对象关系映射,对象领域模型与底层数据之间的转换,泛指iBATIS, mybatis等框架。
- NPE(java.lang.NullPointerException): 空指针异常。
- SOA(Service-Oriented Architecture): 面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用,有利于提升组件可重用性,可维护性。
- 一方库: 本工程内部子项目模块依赖的库(jar包)。
- 二方库: 公司内部发布到中央仓库,可供公司内部其它应用依赖的库(jar包)。
- 三方库: 公司之外的开源库(jar包)。
- IDE(Integrated Development Environment): 用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。