1. 程式人生 > >springmvc常用註解配置說明@[

springmvc常用註解配置說明@[

關於SpringMVC中@Requestmapping的相關配置可以參考下面文章:

URL中的變數——PathVariable

在Web應用中URL通常不是一成不變的,例如微博兩個不同使用者的個人主頁對應兩個不同的URL: http://weibo.com/user1http://weibo.com/user2。 我們不可能對於每一個使用者都編寫一個被@RequestMapping註解的方法來處理其請求,Spring MVC提供了一套機制來處理這種情況:

@RequestMapping("/users/{username}")
public String userProfile(@PathVariable("username") String username) {
    return String.format("user %s", username);
}

@RequestMapping("/posts/{id}")
public String post(@PathVariable("id") int id) {
    return String.format("post %d", id);
}

在上述例子中,URL中的變數可以用{variableName}來表示,同時在方法的引數中加上@PathVariable("variableName"),那麼當請求被轉發給該方法處理時,對應的URL中的變數會被自動賦值給被@PathVariable註解的引數(能夠自動根據引數型別賦值,例如上例中的int)。

支援HTTP方法

對於HTTP請求除了其URL,還需要注意它的方法(Method)。例如我們在瀏覽器中訪問一個頁面通常是GET方法,而表單的提交一般是POST方法。@Controller中的方法同樣需要對其進行區分:

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginGet() {
    return "Login Page";
}

@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginPost() {
    return "Login Post Request";
}

Spring MVCSpring 4.3的版本中提供了一種更加簡潔的配置HTTP方法的方式,增加了四個標註:

  • @PutMapping
  • @GetMapping
  • @PostMapping
  • @DeleteMapping

在Web應用中常用的HTTP方法有四種:

  • PUT方法用來新增的資源
  • GET方法用來獲取已有的資源
  • POST方法用來對資源進行狀態轉換
  • DELETE方法用來刪除已有的資源

這四個方法可以對應到CRUD操作(Create、Read、Update和Delete),比如部落格的建立操作,按照REST風格設計URL就應該使用PUT方法,讀取部落格使用GET方法,更新部落格使用POST方法,刪除部落格使用DELETE方法。

每一個Web請求都是屬於其中一種,在Spring MVC中如果不特殊指定的話,預設是GET請求。

比如@RequestMapping("/")@RequestMapping("/hello")和對應的Web請求是:

  • GET /
  • GET /hello

實際上@RequestMapping("/")@RequestMapping("/", method = RequestMethod.GET)的簡寫,即可以通過method屬性,設定請求的HTTP方法。

比如PUT /hello請求,對應於@RequestMapping("/hello", method = RequestMethod.PUT)

基於新的標註@RequestMapping("/hello", method = RequestMethod.PUT)可以簡寫為@PutMapping("/hello")@RequestMapping("/hello")GetMapping("/hello")等價。

模板渲染

在之前所有的@RequestMapping註解的方法中,返回值字串都被直接傳送到瀏覽器端並顯示給使用者。但是為了能夠呈現更加豐富、美觀的頁面,我們需要將HTML程式碼返回給瀏覽器,瀏覽器再進行頁面的渲染、顯示。

一種很直觀的方法是在處理請求的方法中,直接返回HTML程式碼,但是這樣做的問題在於——一個複雜的頁面HTML程式碼往往也非常複雜,並且嵌入在Java程式碼中十分不利於維護。更好的做法是將頁面的HTML程式碼寫在模板檔案中,渲染後再返回給使用者。為了能夠進行模板渲染,需要將@RestController改成@Controller

import org.springframework.ui.Model;

@Controller
public class HelloController {

    @RequestMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name, Model model) {
        model.addAttribute("name", name);
        return "hello"
    }
}

在上述例子中,返回值"hello"並非直接將字串返回給瀏覽器,而是尋找名字為hello的模板進行渲染,我們使用Thymeleaf模板引擎進行模板渲染,需要引入依賴:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

接下來需要在預設的模板資料夾src/main/resources/templates/目錄下新增一個模板檔案hello.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>

th:text="'Hello, ' + ${name} + '!'"也就是將我們之前在@Controller方法裡新增至Model的屬性name進行渲染,並放入<p>標籤中(因為th:text<p>標籤的屬性)。模板渲染還有更多的用法,請參考Thymeleaf官方文件

處理靜態檔案

瀏覽器頁面使用HTML作為描述語言,那麼必然也脫離不了CSS以及JavaScript。為了能夠瀏覽器能夠正確載入類似/css/style.css, /js/main.js等資源,預設情況下我們只需要在src/main/resources/static目錄下新增css/style.cssjs/main.js檔案後,Spring MVC能夠自動將他們釋出,通過訪問/css/style.css, /js/main.js也就可以正確載入這些資源。

檔案上傳

Spring MVC還能夠支援更為複雜的HTTP請求——檔案資源。我們在網站中經常遇到上傳圖片、附件一類的需求,就是通過檔案上傳技術來實現的。

處理檔案的表單和普通表單的唯一區別在於設定enctype——multipart編碼方式則需要設定enctypemultipart/form-data

<form method="post" enctype="multipart/form-data">
    <input type="text" name="title" value="tianmaying">
    <input type="file" name="avatar">
    <input type="submit">
</form>

這裡我們還設定了<input type='text'>的預設值為tianmaying

該表單將會顯示為一個文字框、一個檔案按鈕、一個提交按鈕。然後我們選擇一個檔案:chrome.png,點選表單提交後產生的請求可能是這樣的:

請求頭:

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

請求體:

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="title"

tianmaying
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="avatar"; filename="chrome.png"
Content-Type: image/png

 ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

這便是一個multipart編碼的表單。Content-Type中還包含了boundary的定義,它用來分隔請求體中的每個欄位。正是這一機制,使得請求體中可以包含二進位制檔案(當然檔案中不能包含boundary)。檔案上傳正是利用這種機制來完成的。

如果不設定<form>enctype編碼,同樣可以在表單中設定type=file型別的輸入框,但是請求體和傳統的表單一樣,這樣伺服器程式無法獲取真正的檔案內容。

在服務端,為了支援檔案上傳我們還需要進行一些配置。

控制器邏輯

對於表單中的文字資訊輸入,我們可以通過@RequestParam註解獲取。對於上傳的二進位制檔案(文字檔案同樣會轉化為byte[]進行傳輸),就需要藉助Spring提供的MultipartFile類來獲取了:

@Controller
public class FileUploadController {

    @PostMapping("/upload")
    @ResponseBody
    public String handleFileUpload(@RequestParam("file") MultipartFile file) {
        byte[] bytes = file.getBytes();

        return "file uploaded successfully."
    }
}

通過MultipartFilegetBytes()方法即可以得到上傳的檔案內容(<form>中定義了一個type="file"的,在這裡我們可以將它儲存到本地磁碟。另外,在預設的情況下Spring僅僅支援大小為128KB的檔案,為了調整它,我們可以修改Spring的配置檔案src/main/resources/application.properties

multipart.maxFileSize: 128KB
multipart.maxRequestSize: 128KB

修改上述數值即可完成配置。

HTML表單

HTML中支援檔案上傳的表單元素仍然是<input>,只不過它的型別是file

<html>
<body>
  <form method="POST" enctype="multipart/form-data" action="/upload">
    File to upload: <input type="file" name="file"><br />
    Name: <input type="text" name="name"><br /> <br />
    <input type="submit" value="Upload"> Press here to upload the file!
  </form>
</body>
</html>

multipart/form-data表單既可以上傳檔案型別,也可以和普通表單一樣提交其他型別的資料,在Spring MVC的@RequestMapping方法引數中用@RequestParam標註即可(也可以利用資料繫結機制,繫結一個物件)

攔截器Interceptor

Spring MVC框架中的Interceptor,與Servlet API中的Filter十分類似,用於對Web請求進行預處理/後處理。通常情況下這些預處理/後處理邏輯是通用的,可以被應用於所有或多個Web請求,例如:

  • 記錄Web請求相關日誌,可以用於做一些資訊監控、統計、分析
  • 檢查Web請求訪問許可權,例如發現使用者沒有登入後,重定向到登入頁面
  • 開啟/關閉資料庫連線——預處理時開啟,後處理關閉,可以避免在所有業務方法中都編寫類似程式碼,也不會忘記關閉資料庫連線

Spring MVC請求處理流程

1.png

上圖是Spring MVC框架處理Web請求的基本流程,請求會經過DispatcherServlet的分發後,會按順序經過一系列的Interceptor並執行其中的預處理方法,在請求返回時同樣會執行其中的後處理方法。

DispatcherServletController之間哪些豎著的彩色細條,是攔截請求進行額外處理的地方,所以命名為攔截器Interceptor)。

HandlerInterceptor介面

Spring MVC中攔截器是實現了HandlerInterceptor介面的Bean:

public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest request, 
                      HttpServletResponse response, 
                      Object handler) throws Exception;

    void postHandle(HttpServletRequest request, 
                    HttpServletResponse response, 
                    Object handler, ModelAndView modelAndView) throws Exception;

    void afterCompletion(HttpServletRequest request, 
                         HttpServletResponse response, 
                         Object handler, Exception ex) throws Exception;
}
  • preHandle():預處理回撥方法,若方法返回值為true,請求繼續(呼叫下一個攔截器或處理器方法);若方法返回值為false,請求處理流程中斷,不會繼續呼叫其他的攔截器或處理器方法,此時需要通過response產生響應;
  • postHandle():後處理回撥方法,實現處理器的後處理(但在渲染檢視之前),此時可以通過ModelAndView對模型資料進行處理或對檢視進行處理
  • afterCompletion():整個請求處理完畢回撥方法,即在檢視渲染完畢時呼叫

HandlerInterceptor有三個方法需要實現,但大部分時候可能只需要實現其中的一個方法,HandlerInterceptorAdapter是一個實現了HandlerInterceptor的抽象類,它的三個實現方法都為空實現(或者返回true),繼承該抽象類後可以僅僅實現其中的一個方法:

public class Interceptor extends HandlerInterceptorAdapter {

    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        // 在controller方法呼叫前列印資訊
        System.out.println("This is interceptor.");
        // 返回true,將強求繼續傳遞(傳遞到下一個攔截器,沒有其它攔截器了,則傳遞給Controller)
        return true;
    }
}

配置Interceptor

定義HandlerInterceptor後,需要建立WebMvcConfigurerAdapter在MVC配置中將它們應用於特定的URL中。一般一個攔截器都是攔截特定的某一部分請求,這些請求通過URL模型來指定。

下面是一個配置的例子:

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleInterceptor());
        registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }
}

@ModelAttribute

方法使用@ModelAttribute標註

@ModelAttribute標註可被應用在方法或方法引數上。

標註在方法上的@ModelAttribute說明方法是用於新增一個或多個屬性到model上。這樣的方法能接受與@RequestMapping標註相同的引數型別,只不過不能直接被對映到具體的請求上。

在同一個控制器中,標註了@ModelAttribute的方法實際上會在@RequestMapping方法之前被呼叫。

以下是示例:

// Add one attribute
// The return value of the method is added to the model under the name "account"
// You can customize the name via @ModelAttribute("myAccount")

@ModelAttribute
public Account addAccount(@RequestParam String number) {
    return accountManager.findAccount(number);
}

// Add multiple attributes

@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
    model.addAttribute(accountManager.findAccount(number));
    // add more ...
}

@ModelAttribute方法通常被用來填充一些公共需要的屬性或資料,比如一個下拉列表所預設的幾種狀態,或者寵物的幾種型別,或者去取得一個HTML表單渲染所需要的命令物件,比如Account等。

@ModelAttribute標註方法有兩種風格:

  • 在第一種寫法中,方法通過返回值的方式預設地將新增一個屬性;
  • 在第二種寫法中,方法接收一個Model物件,然後可以向其中新增任意數量的屬性。

可以在根據需要,在兩種風格中選擇合適的一種。

一個控制器可以擁有多個@ModelAttribute方法。同個控制器內的所有這些方法,都會在@RequestMapping方法之前被呼叫。

@ModelAttribute方法也可以定義在@ControllerAdvice標註的類中,並且這些@ModelAttribute可以同時對許多控制器生效。

屬性名沒有被顯式指定的時候又當如何呢?在這種情況下,框架將根據屬性的型別給予一個預設名稱。舉個例子,若方法返回一個Account型別的物件,則預設的屬性名為"account"。可以通過設定@ModelAttribute標註的值來改變預設值。當向Model中直接新增屬性時,請使用合適的過載方法addAttribute(..)-即帶或不帶屬性名的方法。

@ModelAttribute標註也可以被用在@RequestMapping方法上。這種情況下,@RequestMapping方法的返回值將會被解釋為model的一個屬性,而非一個檢視名,此時檢視名將以檢視命名約定來方式來確定。

方法引數使用@ModelAttribute標註

@ModelAttribute標註既可以被用在方法上,也可以被用在方法引數上。

標註在方法引數上的@ModelAttribute說明了該方法引數的值將由model中取得。如果model中找不到,那麼該引數會先被例項化,然後被新增到model中。在model中存在以後,請求中所有名稱匹配的引數都會填充到該引數中。

這在Spring MVC中被稱為資料繫結,一個非常有用的特性,我們不用每次都手動從表格資料中轉換這些欄位資料。

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute Pet pet) { }

以上面的程式碼為例,這個Pet型別的例項可能來自哪裡呢?有幾種可能:

  • 它可能因為@SessionAttributes標註的使用已經存在於model中
  • 它可能因為在同個控制器中使用了@ModelAttribute方法已經存在於model中——正如上一小節所敘述的
  • 它可能是由URI模板變數和型別轉換中取得的(下面會詳細講解)
  • 它可能是呼叫了自身的預設構造器被例項化出來的

@ModelAttribute方法常用於從資料庫中取一個屬性值,該值可能通過@SessionAttributes標註在請求中間傳遞。在一些情況下,使用URI模板變數和型別轉換的方式來取得一個屬性是更方便的方式。這裡有個例子:

@RequestMapping(path = "/accounts/{account}", method = RequestMethod.PUT)
public String save(@ModelAttribute("account") Account account) {

}

這個例子中,model屬性的名稱("account")與URI模板變數的名稱相匹配。如果配置了一個可以將String型別的賬戶值轉換成Account型別例項的轉換器Converter<String, Account>,那麼上面這段程式碼就可以工作的很好,而不需要再額外寫一個@ModelAttribute方法。

下一步就是資料的繫結。WebDataBinder類能將請求引數——包括字串的查詢引數和表單欄位等——通過名稱匹配到model的屬性上。成功匹配的欄位在需要的時候會進行一次型別轉換(從String型別到目標欄位的型別),然後被填充到model對應的屬性中。

進行了資料繫結後,則可能會出現一些錯誤,比如沒有提供必須的欄位、型別轉換過程的錯誤等。若想檢查這些錯誤,可以在標註了@ModelAttribute的引數緊跟著宣告一個BindingResult引數:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}

拿到BindingResult引數後,可以檢查是否有錯誤,可以通過Spring的<errors>表單標籤來在同一個表單上顯示錯誤資訊。

BindingResult被用於記錄資料繫結過程的錯誤,因此除了資料繫結外,還可以把該物件傳給自己定製的驗證器來呼叫驗證。這使得資料繫結過程和驗證過程出現的錯誤可以被蒐集到一起,然後一併返回給使用者:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {

    new PetValidator().validate(pet, result);
    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}

又或者可以通過新增一個JSR-303規範的@Valid標註,這樣驗證器會自動被呼叫。

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {

    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}

異常處理

Spring MVC框架提供了多種機制用來處理異常,初次接觸可能會對他們用法以及適用的場景感到困惑。現在以一個簡單例子來解釋這些異常處理的機制。

假設現在我們開發了一個部落格應用,其中最重要的資源就是文章(Post),應用中的URL設計如下:

  • 獲取文章列表:GET /posts/
  • 新增一篇文章:POST /posts/
  • 獲取一篇文章:GET /posts/{id}
  • 更新一篇文章:PUT /posts/{id}
  • 刪除一篇文章:DELETE /posts/{id}

這是非常標準的複合RESTful風格的URL設計,在Spring MVC實現的應用過程中,相應也會有5個對應的用@RequestMapping註解的方法來處理相應的URL請求。在處理某一篇文章的請求中(獲取、更新、刪除),無疑需要做這樣一個判斷——請求URL中的文章id是否在於系統中,如果不存在需要返回404 Not Found

使用HTTP狀態碼

在預設情況下,Spring MVC處理Web請求時如果發現存在沒有應用程式碼捕獲的異常,那麼會返回HTTP 500(Internal Server Error)錯誤。但是如果該異常是我們自己定義的並且使用@ResponseStatus註解進行修飾,那麼Spring MVC則會返回指定的HTTP狀態碼:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No Such Post")//404 Not Found
public class PostNotFoundException extends RuntimeException {
}

Controller中可以這樣使用它:

@RequestMapping(value = "/posts/{id}", method = RequestMethod.GET)
public String showPost(@PathVariable("id") long id, Model model) {
    Post post = postService.get(id);
    if (post == null) throw new PostNotFoundException("post not found");
    model.addAttribute("post", post);
    return "postDetail";
}

這樣如果我們訪問了一個不存在的文章,那麼Spring MVC會根據丟擲的PostNotFoundException上的註解值返回一個HTTP 404 Not Found給瀏覽器。

最佳實踐

上述場景中,除了獲取一篇文章的請求,還有更新和刪除一篇文章的方法中都需要判斷文章id是否存在。在每一個方法中都加上if (post == null) throw new PostNotFoundException("post not found");是一種解決方案,但如果有10個、20個包含/posts/{id}的方法,雖然只有一行程式碼但讓他們重複10次、20次也是非常不優雅的。

為了解決這個問題,可以將這個邏輯放在Service中實現:

@Service
public class PostService {

    @Autowired
    private PostRepository postRepository;

    public Post get(long id) {
        return postRepository.findById(id)
                .orElseThrow(() -> new PostNotFoundException("post not found"));
    }
}

這裡`PostRepository`繼承了`JpaRepository`,可以定義`findById`方法返回一個`Optional<Post>`——如果不存在則Optional為空,丟擲異常。

這樣在所有的Controller方法中,只需要正常獲取文章即可,所有的異常處理都交給了Spring MVC。

Controller中處理異常

Controller中的方法除了可以用於處理Web請求,還能夠用於處理異常處理——為它們加上@ExceptionHandler即可:

@Controller
public class ExceptionHandlingController {

  // @RequestHandler methods
  ...

  // Exception handling methods

  // Convert a predefined exception to an HTTP Status code
  @ResponseStatus(value=HttpStatus.CONFLICT, reason="Data integrity violation")  // 409
  @ExceptionHandler(DataIntegrityViolationException.class)
  public void conflict() {
    // Nothing to do
  }

  // Specify the name of a specific view that will be used to display the error:
  @ExceptionHandler({SQLException.class,DataAccessException.class})
  public String databaseError() {
    // Nothing to do.  Returns the logical view name of an error page, passed to
    // the view-resolver(s) in usual way.
    // Note that the exception is _not_ available to this view (it is not added to
    // the model) but see "Extending ExceptionHandlerExceptionResolver" below.
    return "databaseError";
  }

  // Total control - setup a model and return the view name yourself. Or consider
  // subclassing ExceptionHandlerExceptionResolver (see below).
  @ExceptionHandler(Exception.class)
  public ModelAndView handleError(HttpServletRequest req, Exception exception) {
    logger.error("Request: " + req.getRequestURL() + " raised " + exception);

    ModelAndView mav = new ModelAndView();
    mav.addObject("exception", exception);
    mav.addObject("url", req.getRequestURL());
    mav.setViewName("error");
    return mav;
  }
}

首先需要明確的一點是,在Controller方法中的@ExceptionHandler方法只能夠處理同一個Controller中丟擲的異常。這些方法上同時也可以繼續使用@ResponseStatus註解用於返回指定的HTTP狀態碼,但同時還能夠支援更加豐富的異常處理:

  • 渲染特定的檢視頁面
  • 使用ModelAndView返回更多的業務資訊

大多數網站都會使用一個特定的頁面來響應這些異常,而不是直接返回一個HTTP狀態碼或者顯示Java異常呼叫棧。當然異常資訊對於開發人員是非常有用的,如果想要在檢視中直接看到它們可以這樣渲染模板(以JSP為例):

<h1>Error Page</h1>
<p>Application has encountered an error. Please contact support on ...</p>

<!--
Failed URL: ${url}
Exception:  ${exception.message}
<c:forEach items="${exception.stackTrace}" var="ste">    ${ste} 
</c:forEach>
-->

全域性異常處理

@ControllerAdvice提供了和上一節一樣的異常處理能力,但是可以被應用於Spring應用上下文中的所有@Controller

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.CONFLICT)  // 409
    @ExceptionHandler(DataIntegrityViolationException.class)
    public void handleConflict() {
        // Nothing to do
    }
}

Spring MVC預設對於沒有捕獲也沒有被@ResponseStatus以及@ExceptionHandler宣告的異常,會直接返回500,這顯然並不友好,可以在@ControllerAdvice中對其進行處理(例如返回一個友好的錯誤頁面,引導使用者返回正確的位置或者提交錯誤資訊):

@ControllerAdvice
class GlobalDefaultExceptionHandler {
    public static final String DEFAULT_ERROR_VIEW = "error";

    @ExceptionHandler(value = Exception.class)
    public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        // If the exception is annotated with @ResponseStatus rethrow it and let
        // the framework handle it - like the OrderNotFoundException example
        // at the start of this post.
        // AnnotationUtils is a Spring Framework utility class.
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
            throw e;

        // Otherwise setup and send the user to a default error-view.
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);
        return mav;
    }
}

總結

Spring在異常處理方面提供了一如既往的強大特性和支援,那麼在應用開發中我們應該如何使用這些方法呢?以下提供一些經驗性的準則:

  • 不要在@Controller中自己進行異常處理邏輯。即使它只是一個Controller相關的特定異常,在@Controller中新增一個@ExceptionHandler方法處理。
  • 對於自定義的異常,可以考慮對其加上@ResponseStatus註解
  • 使用@ControllerAdvice處理通用異常(例如資源不存在、資源存在衝突等)

相關推薦

springmvc常用註解配置說明@<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6537001410001611080415150c0b02253504110d3304170c04070900">[

關於SpringMVC中@Requestmapping的相關配置可以參考下面文章: URL中的變數——PathVariable 在Web應用中URL通常不是一成不變的,例如微博兩個不同使用者的個人主頁對應兩個不同的URL: http://weibo.com/user1

Arch Linux 下Intel + NVIDIA 雙顯示卡3D 遊戲配置(<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="72161d06134032010617131f">[email 

下午打了幾場dota2 感覺流暢度還算很不錯的,寫點東西記錄一下。用Arch Linux 的一般來說都會用搜索引擎,所以只說下需要注意的地方即可。 1. steam 自帶的OpenGL 庫是過時的,

SpringMVC基礎<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ad80ede0c2c9c8c1ecd9d9dfc4cfd8d9c8">[email protected]a>註解&P

在總結@ModelAttribute註解功能配合PUT請求方式使用之前,先來總結一下POJO類作為入參接收form表單POST提交方式提交資料。 POJO類接收引數 總的來說POJO類還是與普通的Java Bean類還是特別的相似的,私有的屬性,需要

Spring高級話題<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b29ff2f7dcd3d0ded7">[email protected]a>***註解的工作原理

sso metadata bool logs tcl task ota -c ann 出自:http://blog.csdn.net/qq_26525215 @EnableAspectJAutoProxy @EnableAspectJAutoProxy註解 激活Aspe

<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="297a595b40474e69685c5d465e405b4c4d">[email protected]a>註解與自動裝配(轉發)

配置 調用方法 support autowired 信息 ann over 反射機制 test 1 配置文件的方法我們編寫spring 框架的代碼時候。一直遵循是這樣一個規則:所有在spring中註入的bean 都建議定義成私有的域變量。並且要配套寫上 get 和 se

Springboot註解<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="260b0b666549485254494a4a4354">[email protected]a>和@RestCon

1.使用@Controller 註解,在對應的方法上,檢視解析器可以解析return 的jsp,html頁面,並且跳轉到相應頁面;若返回json等內容到頁面,則需要加@ResponseBody註解 [email protected]註解,相當於@[email protected

eclipse支援@<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="95d2f0e1e1f0e7d5c6f0e1e1f0e7">[email protected]a>註解使用 -轉載

1. 下載lombok.jar 2.將下載的lombok.jar放在你的eclipse安裝目錄下,如圖: 3.修改eclipse.ini檔案,新增如下兩行配置:   -Xbootclasspath/a:lombok.jar -javaage

<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="40333032292e27001432212e33212334292f2e212c">[email protected]a>註解事務不回滾

這幾天在專案裡面發現我使用@Transactional註解事務之後,拋了異常居然不回滾。後來終於找到了原因。 如果你也出現了這種情況,可以從下面開始排查。 一、特性先來了解一下@Transactional註解事務的特性吧,可以更好排查問題 1、service類標籤(一般不建議在介面上)上新增@Transa

spring宣告式事務管理方式( 基於tx和aop名字空間的xml配置<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0922495d7b68677a686a7d6066676865">[email&#

轉自:https://www.cnblogs.com/niceyoo/p/8732891.html 1. 宣告式事務管理分類 宣告式事務管理也有兩種常用的方式, 一種是基於tx和aop名字空間的xml配置檔案,另一種就是基於@Transactional註解。 顯然基於註解的方式更簡單

spring <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e5878a8a91a5a08b84878980bdbdbdbd">[email protected]a>註解程式設計模型分析

@EnableXXXX程式設計模型 在spring boot中,@EnableXXX註解的功能通常是開啟某一種功能。根據某些外部配置自動裝配一些bean,來達到開啟某些功能的目的。光說很抽象,要具體分析。 @Enable模型的實現方式基本有3種。一個基本的@

kafka 消費者優化及配置詳解 <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4c3f3c3e25222b2e232338670c072d2a272d00253f382922293e">[email

自定義屬性和執行工廠 public KafkaListenerContainerFactory<?> batchFactory(){ ConcurrentKafkaListenerContainerFactory<Integer, Stri

springMVC @<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9eddf1f3eef1f0fbf0eab3deccfbedf1ebecfdfbb3">[email protected]

作用: @Component------------------------泛指元件,當元件不好歸類的時候,我們可以使用這個註解進行標註。(Component-------成分; 組分; 零件) @Resource------------------------(資源) @Autowired------

Spring註解<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7e53533e2e0c111817121b">[email protected]a>的使用

@Profile的作用:當容器根據標識啟用對應的@Profile註解時,其所關聯的bean類才會註冊到容器。但容器不能或找不到對應的@Profile,就不生成bean例項。 建立配置類MainConf

Spring註解開發<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="96a5bbbbd6dffbe6f9e4e2">[email protected]a> , FacotryBean 註冊

目錄 @ComponentScan註解用於掃描自己寫的類(@Controller,@Service,@Component,@Repository),用於加入IOC容器中 @Bean 可以把其他包或者自己寫的類加到IOC容器中 @Import 也可以辦

手寫<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="92e0f7f6fbe1d2d1f3f1faf7f3f0fef7">[email protected]a>註解 引數java物件作為ke

1.實現方式說明 本文在---- 手寫redis @ Cacheable註解支援過期時間設定   的基礎之上進行擴充套件。 1.1問題說明: @ Cacheable(key = “'leader'+#p0 +#p1 +#p2” )一般用法,#p0表示方法的第一個引數,#p1表示

手寫<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bdcfd8d9d4cefdfedcded5d8dcdfd1d8">[email protected]a>註解 支援過期時間設定

原理解釋  友情連結  手寫redis @ Cacheable註解引數java物件作為鍵值  @Cacheable註解作用,將帶有該註解方法的返回值存放到redis的的中; 使用方法在方法上使用@Cacheable(鍵=“測試+#P0 + P1

<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4f2326213a370f3a2d3a213b3a">[email protected]a>工具Eclipse配置python的pydev

沒辦法,被逼的,幹嘛成功後都要做下記錄,免得以後犯同樣的問題。 首先,電腦裡已經安裝了java環境,同時已經安裝好了eclipse 現在要做的就是配置Eclipse的python的環境,python一般是linux自帶的,不過要注意下puthon的版本。 第一步

<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="6c3f1c1e05020b213a2f2c22031829011c1815">[email protected]a>等註解不生效

現象   表單資料對應的Java Bean屬性上新增的諸多校驗註解不生效,如下示例: @NotEmpty(message = "規則不能為空", groups = {AppConfigGroup.Add.class,AppConfigGroup.Update.class}) @S

Spring 註解中 @<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="622c0d162c170e0e222c0d16270f12161b">[email protected]a>,@N

首先要清楚的是下邊1,2,3; 且空格是有長度的: 1  @NotNull:不能為null,但可以為empty,沒有Size的約束 2  @NotEmpty :不能為null,且Size>0 3  @NotBlank:只用於String,不能為null且trim()之

spring給容器中註冊元件的幾種方式,1.包掃描+元件標註註解<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0634284664636768">[email protected]a>(

              給容器中註冊元件;        1)、包掃描+元件標註註解(@Controller/@Service/@Repository/@Component)[