JAVA 开发规范

JAVA编程遵循的通用原则:

  • 清晰第一、易于维护、易于重构。
  • 简洁为美、易于理解、易于实现。
  • 选择合适的风格,团队保持一致。

一、全局命名

基本常识
定义合法标识符的规则
  • 由26个英文字母大小写,0-9,_或$组成
  • 数字不可以开头
  • 不可以使用关键字和保留字,但是能包括关键字和保留字
  • Java中严格区分大小写,长度无限制
  • 标识符不能包括空格
  • 取名尽量做到"见名知意"
  • 坚决不允许出现中文
驼峰命名法(Camel-Case):

当变量名或函式名是由一个或多个单字连结在一起,而构成的唯一识别字时,首字母以小写开头,每个单词首字母大写(第一个单词除外)。

命名规则
1. 包名 xxxyyyzzz

**要求: 全部小写 **

命名应该都是名词或名词性词组,全部小写,单词之间用"."分开;一般使用本公司/组织网站域名的逆序后跟具体的软件内部模块名

   正例:com.gongzhi.user.controllercom.gongzhi.user.servicecom.gongzhi.user.service.implcom.gongzhi.user.daocom.gongzhi.user.modelcom.gongzhi.utilcom.gongzhi.vocom.gongzhi.dtocom.zlbcom.zlb.activitycom.zhaolaobao.activitycom.zlb.attrcom.zlb.userrule反例:com.zlb.userRulecom.zlb.user_rule
2. 类(接口)名 XxxYyyZzz

要求: 首字母大写,如果类名由多个单词组成,每个单词的首字母都要大写。

类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:(领域模型的相关命名)DO / BO / DTO / VO / AO

正例:
public class MyFirstClass{}
public interface MessageService{}
反例:
public class userService{}正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

接口的实现类,在类的后面加上"Impl"

Exception类用"Exception"做为类命名的结尾,例如:DataNotFoundException;

抽象类用"Abstract"做为类命名的开头,例如:AbstractBeanDefinition,AbstractBeanFactory;

Test类用"Test"做为类命名的结尾。例如:ContainerTest;

3. **变量名、方法名 ** xxxYyyZzz

要求: 首字母小写,如果名称由多个单词组成,每个单词的首字母都要大写。(驼峰式)

除非是在循环中,否则一般不推荐使用单个字母作为变量名,i、j、k等只作为小型循环的循环索引变量。

方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从 驼峰形式。

Model 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。

反例:定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),RPC框架在反向解析的时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

request response 变量名尽量与数据库一致(驼峰式)**

代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。(尽量避免使用纯拼音命名方式)

变量:
//普通变量
int index = 0;
String localValue = null;
//魔法数字
Product product1 = new Product();
Product product2 = new Product();
//根据对象生成变量名称
List<Order> orderList = new ArrayList<>();
List<Order> order = new ArrayList<>();
//变量名对应数据库列名 (不同模块代码不是同一个后端编写,但是传给前端的变量名称一致)
Date createTime;  //变量名
timestamp NOTNULL create_time //数据库列名
//代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束
反例: _name / __name / $Object / name_ / name$ / Object$    方法:
public void toString(){}
public void setName(String name){}
public void getHttpMessage(){}
public void inputUserId(){}
Service/DAO 层方法命名规约,统一前缀。
  1. 获取单个对象的方法用 get,find 做前缀。
  2. 获取多个对象的方法用 list 做前缀/后缀。
  3. 获取统计值的方法用 count 做前缀。
  4. 插入的方法用 save(推荐)或 insert ,add、create做前缀。
  5. 删除的方法用 remove(推荐)或 delete 做前缀。
  6. 修改的方法用 update 做前缀。
  7. 搜索性查询方法前缀:query、search。
  8. 批量插入方法用 insertBatch做前缀.
声明数组
//建议使用
int[] arrays = new int[1024];
//不建议使用
int arrays[] = new int[1024];
4.常量名 全部大写 XXX_YYY_ZZZ

**常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。 **

public static final String GAME_COLOR=”RED”;

常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包
内共享常量、类内共享常量。
跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
应用内共享常量:放置在一方库的 modules 中的 constant 目录下。
反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义 了
表示“是”的变量:
类 A 中:public static final String YES = “yes”;
类 B 中:public static final String YES = “y”;
A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致产生线上问题。
子工程内部共享常量:即在当前子工程的 constant 目录下。
包内共享常量:即在当前包下单独的 constant 目录下。
类内共享常量:直接在类内部 private static final 定义。

二、代码格式

基本常识

类中不要出现无用import,可以采用IDE工具进行优化,类提交前进行代码的格式化;
代码格式以开发工具默认为准,提交SVN代码前,先 ctrl + alt + L 格式化代码;
IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,
不要使用 windows 格式
一个程序(java)文件最好不要超过2000行;

一个方法所完成的功能要单一(单一职责原则),不同的功能封装为不同的方法;

三、注释

基本常识

类、类属性、类方法的注释必须使用 Javadoc 规范,使用/内容/格式,不得使用
//xxx 方式。
说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注
释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高
阅读效率
方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释
使用/
*/注释,注意与代码对齐。
所有的类都必须添加创建者信息
与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持
英文原文即可。
反例:“TCP 连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。
代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑
等的修改。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,
就失去了导航的意义。
特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,
经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1 待办事宜(TODO):( 标记人,标记时间,[预计处理时间])
表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc
还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
2 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。

注释三种方式
  • 单选注释:符号是://

  • 块注释: 符号是: /* */ 可以跨多行

  • javadoc注释: 符号是: /** */ 可以跨多行,

    生成javadoc时,这样的注释会被生成标准的javaapi注释。

使用场景
类注释
/*** @author 毛海涛* @create 2017-11-14 10:42* @DESCRIPTION 后台用户登录**/
@Api(tags = "登陆模块", description = "登陆模块API文档")
@RestController
@RequestMapping(value = "member")
public class LoginController {}
接口注释
/*** 后台用户登录* @param vo 接收参数vo* @return* @throws Exception*/
UserWebVo login(LogInDTO vo) throws Exception;
方法注释/单行注释
/*** 用户登录* @param vo 接收参数vo* @return* @throws Exception*/
@Override
public UserWebVo loginTest(LogInDTO vo) throws Exception {String param = vo.getParam();if (null == param) {throw new ServiceException("8", null, "用户名或密码错误");}// 获取当前的SubjectSubject currentUser = SecurityUtils.getSubject();// 在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查// 每个Realm都能在必要时对提交的AuthenticationTokens作出反应// 所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法currentUser.login(token);}catch (Exception e){throw new ServiceException("8",null,"用户名或密码错误");}//判断账号格式boolean b = RegexUtils.isMobile(name);if(!b){throw new ServiceException("8",null,"账号格式不正确,请重新输入");}//判断有无该账号List<BackMember> members = memberService.findByPhone(name);if (CollectionUtils.isEmpty(members) || members.size() > 1) {throw new ServiceException("8",null,"该账号不存在,请重新输入");}BackMember member = members.get(0);//判断账号是否禁用if(member.getIsAble()==1){throw new ServiceException("8",null,"账号已被禁用,请联系超管");}//判断是否被分配组List<BackMemberRole> memberRoles = memberRoleService.findByMemberId(member.getId());if(CollectionUtils.isEmpty(memberRoles)){throw new ServiceException("8",null,"该账号未被分配组,请联系超管");}//判断所在组是否被禁用List<Integer> roleIds = memberRoles.stream().map(BackMemberRole::getRoleId).collect(Collectors.toList());List<BackRole> roles = roleService.findByRoleIds(roleIds);List<Integer> states = roles.stream().map(BackRole::getIsAble).collect(Collectors.toList());if(!states.contains(0)){throw new ServiceException("8",null,"该账号所在组已被禁用,请联系超管");}//获取权限return getUserWebVo(member);}
//TODO 待处理
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String url = request.getRequestURI();String name = request.getServerName();String type = SendMsgUtil.getProperties();String ipAddress = CusAccessObjectUtil.getIpAddress(request);//TODO  上线时删除   毛海涛  2018.11.27if (type.equals("dev") && !url.equals("/manage/member/findMemberVo")) {//如果未登录  添加登录信息boolean login = MemberUtil.isLogin(request);if (login) {isService(request, ipAddress, name);}} else {isLogin(request);}return true;}
//FIXME 错误
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String url = request.getRequestURI();String name = request.getServerName();String type = SendMsgUtil.getProperties();String ipAddress = CusAccessObjectUtil.getIpAddress(request);//FIXME  代码逻辑错误  待修改  毛海涛  2018.11.27if (type.equals("dev") && !url.equals("/manage/member/findMemberVo")) {//如果未登录  添加登录信息boolean login = MemberUtil.isLogin(request);if (login) {isService(request, ipAddress, name);}} else {isLogin(request);}return true;}

**带有 //TODO //FIXME 提交代码到SVN会提示有待处理注释 **

四、方法(函数)

方法重载

方法重载中 参数的使用,相同参数个数,不同参数类型的方法重载要注意调用方传递参数null时的问题。需要与调用方沟通协调好。

方法重载中 建议尽量不要使用Object 类型参数

//参数列一致时 方式参数为NULL
方法1:public void getIdListByCondition(Integer type, String userName, String email){}方法2:public void getIdListByCondition(Integer type, Date createTime, Integer age){}//建议不要使用Object 类型
方法3:public void getIdListByCondition(Integer type, Date createTime, Object obj){}
方法覆写

所有的覆写方法,必须加@Override 注解。

方法过时

对外暴露的接口签名,原则上不允许修改方法签名,避免对接口调用方产生影响。接

口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么.

equals 方法

Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
equals。
正例: “test”.equals(object);
反例: object.equals(“test”);
说明:推荐使用 java.util.Objects#equals (JDK7 引入的工具类)

入参保护
方法中需要进行参数校验的场景:
  1. 调用频次低的方法
  2. 执行时间开销很大的方法,参数校验时间几乎可以忽略不计,但如果因为参数错误导致
    中间执行回退,或者错误,那得不偿失。
  3. 需要极高稳定性和可用性的方法。
  4. 对外提供的开放接口,不管是 RPC/API/HTTP 接口。
  5. 敏感权限入口。

Hibernate Validator 校验框架使用说明

controller应该要加上@Valid ,否则不会验证!

// 非空判断
@NotNull(message = "id不能为空")
private Long id;@Future(message = "需要一个将来日期") // 只能是将来的日期
// @Past //只能去过去的日期
@DateTimeFormat(pattern = "yyyy-MM-dd") // 日期格式化转换
@NotNull // 不能为空
private Date date;@NotNull // 不能为空
@DecimalMin(value = "0.1") // 最小值0.1元
@DecimalMax(value = "10000.00") // 最大值10000元
private Double doubleValue = null;@Min(value = 1, message = "最小值为1") // 最小值为1
@Max(value = 88, message = "最大值为88") // 最大值88
@NotNull // 不能为空
private Integer integer;@Range(min = 1, max = 888, message = "范围为1至888") // 限定范围
private Long range;// 邮箱验证
@Email(message = "邮箱格式错误")
private String email;@Size(min = 20, max = 30, message = "字符串长度要求20到30之间。")
private String size;

使用说明

@Null               被注释的元素必须为null
@NotNull           被注释的元素不能为null
@AssertTrue        被注释的元素必须为true
@AssertFalse       被注释的元素必须为false
@Min(value)        被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)        被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)  被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)  被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min)     被注释的元素的大小必须在指定的范围内。
@Digits(integer,fraction)  被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past                  被注释的元素必须是一个过去的日期
@Future            被注释的元素必须是一个将来的日期
@Pattern(value)    被注释的元素必须符合指定的正则表达式。
@Email                 被注释的元素必须是电子邮件地址
@Length            被注释的字符串的大小必须在指定的范围内
@NotEmpty              被注释的字符串必须非空
@Range             被注释的元素必须在合适的范围内

**@NotEmpty, @NotBlank 与 @NotNull 的区别 **

@NotEmpty 用在集合类上面
@NotBlank 用在String上面
@NotNull  用在基本类型上

GROUP interface(分组) 具体用法自行百度

方法中不需要参数校验的场景:
  1. 极有可能被循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参
    数检查。
  2. 底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错
    误不太可能到底层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一
    台服务器中,所以 DAO 的参数校验,可以省略.
  3. 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参
    数已经做过检查或者肯定不会有问题,此时可以不校验参数.
公共代码抽取

避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。

说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副
本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块

五、流程控制语句

大括号

在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码,避免使用
下面的形式:if (condition) statements;

//反例
if(a == b) statements;
for(int i= 0;i<=100 i++) i += 1;
switch

在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程
序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且
放在最后,即使它什么代码也没有;

@Test
public void testSwitch() {int i = 0;switch (i) {case 0:System.out.println(i);break;case 1:System.out.println(i);break;case 2:System.out.println(i);break;default://必须包含  default break;}}

防止 switch穿透

@Test
public void testSwitch() {int i = 0;switch (i) {default:i ++;case 0:i ++;case 1:i ++;break;case 2:i --;break;}System.out.println(i);}
if

if-else 代码不允许使用超过 3 层的

if 优化前

 //这是一个type类型private int type = 0;
/*** 此方法根据type类型的不同处理不同的业务(此处作为测试代码简单带过)* 但是每次处理完业务后需在else语句做一个其他业务的处理*/public void getType() {if (type == 0) {} else {if (type == 1) {} else {if (type == 2) {} else {//若在此处在次判断type的值,这个嵌套会更加进行setType();}}}}

if 优化后

//优化成 卫语句
public void getTypeNew() {/***当type的值满足条件后,直接return掉,将这个方法终止。不再继续进行了。*若所有条件均不满足,则执行setType();这个最终else的业务处理*/if (type == 0) {return;}if (type == 1) {return;}if (type == 2) {return;}setType();}

判断条件

除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复
杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

很多 if 语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么
样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢?

正例:
//伪代码如下
boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {...
}
反例:
if ((file.open(fileName, "w") != null) && (...) || (...)) {...
}
for foreach while

循环体中的语句要考量性能,以下操作尽量移至循环体外处理

  • 获取数据库连接
  • 进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)

增强for循环中(foreach 中)不能改变被遍历对象的长度,(数组越界异常)

六、异常处理

try…catch的用法
try{//程序代码
}catch(Exception e){//为空,什么都不写
}//在任何场景中都禁止使用
try{//程序代码
}catch(Exception e){throw new runtimeException(e);//最优先采用的写法
}finally{}
基本常识
  • 不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:
    IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查
    来规避,保证程序健壮性。
  • 对大段代码进行 try-catch,这是不负责任的表现。catch 时请分清稳定代码和非稳
    定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分
    异常类型,再做对应的异常处理。
  • 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请
    将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的
    内容。
  • 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回
    滚事务
  • finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
  • 不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不
    会再执行 try 块中的 return 语句。
  • 方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分
    说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。
  • 定义时区分 unchecked / checked 异常,避免直接使用 RuntimeException 抛出,
    更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定义
    过的自定义异常,如:DAOException / ServiceException 等
防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
  1. 返回类型为包装数据类型,有可能是 null,返回 int 值时注意判空。
    反例:public int f(){ return Integer 对象}; 如果为 null,自动解箱抛 NPE。
  2. 数据库的查询结果可能为 null。
  3. 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
  4. 远程调用返回对象,一律要求进行 NPE 判断。
  5. 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
  6. 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
在代码中使用“抛异常”还是“返回错误码”
  • 对于公司外的 http/api 开放接口必须使用“错误码”;
  • 而应用内部推荐异常抛出;
  • 跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess、“错误码”、“错误简短信息”。
说明:关于 RPC 方法返回方式使用 Result 方式的理由:
  • 使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。
  • 如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用
    端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输
    的性能损耗也是问题

七、事务管理

自动提交(AutoCommit)与连接关闭时的是否自动提交

自动提交

默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果

执行失败则隐式的回滚事务

对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。不过,这个我们不用担心,spring会将底层连接的自动提交特性设置为false。对于正常的事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。不过,这个我们不用担心,spring会将底层连接的自动提交特性设置为false。

声明式事务管理也有两种常用的方式

一种是基于tx和aop名字空间的xml配置文件,
另一种就是基于@Transactional注解。
显然基于注解的方式更简单易用,更清爽。

Mybatis 框架自动开启事务
基于@Transactional注解的方式

用法

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,**我们也可以在方法级别使用该标注来覆盖类级别的定义。

虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

**建议 @Transactional 使用在实现类 或者实现类方法上 **
事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:(读已提交的数据)这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:(读未提交的数据)该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:(读已提交的数据)该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:(可重复读级别)该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:(不可重复读)所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
事务超时

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。

事务只读属性

只读事务用于客户代码只读但不修改数据的情形,只读事务用于特定情景下的优化,比如使用Hibernate的时候。
默认为读写事务。

“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。 但是你非要在“只读事务”里面修改数据,也并非不可以,只不过对于数据一致性的保护不像“读写事务”那样保险而已。 因此,“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可

spring事务回滚规则

指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。
可以明确的配置在抛出那些异常时回滚事务,包括checked异常。也可以明确定义那些异常抛出时不回滚事务。

还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

总结

什么时候rollback

  1. 运行期异常runtimeException,非运行期异常不会触发rollback
  2. 必须uncheck (没有catch) NullPointerException 继承自RuntimeException
  3. 不管什么异常,只要你catch了,spring就会放弃管理,就不会自动rollback

七、日志记录

日志级别
  • 等级

    由低到高:debug < info < warn < Error < Fatal;

  • 区别:

    debug 级别最低,可以随意的使用于任何觉得有利于在调试时更详细的了解系统运行状态的东东;

    info 重要,输出信息:用来反馈系统的当前状态给最终用户的;

    后三个,警告、错误、严重错误,这三者应该都在系统运行时检测到了一个不正常的状态。

    warn, 可修复,系统可继续运行下去;

    Error, 可修复性,但无法确定系统会正常的工作下去;

    Fatal, 相当严重,可以肯定这种错误已经无法修复,并且如果系统继续运行下去的话后果严重。

  • 使用

    调试用 debug

    什么时候使用 info, warn , error ?

    info 用于打印程序应该出现的正常状态信息, 便于追踪定位;

    warn 表明系统出现轻微的不合理但不影响运行和使用;

    error 表明出现了系统错误和异常,无法正常完成目标操作。

  • 格式

  • 意义

    错误日志是排查问题的重要手段之一。 当我们编程实现一项功能时, 通常会考虑可能发生的各种错误及相应原因:

    要排查出相应的原因, 就需要一些关键描述来定位原因。这就会形成三元组:错误现象 -> 错误关键描述 -> 最终的错误原因。

    需要针对每一种错误尽可能提供相应的错误关键描述,从而定位到相应的错误原因。也就是说,编程的时候,要仔细思考, 哪些描述是非常有利于定位错误原因的, 尽可能将这些描述添加到错误日志中。

基本常识
  • 代码中严禁使用System.out.println()进行调试输出,如果要使用调试信息,必须使用log.debug()。对于必要的信息使用log.info()进行输出;

  • 应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架
    SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static final Logger logger =             LoggerFactory.getLogger(Abc.class);
    
  • 日志文件推荐至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。

  • 可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适
    从。注意日志输出的级别,error 级别只记录系统逻辑出错、异常等重要的错误信息。如非必
    要,请不要在此场景打出 error 级别

  • 谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使
    用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘
    撑爆,并记得及时删除这些观察日志。
    说明:大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。记录日志时请
    思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?

JAVA 开发规范相关推荐

  1. java object转list_这份Java开发规范,让你100%受益!

    今天跟大家分享下Java开发规范的知识. 基于阿里巴巴JAVA开发规范整理: https://github.com/alibaba/p3c 1 命名风格 [强制]类名使用 UpperCamelCase ...

  2. 谈谈Google与微信H5牛牛的Java开发规范

    多年前,Google发布微信H5牛牛搭建平台(h5.fanshubbs.com)来定义Java编码时应遵循的微信牛牛Q_1687054422规范:今年年初阿里则发布阿里巴巴Java 开发手册,并随后迭 ...

  3. JAVA 开发规范标准(集合)

    JAVA 开发规范 一.编程规约 (一)命名规约 1. [强制] 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符 号结束. 反例:_name / __name / $Object / ...

  4. 谈谈阿里与谷歌的Java开发规范

    无规矩不成方圆,编码规范就如同协议,有了Http.TCP等各种协议,计算机之间才能有效地通信,同样的,有了一致的编码规范,程序员之间才能有效地合作.道理大家都懂,可现实中的我们,经常一边吐槽别人的代码 ...

  5. 谈谈ali与Google的Java开发规范

    无规矩不成方圆,编码规范就如同协议,有了Http.TCP等各种协议,计算机之间才能有效地通信,同样的,有了一致的编码规范,程序员之间才能有效地合作.道理大家都懂,可现实中的我们,经常一边吐槽别人的代码 ...

  6. hualinux java 1.17:java开发规范(新手必看)

    有不少java初学者都不知道java开发规范的,所以网上找了一个国内电商算是权威的java开发手册 国内最大的电商就是淘宝了,我们向老大学习. 根据<阿里巴巴Java开发手册>中" ...

  7. Java开发规范整理

    Java开发规范整理 (参考<Java开发手册嵩山版>) 文章目录 Java开发规范整理 一.编程规约 (一)命名 (二)常量定义 (三)代码格式 (四)OOP面向对象程序设计 (五)时间 ...

  8. Java开发规范及注意事项

    文章目录 Java开发规范及注意事项 编程规约 异常日志违约 单元测试规约 工程结构规约 数据库规约 Java开发规范及注意事项 编程规约 POJO类中布尔类型的变量,都不要加is前缀,否则部分框架解 ...

  9. 「 Java开发规范 」10人小团队Java开发规范参考这篇就够了

    <菜鸟程序员成长计划>之团队高效合作[开发规范篇] 1.「 Java开发规范 」10人小团队Java开发规范参考这篇就够了! 2.「 前端开发规范 」10人小团队前端开发规范参考这篇就够了 ...

  10. Java开发规范(阿里+腾讯)

    如何适应企业的标准化开发? 文章目录 前言 腾讯开发规范整理(精简) 阿里开发规范整理(精简) 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越 ...

最新文章

  1. 收藏!中国卫星互联网产业发展白皮书
  2. NSArray,NSSet,NSDictionary总结 (转)
  3. 宁波网络推广介绍几点容易被优化人员忽略的图片优化技巧!
  4. JS动态加载脚本及对动态脚本内方法的调用
  5. nyoj1306海拔
  6. python字典默认输出键还是值_说说在 Python 字典中如何在读取不存在的键时得到一个默认值...
  7. SAP Spartacus如何为不同的environment设置不同的baseUrl
  8. [分布式训练] 单机多卡的正确打开方式:Horovod
  9. html5中音频、视频标签、自定义播放器常用属性及方法、全屏操作、新增属性兼容问题
  10. PHP数组缓存:三种方式JSON、序列化和var_export的比较
  11. curl 使用 ~/.netrc ( Windows 上是 _netrc ) 问题
  12. NetOps Defined
  13. offer拿到手软,java分布式面试题及答案
  14. 图文教程使用一套键鼠控制两台电脑
  15. Win11 Windows聚焦不更新了怎么解决?聚焦锁屏图片不更换怎么办
  16. day12 三大神器
  17. C语言用随机函数做猜拳游戏,c语言猜拳游戏
  18. [渝粤教育] 南通职业大学 艺术导论2021 参考 资料
  19. 2021年山东省职业院校技能大赛中职组网络安全赛项竞赛样题
  20. samba 配置文件详解

热门文章

  1. 计算机安装固态硬盘后启动不稳定,完美解决win7系统安装固态硬盘后开机慢的解决方法...
  2. 服务器更换固态后如何安装系统,更换固态硬盘后安装操作系统的两种常用方法...
  3. WIN11电脑如何使用IE浏览器进行正常办公操作-以建行网银为例
  4. 中国十大会计师事务所排名公布!刚刚,中注协正式通知!
  5. 二手升腾网络计算机,瘦客户机终端网络计算机专用计算机
  6. 一起学习荷花定律/金蝉定律和竹子定律
  7. 技术笔记:.Net全套就业班视频教程——数据库
  8. 清明出行之高德路况思考
  9. c语言学习笔记(7)单引号,双引号和逻辑符号的用法
  10. 固定螺栓系统行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)