Spring Boot应用程序的代码审查要点

最近,我在工作中越来越多地接触到Spring Boot。使用Spring时,如果善用注解和相关功能,可以以相对高的生产力构建应用程序,但是如果在某种程度上盲目使用,可能会导致意外行为的发生。这次我打算整理一下在代码审查时经常评论的内容(虽然都是基础知识点……)。

(1) 是否将控制器/服务/存储库进行分割?

在Spring中,建议将整个应用程序的处理拆分为以下三层结构的类进行实现,而不是将其硬编码在特定的类中。

層概要Controllerリクエストの窓口になるクラスServiceビジネスロジックを実装するクラスRepositoryデータ永続化を行うクラス

因为Controller成为请求的入口,所以我认为对于初次接触Spring的开发者来说,它应该非常熟悉。但是,如果我们只是盲目地开发应用程序,很容易在Controller中实现业务逻辑。这样做会导致所谓的肥大Controller,低可读性和可维护性,所以我认为我们应该强烈意识到业务逻辑的隔离。

我个人认为,可以专注于以下各项内容是比较好的想法。

    • Controller

リクエストの受付
リクエストパラメータのValidation
Serviceの実行
例外処理
レスポンスの作成

Service

ビジネスロジックの実行
外部サービスとの連携
Repositoryを介したデータ操作
トランザクション管理

Repository

データ操作

如果在控制器中调用多种类型的服务,由于控制器可能变得庞大,所以在这种情况下,我觉得可以在控制器和服务之间创建一个服务门面类来进行中介。

考虑实例的生命周期实现了吗?

在Spring的DI容器中管理的实例,默认情况下是单例的。换句话说,如果在成员变量等地方保存状态,那么在访问同一应用程序的用户之间,状态(如用户ID等)会混在一起,往往导致预期行为无法实现。基本上,我认为最好是以无状态(Stateless)方式创建,但根据需求,也可以考虑更改为Spring支持的其他范围。

スコープ範囲Singletonアプリケーションで1つsessionセッションごとに1つprototypeアクセスごとに1つ

顺便说一下,如果是自定义类,可以通过添加@Scope注解来更改作用域。

@Scope("prototype")
@Service
public class SecretService {
 //処理実装
}

作为检查要点

    • Singletonなインスタンスで状態を持っていないか?

 

    不必要に他のスコープ(session/prototype)を使っていないか?

附近开始成为重点区域。

你是否使用了构造器注入?

使用Spring时,无法避免的依赖注入(DI)中,有两种方式可以不编写配置文件。※虽然可能仍然存在使用XML定义,但我们认为使用它的人很少,所以不详述。

    • フィールドインジェクション

 

    コンストラクタインジェクション

在中国,只需要一种选择来将以下内容进行中文本地化:
字段注入只需在成员变量上添加@Autowired,非常简单。

@Autowired
private SecretService service;

然而,由于以下列出的缺点,它已被不推荐使用。※我记得Spring也曾发出警告。

    • テストコード作成時にDIコンテナを使わないとモックが出来ない

 

    フィールドにfinal属性を付与出来ずimmutableにならない

因此,除非有非常特殊的情况(例如必须要循环依赖),我们应该使用构造函数注入。

private final SecretService service

public MyClass(SecretService service) {
  this.service = service;
}

如果构造函数的定义太繁琐的话,利用Lombok也是一种选择。

@AllArgsConstructor
public class MyClass {
  //TODO: 実装
}

(4) 是否有可进行共通化的处理方法?

在Spring框架中,提供了HandlerInterceptor和AOP等机制来定义横切性的通用处理。通过利用这些机制,可以实现以下功能:

    • 特定パッケージ配下にある全てのクラスの全メソッドで共通の前処理を行う

 

    特定の型の戻り値を返すメソッドで共通の後処理を行う

在某些情况下,您不再需要实现重复的处理。常见的情况可能是日志输出。

  /**
   * ロガークラス
   */
  private final Logger logger = LoggerFactory.getLogger(MyController.class);


  /**
   * リクエストメソッドのサンプル.
   * @param form Formクラス
   * @param bindingResult Validation結果
   * @return レスポンス
   */
  @RequestMapping(value = "/", method = RequestMethod.GET)
  public String handleRequest(@Valid MyControllerForm form, BindingResult bindingResult) {
    logger.info("Interceptorテスト");
    //省略
  }
}

详细地

@Aspect
@Component
public class MyInterceptor {

  @Before("execution(* jp.co.cross_xross.controller..*.*(..))")
  public void beforeProcess(JoinPoint joinPoint) throws Throwable {
    //前処理
  }
}

通过指定包名、方法名、参数和返回值,可以执行”前处理”、”后处理”和”替代处理”之类的操作。

(5) 是否没有自行实现Spring提供的功能?

我觉得差不多没有梗了(笑),Spring常常将常见的功能作为子项目提供。如果使用该功能,可以轻松地实现自己实现时常遇到的困难部分,所以我认为应该利用它。

サブプロジェクト名提供機能備考Spring Sessionセッションレプリケーション-Spring Security認証機能-Spring Dataデータ(RDBMS/KVS)操作-Spring SocialSNS(Facebook/Twitter/LinkedIn)連携その他SNSとの連携も可能Spring Batchバッチ処理-Spring AMQPRabbitMQ連携-

附言…

下面是需要结合Java的基本代码审查要点来确认的地方。作为纯Java代码审查的视角,我参考了之前在qiita上写的一些前辈们的文章。

    • うまくメソッド名を付けるための参考情報

 

    • ちょっといいJavaコードを書こう

 

    コードレビュー チェックリスト
广告
将在 10 秒后关闭
bannerAds