目次
3.1. 一般的に使用される JSR 303 アノテーション
1.JSR303
1. JSR 303とは何ですか?
JSR 303は Java Specific Request の略で、 Java 仕様提案を意味します。これは、標準化された技術仕様を追加するようJCP (Java Community Process)に正式に要求するものです。誰でも JSR を送信して、新しい API やサービスを Java プラットフォームに追加できます。JSR は Java の世界では重要な標準となっています。JSR-303 は、Bean Validationと呼ばれるJAVA EE 6のサブ仕様です。Hibernate Validator は、 Bean Validationの参照実装です。Hibernate Validator は、いくつかの追加の制約に加えて、 JSR 303 仕様のすべての組み込み制約の実装を提供します。
Java Bean 検証の一連の標準を定義します。JSR 303では、Spring MVCでデータの検証や検証に広く使用されているアノテーションを使用します。
データの検証は、プレゼンテーション層から永続層までのすべてのアプリケーション層で発生する共通のタスクです。多くの場合、同じ検証ロジックがすべてのレイヤーで実装されますが、これには時間がかかり、エラーが発生しやすくなります。これらの検証の重複を避けるために、開発者は多くの場合、検証ロジックをドメイン モデルに直接バンドルし、ドメイン クラスと、実際にはクラス自体に関するメタデータである検証コードを混合します。
2. なぜ JSR 303 を使用するのですか?
フロントエンドでデータ検証を実行しましたが、なぜ別の記事を行う必要があるのでしょうか?小さな間違いのため、フロントエンド検証が適切に記述されていませんでしたが、一部の人は依然としてフロントエンドによって送信されたリクエストをバイパスしているためです( Postman ツールなどのテストでは異常なデータ要求が行われ、間違ったパラメーターが渡されるため、バックエンド コードが大きな危険にさらされるため、通常はフロントエンドで 1 セットの検証を使用し、バックエンドで 1 セットの検証を使用します。セキュリティが大幅に向上する可能性があります。
そこで、 JSR 303の利点を以下にまとめました。
- コードの保守性の向上:エンティティ クラスにアノテーションを追加することで、検証する必要があるフィールドとルールを明確に識別できるため、コードの理解と保守が容易になります。
- データの整合性の強化:定義されたルールに従って、入力データの合法性を自動的に検証して、システムへの誤ったデータの入力を防ぎ、データの整合性と正確性を保証できます。
- 重複コードの削減:注釈を使用すると、同じ検証ルールをさまざまなシナリオで再利用できるため、重複コードを記述する作業負荷が軽減されます。
3. JSR 303 の共通アノテーション
3.1. 一般的に使用される JSR 303 アノテーション
注釈の説明 @Null は、オブジェクトがnull であることを確認するために使用されます。 @NotNull は、オブジェクトがnullにできず、長さ 0 の文字列をチェックできない場合に使用されます。 @NotBlank はString型にのみ使用され、 nullにすることはできません。Trim()後のサイズは> 0 @NotEmpty コレクション クラスに使用されます。 String クラスはnull、およびsize>0にすることはできません。ただし、スペースを含む文字列は検証できません。オブジェクト ( Array、Collection、Map、String ) には、長さが指定された範囲内かどうか @Size が使用されます。String オブジェクトには @Length が使用されます。サイズは、指定された範囲内である必要があります。@パターンStringオブジェクトが正規表現のルールに準拠するかどうかに使用されます @Email Stringオブジェクトが電子メール形式に準拠するかどうかに使用されます @Min NumberおよびStringオブジェクトが指定された値以上かどうかに使用されます @Max 使用されますNumber オブジェクトとStringオブジェクトが指定された値以下であるかどうか @ AssertTrue を使用して、Boolean オブジェクトが指定された値以下であるかどうかを判断します。ブール値オブジェクトがfalseかどうかを表すtrue @AssertFalse
3.2. @Validated と @Valid の違い
@Validatedと@Valid はデータ検証に使用されるアノテーションですが、いくつかの違いと適用シナリオがあります。
3.2.1、@検証済み
- Springフレームワークによって提供されるアノテーション
- サポートグループの確認
- 型、メソッド、およびメソッド パラメータで使用できます。ただし、メンバー属性 (フィールド) には使用できません。
- メンバー属性(フィールド)に追加できないため、カスケード検証だけでは完了できず、@Validと連携する必要がある
- Spring によって提供されるバリデーターは、デフォルトでHibernate Validatorを使用します(JSR-303 仕様を実装) が、グループ検証などのより豊富な検証シナリオをサポートしています。
3.2.2、@有効
- JDK によって提供されるJSR-303 ( Bean Validation ) 仕様のアノテーション
- グループ検証はサポートされていません
- メソッド、コンストラクター、メソッド パラメーター、メンバー プロパティ (フィールド)で使用できます。
- メンバー属性 (フィールド)に追加して、カスケード検証を個別に完了できます。
- @Valid アノテーションを使用すると、JSR-303 またはその他のサポートされている検証フレームワークがトリガーされて、アノテーションが付けられたオブジェクトが検証されます。
3.2.3. 相違点
- 適用範囲: @Valid はメソッドのパラメータ、戻り値、フィールド、メソッドに適用できますが、@Validated はクラス、インターフェイス、メソッドにのみ適用できます。
- 検証フレームワーク: @Validated はデフォルトでHibernate Validator (JSR-303 実装)、Spring Validator、またはカスタム バリデータを使用しますが、@Valid は JSR-303 またはその他のサポートされている検証フレームワークを使用します。
- グループ検証: @Validated はグループ検証をサポートし、さまざまなシナリオでさまざまな検証ルールを使用できるようにしますが、@Valid はグループ検証を直接サポートしません。
@Validated は Spring フレームワークによって提供される拡張アノテーションであり、Java の標準仕様には属しません。これを使用する場合、特定のニーズに応じてデータ検証に適切な注釈を選択できます。
4. ユースケース
4.1. 依存関係のインポート
<!-- JSR303 -->
<hibernate.validator.version>6.0.7.Final</hibernate.validator.version>
<!-- JSR303 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate.validator.version}</version>
</dependency>
4.2. 検証ルールの設定
エンティティクラスに構成検証を追加する
package com.tgq.model; import lombok.ToString; import org.hibernate.validator.constraints.NotBlank; import javax.validation.constraints.NotNull; @ToString public class MyStudent { @NotNull(message = "学生编号不能为空") private String sid; @NotBlank(message = "学生名不能为空") private String sname; @NotBlank(message = "学生年龄不能为空") private String sage; @NotBlank(message = "学生性别不能为空") private String ssex; public MyStudent(String sid, String sname, String sage, String ssex) { this.sid = sid; this.sname = sname; this.sage = sage; this.ssex = ssex; } public MyStudent() { super(); } public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSage() { return sage; } public void setSage(String sage) { this.sage = sage; } public String getSsex() { return ssex; } public void setSsex(String ssex) { this.ssex = ssex; } }
4.3. 書き込み検証方法
注釈を使用して
@Validated
、MyStudent でサーバー側の検証を実行します。
// 给数据添加服务端校验
@RequestMapping("/valiAdd")
public String valiAdd(@Validated MyStudent myStudent, BindingResult result, HttpServletRequest req) {
// 如果服务端验证不通过,有错误
if (result.hasErrors()) {
// 服务端验证了实体类的多个属性,多个属性都没有验证通过
List<FieldError> fieldErrors = result.getFieldErrors();
Map<String, Object> map = new HashMap<>();
for (FieldError fieldError : fieldErrors) {
// 将多个属性的验证失败信息输送到控制台
System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
req.setAttribute("errorMap", map);
} else {
this.myStudentBiz.insertSelective(myStudent);
return "redirect:stu/list";
}
return "stu/edit";
}
4.4. フロントエンドコード
フォームを使用して送信する
<%--
Created by IntelliJ IDEA.
User: tgq
Date: 12/9/2023
Time: 下午8:05
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>edit</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/stu/valiAdd" method="post">
用户id:<input type="text" name="sid"><span style="color: red">${errorMap.sid}</span><br>
用户名:<input type="text" name="sname"><span style="color: red">${errorMap.sname}</span><br>
用户年龄:<input type="text" name="sage"><span
style="color: red">${errorMap.sage}</span><br>
用户性别:<input type="text" name="ssex"><span style="color: red">${errorMap.ssex}</span><br>
<input type="submit" value="提交">
</form>
</body>
</html>
4.5. テスト
「送信」をクリックします。空の場合は、プロンプトが表示されます。
2.インターセプターインターセプター
1. インターセプターとは何ですか?
インターセプターは、リクエストがバックエンド ハンドラーに入る前または後に特定のロジックを実行するコンポーネントです。ハンドラーによる実行フローをインターセプトできるため、リクエスト処理プロセスに追加機能を挿入できます。
SpringMVCのプロセッサ インターセプタはサーブレット開発におけるフィルタに似ており、プロセッサの前処理と後処理に使用されます。これは Web フレームワークに依存しており、実装ではアスペクト指向プログラミング (AOP)のアプリケーションである Java のリフレクション メカニズムに基づいています。インターセプターは Web フレームワークの呼び出しに基づいているため、Spring の依存関係注入 (DI)を使用して一部のビジネス操作を実行でき、同時にインターセプター インスタンスをコントローラーのライフ サイクル内で複数回呼び出すことができます。
2. インターセプターを使用する理由は何ですか?
- 水平的な機能拡張: インターセプターを通じて、元のビジネス ロジックを変更することなく、ロギング、権限検証、パフォーマンス統計などの追加機能を追加できます。
- コードの再利用: 複数のリクエストで同じ処理ロジックが必要になる場合がありますが、ロジックのこの部分はインターセプターを通じて抽出できるため、コードの繰り返し記述を減らすことができます。
- デカップリング: インターセプターを通じて懸念事項を分離し、共通のロジックをインターセプターで処理することで、ビジネス プロセッサーがビジネスそのものに集中できるようになります。
3. インターセプターとフィルター
3.1. フィルターとは何ですか?
サーブレットコンテナに依存します。実装時の関数コールバックに基づいて、ほとんどすべてのリクエストをフィルタリングできますが、欠点は、コンテナの初期化時にフィルタ インスタンスを 1 回しか呼び出せないことです。フィルタを使用する目的は、フィルタ内の文字エンコーディングの変更、フィルタ内のHttpServletRequestの一部のパラメータの変更など、フィルタリング操作を実行することです。これには、下品なテキストや危険な文字などのフィルタリングが含まれます。
3.2. インターセプターとフィルターの違い
3.2.1. フィルター
- フィルターはサーブレットテクノロジーに属しており、あらゆる Web プロジェクトで使用できます。
- フィルタは主にすべてのリクエストをフィルタリングすることによるものです
- フィルターの実行時間がインターセプターよりも早い
3.2.2. インターセプター
- Interceptor はSpringMVC テクノロジーに属しており、使用する前にSpringMVC環境が必要です。
- インターセプターは通常、プロセッサーコントローラーをインターセプトします
- インターセプターは、dispatcherServlet によって処理されたリクエストのみをインターセプトできます
3.2.3. 概要
- インターセプタはアプリケーション ハンドラ内で実行され、フィルタはアプリケーションの前後で実行されます。
- フィルターはサーブレット仕様に基づいており、インターセプターはアプリケーション フレームワークに基づいています。
- フィルターはリクエストがサーブレット コンテナに到達する前に処理できますが、インターセプタはリクエストがアプリケーションに到達した後にのみ処理できます。
4. インターセプターの適用シナリオ
- 権限の検証: インターセプタは、ユーザーに操作権限があるかどうかを確認し、権限がない場合はリクエストをインターセプトし、対応するエラー メッセージを返します。そうでない場合は、ログイン ページに直接戻ってください。
- ロギング: インターセプターは、後のログ分析やトラブルシューティングを容易にするために、リクエスト時間、情報、リクエストパラメーターなどのリクエストの詳細情報を記録できます。情報モニタリング、情報統計、PV(ページビュー)の算出等を行うため。
- パフォーマンス統計: インターセプターはリクエストの実行時間をカウントして、システム パフォーマンスの分析と最適化を促進できます。(Apacheなどのリバースプロキシがある場合は自動的に記録されます);
- 一般的な動作: Cookie を読み取ってユーザー情報を取得し、ユーザー オブジェクトをリクエストに入れます。これにより、その後のプロセスの使用が容易になります。また、複数のコントローラーで処理メソッドが存在する限り、ロケールやテーマの情報なども抽出されます。必要な場合は、インターセプト実装を使用できます。
インターセプター チェーンは、複数のインターセプターで構成されるチェーン構造であり、それぞれのインターセプターは、リクエスト ハンドラーの実行前または実行後に特定の操作を実行できます。インターセプター チェーン内のインターセプターは事前に定義された順序で実行され、各インターセプターにはリクエストと応答を処理する機会があります。インターセプター チェーンは、各インターセプターの順序正しい実行を保証して、期待される処理ロジックを実現します。
5. ユースケース
5.1. インターセプターの作成
インターセプター パッケージを作成し、そのパッケージの下にインターセプターを作成します
package com.tgq.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class OneInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("【OneInterceptor】:preHandle...");
return true;//返回true / false
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("【OneInterceptor】:postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("【OneInterceptor】:afterCompletion...");
}
}
5.2. インターセプターの設定
独自の設定 spring-mvc.xml でインターセプタを設定します。
<mvc:interceptors> <bean class="com.tgq.interceptor.OneInterceptor"></bean> </mvc:interceptors>
5.3. テストの実行
プロジェクトを開始し、ブラウザを開いてリクエスト アドレスにアクセスし、インターセプターのインターセプト効果をテストします。
実行順序は次のとおりです: preHandle --> postHandle --> afterCompletion
5.4. インターセプターの動作原理
- preHandle : インターセプトされたリクエストを前処理するために使用されます。メソッドはブール (true、false) 型の戻り値を受け取ります。true : 解放された、false: 解放されないを返します。
実行タイミング:プロセッサメソッドの実行前に実行
パラメータ | 説明する |
---|---|
リクエスト | リクエストオブジェクト |
応答 | 応答オブジェクト |
ハンドラ | インターセプトされたハンドラー メソッド |
モデルとビュー | ハンドラー メソッドによって返されるモデル オブジェクトとビュー オブジェクト。モデルとビューはメソッド内で変更できます。 |
- afterCompletion : 全体の処理が完了した後の最終処理に使用され、リクエスト処理で例外が発生した場合、メソッドでオブジェクトを取得できます
実行タイミング:ビューの描画完了後(全処理終了後)
パラメータ | 説明する |
---|---|
リクエスト | リクエストパラメータ |
応答 | 応答オブジェクト |
ハンドラ | インターセプトされたハンドラー メソッド |
元 | 例外オブジェクト |
5.5. インターセプターチェーン
複数のインターセプターが同じリクエストをインターセプトできる場合、複数のインターセプターがインターセプター チェーンを形成します。主な目的は、インターセプター チェーン内の各インターセプターの実行順序を理解することです。インターセプター チェーン内の複数のインターセプターの実行順序は、ルート インターセプターの構成順序に関連しており、最初に構成されたインターセプターが最初に実行されます。
package com.tgq.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TwoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("【TwoInterceptor】:preHandle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("【TwoInterceptor】:postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("【TwoInterceptor】:afterCompletion...");
}
}
spring-mvc.xml を設定する
<mvc:interceptors>
<!--2) 多拦截器(拦截器链)-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.tgq.interceptor.OneInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/sc/**"/>
<bean class="com.tgq.interceptor.TwoInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
インターセプタを使用します: http://localhost:8080/stu/saveを編集します
2 つのインターセプターを取得します: list http://localhost:8080/sc/list
5.6. ログインインターセプトの例
5.6.1. インターセプターの作成
package com.tgq.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("【implements】:preHandle...");
StringBuffer url = request.getRequestURL();
if (url.indexOf("/login") > 0 || url.indexOf("/logout") > 0) {
// 如果是 登录、退出 中的一种
return true;
}
// 代表不是登录,也不是退出
// 除了登录、退出,其他操作都需要判断是否 session 登录成功过
String sname = (String) request.getSession().getAttribute("sname");
if (sname == null || "".equals(sname)) {
response.sendRedirect("/page/stu/login");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
5.6.2. インターセプターの設定
<!--登录拦截器实例-->
<mvc:interceptors>
<bean class="com.tgq.interceptor.LoginInterceptor"></bean>
</mvc:interceptors>
5.6.3. コントロール層の記述
package com.tgq.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(HttpServletRequest req) {
String sname = req.getParameter("sname");
HttpSession session = req.getSession();
if ("tgq".equals(sname)) {
session.setAttribute("sname", sname);
}
return "redirect:/sc/list";
}
@RequestMapping("/logout")
public String logout(HttpServletRequest req) {
req.getSession().invalidate();
return "redirect:/sc/list";
}
}
5.6.4. フロントエンドページ
<%--
Created by IntelliJ IDEA.
User: tgq
Date: 12/9/2023
Time: 下午10:03
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户登录</title>
</head>
<body>
<form action="/login" method="post">
账号:<input name="sname">
<input type="submit">
</form>
</body>
</html>
5.6.5. テスト
ログイン
http://localhost:8080/logout http://localhost:8080/login