Spring MVC

コア実装プロセス
Spring MVCがリクエストを処理する場合、主なコンポーネントと責任は以下のとおりです。
DispatcherServlet:要求を統合的に受信し、その後の処理フローを調整するフロントエンドコントローラ。HandlerMapping:要求先アドレスに基づいて対応するプロセッサメソッドを検索する.HandlerAdapter:プロセッサを統一的に呼び出し、パラメータ解析などの作業を行います。Handler:実際にリクエストを処理するコントローラメソッド。ModelAndViewモデルデータと論理ビュー名の両方をカプセル化します。ViewResolver:論理ビュー名を具体ビューに解析する。View:レスポンスコンテンツのレンダリングを担当。
従来のSpring MVCプロジェクトの作成
Maven Webプロジェクトの作成
Spring MVC 依存関係の導入。次のリリースは、元のコースサンプル環境にのみ対応しています。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
DispatcherServletの設定
フロントエンド·ダイレクタをweb.xmlに登録するには、次の手順に従います。
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>/は、コンテナ内の特別なリクエストを除くほとんどのリクエストをDispatcherServletで処理し、静的リソースはデフォルトのサーブレットで処理できます。/*として構成することは、JSP 転送などのコンテナの動作に影響を与える可能性があるため、一般的には推奨されません。
Spring MVCファイルの作成
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.hyxy.controller"/>
<mvc:annotation-driven/>
</beans>コントローラーの作成
@Controller
public class TestController {
@RequestMapping("/test")
@ResponseBody
public String test() {
return "test";
}
}
サーバを起動して/testにアクセスすると、レスポンスボディにtestが返されます。
リクエスト·マッピング
@RequestMapping
@RequestMappingはクラスとメソッドを修飾することができる。クラスの修飾は通常、ユニファイドアドレスプレフィックスを表し、メソッドの修飾はリクエストマッピングを表します。
一般的な属性は次のとおりです。
valueまたはpath要求先アドレス。method許可されたリクエストメソッド。params要求に指定パラメータを含めるか、含めないかを要求します。headersリクエストヘッダーに基づいてマッピングをさらに制限する。consumes:リクエストボディのメディアタイプを制限します。produces:応答メディアタイプを宣言する。
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ResponseBody
public String add() {
return "add";
}
}実際のプロジェクトでは、@GetMapping、@PostMapping、@PutMapping、@DeleteMappingなど、より明確なセマンティック注釈を使用することもできます。
要求パラメータ·バインディング
単纯パラメータの
要求パラメータ名がメソッドパラメータ名と一致している場合、Spring MVCは自動的にバインドできます。パラメータ名を保持せずにコンパイルする場合、または明示的にパラメータ名を指定する場合は、@RequestParamを使用します。
@GetMapping("/user")
@ResponseBody
public String getUser(
@RequestParam(name = "name") String username,
@RequestParam(name = "age", required = false, defaultValue = "0") Integer age) {
return username + ":" + age;
}
プリミティブ型を使用してオプションパラメータを受け取る場合、パラメータが欠落していると型変換が失敗するため、オプション数値パラメータには一般的にラッパー型が使用されます。
バインドパス変数
@GetMapping("/add/{name}/{pwd}")
@ResponseBody
public String add(
@PathVariable("name") String username,
@PathVariable("pwd") String password) {
return username + ":" + password;
}
例えば、http://localhost:8088/mvc/test/add/tom/123にアクセスすると、usernameの値はtomであり、passwordの値は123である。
リクエストヘッダーとCookieの読み込み
@GetMapping("/header")
@ResponseBody
public String header(
@RequestHeader("Accept") String accept,
@CookieValue(value = "username", defaultValue = "rose") String username) {
return accept + ":" + username;
}
Javaオブジェクトのバインド#Javaオブジェクトノバインド#
要求パラメータ名がオブジェクトプロパティ名と一致する場合、パラメータをオブジェクトにバインドできます。
@PostMapping("/emp")
@ResponseBody
public Emp add(Emp emp) {
return emp;
}
関連オブジェクトの場合、プロパティパスを使用してパラメータを渡すことができます。例えば、EmpにDept deptが含まれている場合、要求パラメータはdept.deptName=开发部と書くことができる。
public class Emp {
private String empNo;
private String empName;
private Dept dept;
// getter 和 setter
}
public class Dept {
private String deptName;
// getter 和 setter
}サーブレットAPIオブジェクトを取得する
元のリクエストやセッションにアクセスする必要がある場合は、コントローラメソッドパラメータで直接宣言できます。
@GetMapping("/request-info")
@ResponseBody
public String requestInfo(HttpServletRequest request, HttpSession session) {
session.setAttribute("method", request.getMethod());
return request.getMethod();
}
ビジネス·コードはサーブレットAPIに過度に依存しないでください。アノテーションとパラメータバインディングで実行できるシナリオは、Spring MVCのパラメータ解決メカニズムを優先する必要があります。
ビュージャンプ対モデルデータ
論理ビュー名を返します
@GetMapping("/books")
public String books(Model model) {
model.addAttribute("bookList", List.of("Java", "Spring"));
return "book/list";
}
ビューパーサと合わせると、book/listは具体的なページパスにされます。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
ModelAndViewへ戻る
@GetMapping("/detail")
public ModelAndView detail() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", "Spring MVC");
modelAndView.setViewName("book/detail");
return modelAndView;
}
静的リソース処理
静的リソースマッピングは、次のように明示的に設定できます。
<mvc:annotation-driven/>
<mvc:resources mapping="/html/**" location="/html/"/>
デフォルトのサーブレットを使用して、一致しない静的リソースを処理することもできます
<mvc:default-servlet-handler/>
前後端の分離開発
@RequestBody
@RequestBodyはリクエストボディを読み取り、メッセージコンバータを介してJSONをJavaオブジェクトに変換します。要求ヘッダーには通常Content-Type: application/jsonが含まれます。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
</dependency>
@RestController
@RequestMapping("/test")
public class TestController {
@PostMapping("/emp")
public Emp save(@RequestBody Emp emp) {
return emp;
}
}

JSONフィールド名はJavaプロパティ名と一致します。フィールドの名前が一貫していない場合は、Jackson 注釈を使用してマッピングできます。
@ResponseBody
@ResponseBodyはメソッドの戻り値をレスポンスボディに書き込むことを示します。オブジェクトが返されると、Spring MVCはメッセージコンバータを介してJSONにシリアライズします。文字列が返されると、通常はテキストコンテンツに直接書き込まれます。
@GetMapping("/get")
@ResponseBody
public User get() {
User user = new User();
user.setUsername("jack");
user.setPassword("123456");
return user;
}
@RestController
@RestControllerは、クラスレベルの@Controllerと@ResponseBodyの組み合わせに相当し、JSONを主なレスポンスコンテンツとするコントローラに適しています。
統合レスポンスオブジェクト
public class Result<T> {
private boolean success;
private T data;
private String message;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.success = true;
result.data = data;
return result;
}
public static <T> Result<T> failure(String message) {
Result<T> result = new Result<>();
result.success = false;
result.message = message;
return result;
}
// getter 和 setter
}クロスドメインリクエスト
ブラウザの同一オリジンポリシーは、異なるソースのリソースへのスクリプトのアクセスを制限します。ソースはプロトコル、ホスト、ポートの組み合わせによって決定され、その違いはクロスソースリクエストになります。
略称は@CrossOrigin
@RestController
@RequestMapping("/book")
@CrossOrigin(origins = "http://localhost:8081")
public class BookController {
}
CORSのグローバル設定
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8081")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}資格情報を有効にする場合、許可ソースをワイルドカードとして単純に設定できません。本番環境では、実際に必要なソース、メソッド、リクエストヘッダーのみをオープンにしてください。
フロントエンドプロキシとNginxリバースプロキシ
開発環境はフロントエンド開発サーバプロキシインターフェイスを介してリクエストできます。本番環境は通常、フロントエンドの静的リソースとバックエンドのインターフェイスを同じサイトに統合するためにNginxを使用します。


リバースプロキシはリクエストの転送方法を変更するだけで、バックエンド自身の認証、パーミッションチェック、入力チェックを置き換えることはありません。
ファイルのアップロード
プロファイルパーサ
従来のSpring MVCプロジェクトでは、CommonsMultipartResolverを構成できます。
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5242880"/>
<property name="defaultEncoding" value="UTF-8"/>
</bean>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
フォームはPOSTリクエストを使用し、enctype="multipart/form-data"を設定してください。
アップロードインターフェイスの作成
@RestController
public class FileUploadController {
private static final Path UPLOAD_DIR = Paths.get("D:/Lesson/uploadFile");
@PostMapping("/upload")
public Result<String> upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return Result.failure("上传文件不能为空");
}
String originalName = file.getOriginalFilename();
String safeName = originalName == null
? UUID.randomUUID().toString()
: Paths.get(originalName).getFileName().toString();
try {
Files.createDirectories(UPLOAD_DIR);
Path target = UPLOAD_DIR.resolve(safeName).normalize();
if (!target.startsWith(UPLOAD_DIR)) {
return Result.failure("文件名不合法");
}
file.transferTo(target);
return Result.success(safeName);
} catch (IOException e) {
return Result.failure(e.getMessage());
}
}
}クライアントがコミットする元のファイル名を直接信頼しないでください。実際のプロジェクトでは、ファイルサイズ、拡張子、MIMEタイプ、ストレージパーミッションもチェックし、アップロードディレクトリを実行可能ディレクトリとして公開しないようにする必要があります。

気に入ったならばコメントを残してくださいね~