当前位置: 首页 > news >正文

iis 建立子网站如何制作属于自己的网页

iis 建立子网站,如何制作属于自己的网页,贵阳微网站建设,wordpress可以上传文件吗摘要 本文主要介绍了Java开发中的日志治理经验#xff0c;包括系统异常日志、接口摘要日志、详细日志和业务摘要日志的定义和目的#xff0c;以及错误码规范和异常处理规范。强调了日志治理的重要性和如何通过规范化错误码和日志格式来提高系统可观测性和问题排查效率。 1. …摘要 本文主要介绍了Java开发中的日志治理经验包括系统异常日志、接口摘要日志、详细日志和业务摘要日志的定义和目的以及错误码规范和异常处理规范。强调了日志治理的重要性和如何通过规范化错误码和日志格式来提高系统可观测性和问题排查效率。 1. 错误码规范 【强制】错误码的制定原则:快速溯源、沟通标准化。 说明: 错误码想得过于完美和复杂就像康熙字典中的生僻字一样用词似乎精准但是字典不容易随身携带并且简单易懂。 正例: 错误码回答的问题是谁的错?错在哪? 错误码必须能够快速知晓错误来源可快速判断是谁的问题。错误码必须能够进行清晰地比对(代码中容易 equals)。错误码有利于团队快速对错误原因达 到一致认知。 【强制】错误码不体现版本号和错误等级信息。 说明:错误码以不断追加的方式进行兼容。错误等级由日志和错误码本身的释义来决定。 【强制】全部正常但不得不填充错误码时返回五个零:00000。 【强制】错误码为字符串类型共5位分成两个部分:错误产生来源四位数字编号。 说明: 错误产生来源分为 A/B/CA 表示错误来源于用户比如参数错误用户安装版本过低用户支付 超时等问题;B 表示错误来源于当前系统往往是业务逻辑出错或程序健壮性差等问题;C 表示错误来源 于第三方服务比如 CDN 服务出错消息投递超时等问题;四位数字编号从 0001 到 9999大类之间的步长间距预留 100。 【强制】编号不与公司业务架构更不与组织架构挂钩以先到先得的原则在统一平台上进行审批生效编号即被永久固定。 【强制】错误码使用者避免随意定义新的错误码。 说明: 尽可能在原有错误码附表中找到语义相同或者相近的错误码在代码中使用即可。 假设系统已有的错误码表如下 错误码 错误描述 1001 用户名不存在 1002 密码错误 2001 用户未授权 3001 请求参数缺失 3002 参数格式错误 不建议的做法当遇到一个新的错误情况时比如“用户输入的邮箱地址格式错误”如果开发者随意定义新的错误码 public static final int ERROR_INVALID_EMAIL_FORMAT 4001; // 随意定义错误码 推荐的做法首先检查现有的错误码表中是否有适合的错误码。如果已存在类似的错误码如3002参数格式错误可以在代码中使用已有的错误码或者选择一个相关的错误码。 public static final int ERROR_INVALID_EMAIL_FORMAT 3002; // 采用已有的错误码 如果系统的错误码表没有类似的错误码可以考虑增加新的错误码但应该尽量确保其语义清晰避免误解 public static final int ERROR_INVALID_EMAIL_FORMAT 4002; // 增加新的错误码避免与其他错误码冲突 【强制】错误码不能直接输出给用户作为提示信息使用。 说明: 堆栈(stack_trace)、错误信息(error_message)、错误码(error_code)、提示信息(user_tip) 是一个有效关联并互相转义的和谐整体但是请勿互相越俎代庖。 错误码不应该直接作为提示信息输出给用户。错误码是开发人员用于定位问题、分析故障的工具而不是直接与用户交互的内容。错误码应该与用户提示信息分开避免让用户看到冗长或无意义的错误码从而影响用户体验。 错误处理的正确做法 // 错误码定义 public static final int ERROR_INVALID_EMAIL_FORMAT 4002;// 错误处理方法 public void handleInvalidEmail(String email) {try {// 检查邮箱格式是否有效if (!isValidEmail(email)) {// 如果邮箱格式无效抛出自定义异常throw new CustomException(ERROR_INVALID_EMAIL_FORMAT, 邮箱格式错误);}} catch (CustomException e) {// 输出堆栈信息给开发者e.printStackTrace();// 记录错误信息logError(e.getErrorCode(), e.getErrorMessage());// 给用户友好的提示信息showUserTip(请输入有效的邮箱地址例如 userexample.com。);} } 错误信息输出 开发者看到 ERROR_INVALID_EMAIL_FORMAT 和详细的错误信息如 邮箱格式错误以及堆栈信息用于定位代码中的问题。用户看到简洁的提示信息 请输入有效的邮箱地址例如 userexample.com。这是用户可以理解并采取相应行动的内容。 错误码与提示信息的区分 错误码ERROR_INVALID_EMAIL_FORMAT开发者使用。错误信息邮箱格式错误开发者记录日志时使用。用户提示信息请输入有效的邮箱地址例如 userexample.com。直接展示给用户。 不推荐的做法 public void handleInvalidEmail(String email) {try {if (!isValidEmail(email)) {// 错误码直接暴露给用户throw new CustomException(4002, 错误码 4002: 邮箱格式错误);}} catch (CustomException e) {// 将错误码直接展示给用户showUserTip(错误码 4002: 邮箱格式错误);} } 【推荐】在获取第三方服务错误码时向上抛出允许本系统转义由C转为B,并且在错误信息上带上原有的第三方错误码。 在系统集成时往往需要将第三方服务的错误码转化为本系统的错误码这样既能让系统的错误码保持一致性又能确保第三方错误信息不丢失并提供给用户或开发者必要的上下文信息。为了实现这一目标可以通过异常机制将第三方错误码捕获后转义为本系统的错误码并且在错误信息中附带原有的第三方错误码确保错误的追踪和调试不受影响。 第三方错误码转义 假设我们的系统调用了一个第三方支付服务而该支付服务返回的错误码格式为 TS_ERR_1001。我们希望将这个错误码转义为系统内部的错误码 5001并且将第三方错误码和错误信息一起传递到本系统。 定义第三方错误码和系统内部错误码 // 第三方错误码 public static final String THIRD_PARTY_ERR_1001 TS_ERR_1001; public static final String THIRD_PARTY_ERR_1002 TS_ERR_1002;// 系统内部错误码 public static final int ERROR_PAYMENT_FAILED 5001; public static final int ERROR_INVALID_PAYMENT_METHOD 5002; 封装第三方错误码的异常类 首先定义一个自定义异常类用于封装来自第三方服务的错误信息并在其中记录第三方错误码。 public class ThirdPartyException extends Exception {private final String thirdPartyErrorCode;public ThirdPartyException(String message, String thirdPartyErrorCode) {super(message);this.thirdPartyErrorCode thirdPartyErrorCode;}public String getThirdPartyErrorCode() {return thirdPartyErrorCode;} } 服务调用和错误处理 在服务调用过程中如果第三方服务返回错误我们可以通过异常处理将第三方错误转义为本系统的错误并在错误信息中附带第三方的错误码。 public class PaymentService {public void processPayment(String paymentDetails) throws CustomException {try {// 调用第三方支付服务并捕获其可能抛出的异常String thirdPartyErrorCode callThirdPartyPaymentService(paymentDetails);if (thirdPartyErrorCode ! null) {// 根据第三方错误码转义为系统内部的错误码throw new ThirdPartyException(支付失败第三方服务错误, thirdPartyErrorCode);}} catch (ThirdPartyException e) {// 转义第三方错误码为本系统的错误码if (e.getThirdPartyErrorCode().equals(THIRD_PARTY_ERR_1001)) {throw new CustomException(ERROR_PAYMENT_FAILED, 支付失败错误码: e.getThirdPartyErrorCode());} else if (e.getThirdPartyErrorCode().equals(THIRD_PARTY_ERR_1002)) {throw new CustomException(ERROR_INVALID_PAYMENT_METHOD, 无效支付方式错误码: e.getThirdPartyErrorCode());} else {// 处理其他未知的错误码throw new CustomException(9999, 未知错误错误码: e.getThirdPartyErrorCode());}}}private String callThirdPartyPaymentService(String paymentDetails) {// 模拟调用第三方支付服务返回错误码return THIRD_PARTY_ERR_1001; // 假设返回的是第三方的错误码} } 自定义异常类 (CustomException) 为了便于处理系统内部的错误可以创建一个自定义异常类 CustomException将错误码和错误信息封装起来。 public class CustomException extends Exception {private final int errorCode;private final String errorMessage;public CustomException(int errorCode, String errorMessage) {super(errorMessage);this.errorCode errorCode;this.errorMessage errorMessage;}public int getErrorCode() {return errorCode;}public String getErrorMessage() {return errorMessage;} } 调用并处理错误 调用服务时通过异常捕获机制可以得到转义后的错误信息。 public class Main {public static void main(String[] args) {PaymentService paymentService new PaymentService();try {paymentService.processPayment(paymentDetails);} catch (CustomException e) {// 打印系统内部错误码和错误信息System.out.println(系统错误码: e.getErrorCode());System.out.println(错误信息: e.getErrorMessage());}} } 错误处理流程总结 第三方服务错误当第三方服务返回错误时抛出 ThirdPartyException 异常并带上第三方的错误码。错误码转义系统通过捕获该异常根据第三方错误码转义为本系统的错误码同时在错误信息中带上第三方的错误码帮助开发者调试。错误信息反馈给用户系统向用户返回的错误提示信息不包含技术细节如第三方错误码而是使用系统错误码和友好的提示信息。 【推荐】在获取第三方服务错误码时向上抛出允许本系统转义由C转为B并且在错误信 息上带上原有的第三方错误码。 【参考】错误码分为一级宏观错误码、二级宏观错误码、三级宏观错误码。 说明:在无法更加具体确定的错误场景中可以直接使用一级宏观错误码分别是:A0001(用户端错误)、 B0001(系统执行出错)、C0001(调用第三方服务出错)。 正例: 调用第三方服务出错是一级中间件错误是二级消息服务出错是三级。 【参考】错误码的后三位编号与 HTTP 状态码没有任何关系。 【参考】错误码有利于不同文化背景的开发者进行交流与代码协作。 说明:英文单词形式的错误码不利于非英语母语国家(如阿拉伯语、希伯来语、俄罗斯语等)之间的开发 者互相协作。 【参考】错误码即人性感性认知口口相传使用纯数字来进行错误码编排不利于感性记忆和分类。 说明: 数字是一个整体每位数字的地位和含义是相同的。 反例: 一个五位数字 12345第 1 位是错误等级第 2 位是错误来源345 是编号人的大脑不会主动地 拆开并分辨每位数字的不同含义。 2. 异常处理规范 【强制】Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过catch 的方式来处理比如:NullPointerExceptionIndexOutOfBoundsException 等等。 说明无法通过预检查的异常除外比如在解析字符串形式的数字时可能存在数字格式错误不得不 通过catch NumberFormatException来实现。 正例if(obj!nul){} 反例try{ obj.method(); } catch ( NullPointerException e){ . } 错误的做法使用异常进行流程控制 public class OrderService {public void processOrder(int orderId) {try {// 用异常捕获条件控制的流程if (orderId 0) {throw new IllegalArgumentException(订单ID无效);}// 正常的订单处理逻辑System.out.println(处理订单 orderId);} catch (IllegalArgumentException e) {// 处理异常用异常控制流程这种做法是错误的System.out.println(捕获异常订单ID无效);}} } 问题在上面的代码中使用 throw 和 catch 来控制流程判断 orderId 是否有效这明显是不合适的。orderId 0 只是一个普通的条件检查完全可以使用条件判断来处理而不需要抛出异常。 正确的做法使用条件判断来控制流程 public class OrderService {public void processOrder(int orderId) {// 使用条件判断处理正常的逻辑流程if (orderId 0) {System.out.println(订单ID无效);return; // 直接返回不继续处理}// 正常的订单处理逻辑System.out.println(处理订单 orderId);} } 错误的异常用法会导致性能问题 假设我们在一个循环中每次都要检查条件并且抛出异常 public void processOrders(ListInteger orderIds) {for (int orderId : orderIds) {try {// 通过异常进行流程控制if (orderId 0) {throw new IllegalArgumentException(订单ID无效);}// 处理订单System.out.println(处理订单 orderId);} catch (IllegalArgumentException e) {System.out.println(捕获异常订单ID无效);}} } 这种写法会导致 每次循环都会进行异常处理严重影响性能。异常捕获会增加系统的资源消耗特别是在异常发生频繁时。 public void processOrders(ListInteger orderIds) {for (int orderId : orderIds) {// 使用条件判断进行流程控制if (orderId 0) {System.out.println(订单ID无效);continue; // 直接跳过这个订单继续下一个}// 处理有效订单System.out.println(处理订单 orderId);} } 【强制】catch时请分清稳定代码和非稳定代码稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型再做对应的异常处理。 说明:对大段代码进行 try-catch使程序无法根据不同的异常做出正确的应激反应也不利于定位问题这是一种不负责任的表现。 正例: 用户注册的场景中如果用户输入非法字符或用户名称已存在或用户输入密码过于简单在程序上作出分门别类的判断并提示给用户。 【强制】捕获异常是为了处理它不要捕获了却什么都不处理而抛弃之如果不想处理它请 将该异常抛给它的调用者。最外层的业务使用者必须处理异常将其转化为用户可以理解的内容。 【强制】事务场景中抛出异常被catch后如果需要回滚一定要注意手动回滚事务。 在实际项目中通常会使用 Spring 框架的事务管理来简化事务的控制。Spring 提供了 Transactional 注解它会自动处理事务的开始、提交、回滚等过程。 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;Service public class BankService {Autowiredprivate AccountRepository accountRepository;Transactionalpublic void transferFunds(int fromAccountId, int toAccountId, double amount) {// 减少账户余额Account fromAccount accountRepository.findById(fromAccountId);if (fromAccount.getBalance() amount) {throw new IllegalArgumentException(余额不足);}fromAccount.setBalance(fromAccount.getBalance() - amount);accountRepository.save(fromAccount);// 增加目标账户余额Account toAccount accountRepository.findById(toAccountId);if (toAccount null) {throw new IllegalArgumentException(目标账户不存在);}toAccount.setBalance(toAccount.getBalance() amount);accountRepository.save(toAccount);// 如果这里抛出异常Spring 会自动回滚事务} } Spring 事务管理的特点 自动管理事务使用 Transactional 注解时Spring 会自动开启事务并且在方法执行结束后自动提交或回滚事务。异常回滚如果方法执行过程中抛出了异常Spring 会自动回滚事务。默认情况下RuntimeException 和 Error 会导致回滚而 checked exception 默认不会回滚可以通过 Transactional(rollbackFor Exception.class) 指定回滚条件。简化代码无需手动管理事务的开始、提交和回滚极大简化了代码。 【强制】finally块必须对资源对象、流对象进行关闭有异常也要做try-catch。 说明:如果 JDK7 及以上可以使用 try-with-resources 方式。 finally 块关闭资源传统做法 import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream;public class ResourceManagement {public void readFile(String filePath) {InputStream inputStream null;try {inputStream new FileInputStream(filePath);// 执行文件读取操作System.out.println(读取文件内容);} catch (IOException e) {// 处理异常System.out.println(文件读取异常 e.getMessage());} finally {// 确保资源被关闭if (inputStream ! null) {try {inputStream.close();} catch (IOException e) {System.out.println(关闭流时发生异常 e.getMessage());}}}}public static void main(String[] args) {ResourceManagement rm new ResourceManagement();rm.readFile(test.txt);} } 问题分析 finally 块用于确保资源被关闭即使在 try 或 catch 块中发生异常。但在 finally 块中关闭流时仍然可能抛出异常这时需要在 finally 块内再嵌套一个 try-catch 来捕获异常。代码冗长且资源关闭的逻辑分散在不同的地方可能增加出错的风险。 JDK 7 及以上的 try-with-resources推荐做法 从 JDK 7 开始引入了 try-with-resources 语法能够自动关闭实现了 AutoCloseable 接口如 InputStream、OutputStream、Connection 等的资源。它通过实现 AutoCloseable 接口的 close() 方法来确保资源的关闭因此我们不再需要显式地在 finally 块中关闭资源。 import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream;public class ResourceManagement {public void readFile(String filePath) {// 使用 try-with-resources 语法自动关闭资源try (InputStream inputStream new FileInputStream(filePath)) {// 执行文件读取操作System.out.println(读取文件内容);} catch (IOException e) {// 处理异常System.out.println(文件读取异常 e.getMessage());}// 无需显式关闭流资源会自动关闭}public static void main(String[] args) {ResourceManagement rm new ResourceManagement();rm.readFile(test.txt);} } try-with-resources 的优势 自动关闭资源当 try-with-resources 块执行完毕时JVM 会自动调用资源的 close() 方法无需显式地调用 close()避免了资源泄漏。代码更简洁代码更加简洁且易读避免了冗长的 finally 块。异常处理更简便如果资源在关闭过程中抛出异常它会被添加到原有异常的 suppressed 异常中而不需要额外的 try-catch。 【强制】不要在 finally 块中使用 return。 说明:try 块中的 return 语句执行成功后并不马上返回而是继续执行 finally 块中的语句如果此处存 在return 语句则在此直接返回无情丢弃掉 try 块中的返回点。 为什么不能在 finally 块中使用 return 覆盖返回值finally 中的 return 会覆盖 try 或 catch 中的返回值导致原本应该返回的值被替换。影响程序逻辑finally 中的 return 会导致无法清晰判断哪个返回值应该被执行。可维护性差其他开发者阅读代码时会对 finally 中的 return 产生困惑不知道程序到底返回了什么。 错误示例在 finally 块中使用 return public class ReturnInFinallyExample {public int exampleMethod() {int result 0;try {result 10;// 模拟某种异常if (result 10) {throw new Exception(Exception in try block);}} catch (Exception e) {result 20;} finally {// 错误在 finally 块中使用 returnreturn 30; // 这会覆盖 try 或 catch 中的返回值}// 这个返回值永远不会被执行return result; // 这个返回值将被覆盖}public static void main(String[] args) {ReturnInFinallyExample example new ReturnInFinallyExample();System.out.println(example.exampleMethod()); // 输出 30而不是 20 或 10} } 正确的做法避免在 finally 块中使用 return 通常我们应该在 finally 块中执行必要的清理工作而不是返回值。如果确实需要返回值可以将返回值的处理放在 try 或 catch 中确保 finally 块中不会有返回语句。 public class ReturnInFinallyExample {public int exampleMethod() {int result 0;try {result 10;// 模拟某种异常if (result 10) {throw new Exception(Exception in try block);}} catch (Exception e) {result 20;} finally {// 在 finally 中进行清理工作但不返回值System.out.println(Finally block executed);}// 返回结果return result;}public static void main(String[] args) {ReturnInFinallyExample example new ReturnInFinallyExample();System.out.println(example.exampleMethod()); // 输出 20} } 【强制】捕获异常与抛异常必须是完全匹配或者捕获异常是抛异常的父类。 说明: 如果预期对方抛的是绣球实际接到的是铅球就会产生意外情况。 【强制】在调用 RPC、二方包、或动态生成类的相关方法时捕捉异常必须使用 Throwable类来进行拦截。 说明: 通过反射机制来调用方法如果找不到方法抛出 NoSuchMethodException。什么情况会抛出 NoSuchMethodError 呢?二方包在类冲突时仲裁机制可能导致引入非预期的版本使类的方法签名不匹配 或者在字节码修改框架(比如:ASM)动态创建或修改类时修改了相应的方法签名。这些情况即使代 码编译期是正确的但在代码运行期时会抛出 NoSuchMethodError。 在调用 RPC (Remote Procedure Call)、二方包即第三方库或 动态生成类 的相关方法时捕捉异常需要使用 Throwable 类来进行拦截而不仅仅是捕捉 Exception。原因是Throwable 是 Java 中所有错误和异常的父类包含了两种类型的对象 Exception通常用于表示程序中能够预料到的异常情况。Error表示 JVM 或底层运行环境发生的严重错误例如 OutOfMemoryError、StackOverflowError 等通常不可恢复。 在调用这些方法时尤其是在网络请求、动态类加载或不受控制的第三方库调用中可能会遇到 Error 类型的错误导致系统崩溃。因此捕获 Throwable 可以确保所有可能的错误无论是 Exception 还是 Error都能被捕获并做出相应处理。 捕捉 Throwable 类的示例假设我们有一个 RPC 调用或二方包的调用可能会抛出未知的 Error 或 Exception我们希望在捕获异常时处理所有情况。 捕捉 Throwable 类来拦截所有异常和错误 public class RpcClient {public void makeRpcCall() {try {// 假设这是一个 RPC 调用可能会抛出异常或错误callRemoteService();} catch (Throwable t) {// 捕捉所有异常和错误System.out.println(捕获到异常或错误: t.getMessage());// 这里可以做异常处理或者记录日志等}}private void callRemoteService() throws Exception {// 模拟远程调用抛出异常throw new Exception(远程服务调用失败);}public static void main(String[] args) {RpcClient client new RpcClient();client.makeRpcCall();} } 为什么需要捕捉 Throwable RPC 调用、二方包、动态生成类的操作可能引发 Error这些操作可能会遇到网络中断、服务不可用等导致的 Error比如 OutOfMemoryError 或 UnknownHostException这些是我们通常不预期的严重错误但它们仍然会影响程序的正常运行。无法预见的异常和错误调用外部服务时可能会遇到一些底层错误例如JVM 内存问题等这些错误是 Exception 类无法捕获的。保证稳定性通过捕获 Throwable可以确保程序能够稳健地处理异常和错误而不至于因为未处理的 Error 导致程序崩溃。 动态生成类的调用假设我们使用反射或动态代理来调用一些方法这些方法可能会抛出异常或错误我们同样需要使用 Throwable 来捕获所有可能的异常和错误。 通过反射调用动态生成的类方法时捕捉 Throwable import java.lang.reflect.Method;public class DynamicMethodInvocation {public void invokeMethod() {try {// 动态生成或反射调用某个类的方法Class? clazz Class.forName(com.example.SomeService);Object instance clazz.getDeclaredConstructor().newInstance();Method method clazz.getMethod(someMethod);method.invoke(instance);} catch (Throwable t) {// 捕捉所有异常和错误System.out.println(捕获到异常或错误: t.getMessage());// 可以进一步处理或者记录错误}}public static void main(String[] args) {DynamicMethodInvocation invocation new DynamicMethodInvocation();invocation.invokeMethod();} } 二方包调用时捕捉 Throwable 假设我们使用第三方库来发送消息或执行某些操作这些操作可能会抛出 Exception 或 Error。 捕捉二方包的异常和错误 import org.apache.commons.lang3.StringUtils;public class ThirdPartyLibraryUsage {public void useLibraryMethod() {try {// 使用一个第三方库例如 Apache Commons LangString input null;String result StringUtils.capitalize(input); // 可能会抛出异常System.out.println(result);} catch (Throwable t) {// 捕捉所有异常和错误System.out.println(捕获到异常或错误: t.getMessage());// 进行适当的错误处理}}public static void main(String[] args) {ThirdPartyLibraryUsage usage new ThirdPartyLibraryUsage();usage.useLibraryMethod();} } 捕捉 Throwable 的优缺点 优点 全局捕获异常和错误可以保证程序不会因为未处理的 Error 崩溃能够适当地记录、处理或者上报这些错误。稳定性通过捕获所有 Throwable 类型的对象程序能够尽量保证即使出现无法预见的错误也能稳定运行。 缺点 过度捕获捕获 Throwable 会捕获所有错误类型包括 Error例如 OutOfMemoryError 等严重错误。如果这些错误发生时程序可能已经处于无法恢复的状态捕获并继续执行可能并不合适。对于严重错误通常应该让程序终止并进行适当的资源释放。异常处理不够精确捕获 Throwable 可能会掩盖一些具体的异常类型导致代码的异常处理逻辑不够明确。 【推荐】方法的返回值可以为 null不强制返回空集合或者空对象等必须添加注释充分说 明什么情况下会返回 null 值。 说明: 本手册明确防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象对调用者来说也 并非高枕无忧必须考虑到远程调用失败、序列化失败、运行时异常等场景返回 null 的情况。 【推荐】防止 NPE是程序员的基本修养注意 NPE 产生的场景: 返回类型为基本数据类型return 包装数据类型的对象时自动拆箱有可能产生 NPE。反例: public int f() { return Integer 对象} 如果为 null自动解箱抛 NPE。数据库的查询结果可能为 null。集合里的元素即使 isNotEmpty取出的数据元素也可能为 null。远程调用返回对象时一律要求进行空指针判断防止 NPE。对于 Session 中获取的数据建议进行 NPE 检查避免空指针。级联调用 obj.getA().getB().getC();一连串调用易产生 NPE。 正例: 使用 JDK8 的 Optional 类来防止 NPE 问题。 常见的导致 NPE 的场景 调用 null 对象的方法访问 null 对象的属性尝试对 null 数组元素进行操作集合对象为 null 时进行操作反射调用 null 对象 防止 NPE 的最佳实践 空值检查在操作对象之前总是进行空值检查。Optional 使用在不确定对象是否为 null 时可以使用 Optional 来避免直接操作 null。使用工具类使用 Objects.requireNonNull() 来验证传入对象不为 null从而避免出现 NPE。尽量避免返回 null如果可能避免返回 null使用空对象或者 Optional 类型来代替。使用现代 APIJava 8 引入的 Stream、Optional 等 API 提供了更多处理空值的优雅方式避免了直接与 null 打交道。 NullPointerException (NPE) 常发生在访问 null 对象的方法、属性、数组元素时。避免 NPE 是编程的基本修养。预防 NPE 的方法检查对象是否为 null使用 Optional 类型使用 Java 8 的流式操作避免返回 null以及使用 Objects.requireNonNull() 来明确断言非空。使用现代工具和 API如 Optional、Stream、Objects 等工具类可以大大减少由于 null 导致的问题。 【推荐】定义时区分 unchecked / checked 异常避免直接抛出 new RuntimeException() 更不允许抛出 Exception 或者 Throwable应使用有业务含义的自定义异常。 推荐业界已定义过的自定义异常如:DAOException/ServiceException等。 【参考】对于公司外的 http/api 开放接口必须使用errorCode; 而应用内部推荐异常抛出; 跨应用间 RPC 调用优先考虑使用 Result方式封装 isSuccess()方法、errorCode、 errorMessage;而应用内部直接抛出异常即可。 说明: 关于 RPC 方法返回方式使用 Result 方式的理由: 使用抛异常返回方式调用方如果没有捕获到就会产生运行时错误。如果不加栈信息只是 new 自定义异常加入自己的理解的 error message对于调用端解决问题 的帮助不会太多。如果加了栈信息在频繁调用出错的情况下数据序列化和传输的性能损耗也是问题。 3. 日志规约 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API而应依赖使用日志框架 (SLF4J、JCL--Jakarta Commons Logging)中的 API使用门面模式的日志框架有利于维护和 各个类的日志处理方式统一。 说明:日志框架(SLF4J、JCL--Jakarta Commons Logging)的使用方式(推荐使用 SLF4J) // 使用 SLF4J: import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger logger LoggerFactory.getLogger(Test.class); // 使用 JCL: import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; private static final Log log LogFactory.getLog(Test.class); 【强制】所有日志文件至少保存15天因为有些异常具备以“周”为频次发生的特点。对于当天日志以“应用名.log”来保存保存在/home/admin/应用名/logs/目录下过往日志 格式为: {logname}.log.{保存日期}日期格式:yyyy-MM-dd 正例: 以 aap 应用为例日志保存在/home/admin/aapserver/logs/aap.log历史日志名称为 app.log.2016-08-01 【强制】根据国家法律网络运行状态、网络安全事件、个人敏感信息操作等相关记录留存 的日志不少于六个月并且进行网络多机备份。 【强制】应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:appName_logType_logName.log。logType:日志类型如 stats/monitor/access 等;logName:日志描 述。这种命名的好处:通过文件名就可知道日志文件属于什么应用什么类型什么目的也有利于归类查找。 说明: 推荐对日志进行分类如将错误日志和业务日志分开存放便于开发人员查看也便于通过日志对系统进行及时监控。 正例: mppserver 应用中单独监控时区转换异常如:mppserver_monitor_timeZoneConvert.log 【强制】在日志输出时字符串变量之间的拼接使用占位符的方式。 说明: 因为 String 字符串的拼接会使用 StringBuilder 的 append()方式有一定的性能损耗。使用占位符仅是替换动作可以有效提升性能。 正例: logger.debug(Processing trade with id: {} and symbol: {}, id, symbol); 【强制】对于 trace/debug/info 级别的日志输出必须进行日志级别的开关判断。 说明:虽然在 debug(参数)的方法体内第一行代码 isDisabled(Level.DEBUG_INT)为真时(Slf4j 的常见实现 Log4j 和 Logback)就直接 return但是参数可能会进行字符串拼接运算。此外如果 debug(getName())这种参数内有 getName()方法调用无谓浪费方法调用的开销。 正例: // 如果判断为真那么可以输出 trace 和 debug 级别的日志 if (logger.isDebugEnabled()) {logger.debug(Current ID is: {} and name is: {}, id, getName()); } 【强制】避免重复打印日志浪费磁盘空间务必在日志配置文件中设置 additivityfalse。 正例:logger namecom.taobao.dubbo.config additivityfalse 【强制】生产环境禁止直接使用 System.out 或 System.err 输出日志或使用 e.printStackTrace()打印异常堆栈。 说明: 标准日志输出与标准错误输出文件每次 Jboss 重启时才滚动如果大量输出送往这两个文件容易 造成文件大小超过操作系统大小限制。 错误示例直接使用 System.out 或 System.err public class BadLoggingExample {public static void main(String[] args) {try {// 模拟一些代码可能会抛出异常int result 10 / 0;} catch (Exception e) {// 错误做法直接使用 System.err 打印异常堆栈System.err.println(An error occurred: e.getMessage());e.printStackTrace(); // 这会将堆栈信息打印到控制台}// 错误做法直接使用 System.out 打印日志System.out.println(This is a log message);} } 正确示例使用日志框架如 Logback dependencies!-- SLF4J API --dependencygroupIdorg.slf4j/groupIdartifactIdslf4j-api/artifactIdversion2.0.0/version/dependency!-- Logback 实现 --dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.4.7/version/dependency /dependencies 配置 logback.xml 然后在项目的 src/main/resources 目录下创建 logback.xml 文件配置日志输出到控制台和文件 configuration!-- 控制台输出 --appender nameconsole classch.qos.logback.core.ConsoleAppenderencoderpattern%d{yyyy-MM-dd HH:mm:ss} - %msg%n/pattern/encoder/appender!-- 文件输出 --appender namefile classch.qos.logback.core.FileAppenderfilelogs/app.log/fileencoderpattern%d{yyyy-MM-dd HH:mm:ss} - %msg%n/pattern/encoder/appender!-- 根日志配置 --root levelINFOappender-ref refconsole /appender-ref reffile //root/configuration 使用 SLF4J 记录日志 在代码中使用 SLF4J API 来记录日志替换掉 System.out 和 System.err。 import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class GoodLoggingExample {// 创建日志记录器private static final Logger logger LoggerFactory.getLogger(GoodLoggingExample.class);public static void main(String[] args) {try {// 模拟一些代码可能会抛出异常int result 10 / 0;} catch (Exception e) {// 使用 SLF4J 记录异常信息logger.error(An error occurred: {}, e.getMessage(), e); // 记录异常及堆栈信息}// 使用 SLF4J 记录普通日志logger.info(This is a log message);} } 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理那么通过关键字 throws 往上抛出。 正例: logger.error(inputParams:{} and errorMessage:{}, 各类参数或者对象 toString(), e.getMessage(), e); 【强制】日志打印时禁止直接用 JSON 工具将对象转换成 String。 说明: 如果对象里某些 get 方法被覆写存在抛出异常的情况则可能会因为打印日志而影响正常业务流 程的执行。 正例: 打印日志时仅打印出业务相关属性值或者调用其对象的 toString()方法。 在日志打印时直接使用 JSON 工具将对象转换成 String例如使用 JSON.toJSONString() 或 ObjectMapper.writeValueAsString() 等并不推荐。原因如下 性能问题将对象转换为 JSON 字符串可能会导致性能问题尤其是在高并发环境下频繁进行对象的序列化会占用不必要的资源。日志污染如果对象包含大量字段或者嵌套对象直接将整个对象序列化为 JSON 字符串会导致日志输出过长增加阅读和排查问题的难度。敏感信息泄露直接序列化整个对象可能会泄露敏感信息尤其是当对象包含密码、身份标识符或其他敏感数据时。 错误做法直接使用 JSON 工具将对象转换成 String 错误做法 - 直接使用 JSON.toJSONString() 或 ObjectMapper.writeValueAsString() import com.alibaba.fastjson.JSON;public class BadLoggingExample {public static void main(String[] args) {MyObject obj new MyObject(Alice, 30, Engineer);// 错误做法直接使用 JSON.toJSONString 打印整个对象的 JSON 字符串System.out.println(JSON.toJSONString(obj)); // 直接打印整个对象的 JSON 字符串} }class MyObject {private String name;private int age;private String job;public MyObject(String name, int age, String job) {this.name name;this.age age;this.job job;}// getters and setters } 正确做法通过日志框架打印日志并记录必要的字段 dependencies!-- SLF4J API --dependencygroupIdorg.slf4j/groupIdartifactIdslf4j-api/artifactIdversion2.0.0/version/dependency!-- Logback 实现 --dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.4.7/version/dependency /dependencies配置 Logback 在 src/main/resources/logback.xml 中配置日志输出格式 configuration!-- 控制台输出 --appender nameconsole classch.qos.logback.core.ConsoleAppenderencoderpattern%d{yyyy-MM-dd HH:mm:ss} - %msg%n/pattern/encoder/appender!-- 根日志配置 --root levelINFOappender-ref refconsole //root/configuration 通过 SLF4J 记录日志避免直接将对象转换为 JSON 字符串输出必要的字段 import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class GoodLoggingExample {// 创建日志记录器private static final Logger logger LoggerFactory.getLogger(GoodLoggingExample.class);public static void main(String[] args) {MyObject obj new MyObject(Alice, 30, Engineer);// 正确做法只记录对象的必要信息logger.info(User Info - Name: {}, Age: {}, Job: {}, obj.getName(), obj.getAge(), obj.getJob());} }class MyObject {private String name;private int age;private String job;public MyObject(String name, int age, String job) {this.name name;this.age age;this.job job;}// getterspublic String getName() {return name;}public int getAge() {return age;}public String getJob() {return job;} } 防止敏感信息泄露 为了防止将敏感信息泄露到日志中应该确保 只记录对象的必要信息在日志中记录关键信息如用户的用户名、请求类型等而非整个对象或敏感字段如密码、Token 等。自定义日志输出格式通过日志框架定制日志输出避免将整个对象序列化为字符串输出。脱敏处理如果必须记录一些敏感信息确保通过脱敏方法将敏感数据遮蔽或加密。 import org.slf4j.Logger; import org.slf4j.LoggerFactory;public class SecureLoggingExample {private static final Logger logger LoggerFactory.getLogger(SecureLoggingExample.class);public static void main(String[] args) {User user new User(Alice, secretPassword, 30);// 只记录用户的非敏感信息logger.info(User Info - Name: {}, Age: {}, user.getName(), user.getAge());// 如果必须记录敏感信息进行脱敏处理logger.info(User Info with sensitive data: Name: {}, Password: {}, user.getName(), [PROTECTED]);} }class User {private String name;private String password;private int age;public User(String name, String password, int age) {this.name name;this.password password;this.age age;}// getterspublic String getName() {return name;}public String getPassword() {return password;}public int getAge() {return age;} } 总结 避免直接使用 JSON 工具将对象转换为 String。直接将对象序列化为 JSON 字符串会导致日志冗长、性能下降并可能泄露敏感信息。使用日志框架如 SLF4J 和 Logback并只记录必要的字段或关键信息而不是整个对象的 JSON 字符串。敏感信息保护避免在日志中记录敏感信息如密码、Token 等必要时可以进行脱敏处理。 【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使用 warn 来记录刚上线时的业务行为信息一定要注意日志输出量的问题避免把服务器磁盘撑 爆并记得及时删除这些观察日志。 说明: 大量地输出无效日志不利于系统性能提升也不利于快速定位错误点。记录日志时请思考:这些 日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处? 【推荐】可以使用 warn 日志级别来记录用户输入参数错误的情况避免用户投诉时无所适从。如非必要请不要在此场景打出 error 级别避免频繁报警。 说明: 注意日志输出的级别error 级别只记录系统逻辑出错、异常或者重要的错误信息。 【推荐】尽量用英文来描述日志错误信息如果日志中的错误信息用英文描述不清楚的话使用中文描述即可否则容易产生歧义。国际化团队或海外部署的服务器由于字符集问题使用全英文来注释和描述日志错误信息。 博文参考 《阿里巴巴java开发规范》
http://www.hkea.cn/news/14498494/

相关文章:

  • dw做网站导航条中国品牌设计公司
  • 电子商务网站推广方案wordpress漫画主题推荐
  • wordpress 手机版 导航seo诊断分析在线工具
  • 深圳做网站服务商网页设计实验步骤
  • 有趣的网站 知乎网站开发人员需要什么技能
  • 如何把网站做的和别人一样吗营销策划机构
  • elementor做视频网站外贸营销平台推广
  • 广州网站服务国家企业信息公示系统全国
  • 产教融合平台建设网站营销型外贸网站建设软件
  • 知名营销类网站建设网站培训班
  • WordPress主题资源台州seo
  • 如何做网站商城服装网站建设目的
  • 网站建设的数据库设计图淘宝网站开发的多少钱
  • 长安商城网站建设微信公众号运营方法
  • 网站开发多少费用东莞网站建设排名公司
  • 深圳广告设计公司网站网站右下角调用优酷视频广告代码
  • 莲湖区建设局网站怎么弄个人网站
  • 六安找人做网站ppt模板制作免费下载
  • 怎么做动漫照片下载网站wordpress 本地头像
  • 本地电脑做服务器 建网站网站建设模板图片
  • 《网页设计与网站建设》大作业要求做设计需要知道的几个网站吗
  • 珠宝网站模版动画制作平台
  • 专业商城网站建设多少钱wordpress mu功能
  • 手机网站dedecmswordpress 数据库管理
  • 淘宝放单网站开发优秀网站设计
  • 网站logo怎么修改网站已备案 还不能访问
  • 大连网站建设公司腾讯云域名控制台
  • 我有网网站建设香河住房和城乡建设局网站
  • 如何攻克房地产网站人社局网站建设
  • 网站的用户登录一般怎么做的网页空间