1.@Configuration註解
該類等價 與XML中配置beans,相當於Ioc容器,它的某個方法頭上如果註冊了@Bean,就會作爲這個Spring容器中的Bean,與xml中配置的bean意思一樣。
@Configuration註解的類必需使用<context:component-scanbase-package=”XXX”/>掃描.如下:
[html] view plain copy
- @Configuration
- public class MainConfig {
- //在properties文件裏配置
- @Value(“${wx_appid}”)
- public String appid;
- protected MainConfig(){}
- @Bean
- public WxMpService wxMpService() {
- WxMpService wxMpService = new WxMpServiceImpl();
- wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
- return wxMpService;
- }
- }
@Configuration
public class MainConfig {
//在properties文件裏配置
@Value("${wx_appid}")
public String appid;
protected MainConfig(){}
@Bean
public WxMpService wxMpService() {
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
定義一個MainConfig,用@Configuration註解,那MainConfig相當於xml裏的beans,裏面用@Bean註解的和xml裏定義的bean等價,用<context:component-scanbase-package=”XXX”/>掃描該類,最終我們可以在程序裏用@AutoWired或@Resource註解取得用@Bean註解的bean,和用xml先配置bean然後在程序裏自動注入一樣。目的是減少xml裏配置。
2.@Value註解
爲了簡化從properties裏取配置,可以使用@Value, 可以properties文件中的配置值。
在dispatcher-servlet.xml裏引入properties文件。
[html] view plain copy
- <context:property-placeholder location=“classpath:test.properties” />
<context:property-placeholder location="classpath:test.properties" />
在程序裏使用@Value:
@Value(“${wx_appid}”)
publicString appid;
即使給變量賦了初值也會以配置文件的值爲準。
3. @Controller, @Service, @Repository,@Component
目前4種註解意思是一樣,並沒有什麼區別,區別只是名字不同。使用方法:
1.使用<context:component-scanbase-package=”XXX”/>掃描被註解的類
2. 在類上寫註解:
@Controller
public class TestController {
}
4. @PostConstruct 和 @PreDestory
實現初始化和銷燬bean之前進行的操作,只能有一個方法可以用此註釋進行註釋,方法不能有參數,返回值必需是void,方法需要是非靜態的。
例如:
[html] view plain copy
- public class TestService {
- @PostConstruct
- public void init(){
- System.out.println(“初始化”);
- }
- @PreDestroy
- public void dostory(){
- System.out.println(“銷燬”);
- }
- }
public class TestService {
@PostConstruct
public void init(){
System.out.println("初始化");
}
@PreDestroy
public void dostory(){
System.out.println("銷燬");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
@PostConstruct:在構造方法和init方法(如果有的話)之間得到調用,且只會執行一次。
@PreDestory:註解的方法在destory()方法調用後得到執行。
網上拷貝的流程圖:
引深一點,Spring 容器中的 Bean 是有生命週期的,Spring 允許在 Bean 在初始化完成後以及 Bean 銷燬前執行特定的操作,常用的設定方式有以下三種:
1.通過實現 InitializingBean/DisposableBean 接口來定製初始化之後/銷燬之前的操作方法;
2.通過 <bean> 元素的 init-method/destroy-method屬性指定初始化之後 /銷燬之前調用的操作方法;
3.在指定方法上加上@PostConstruct 或@PreDestroy註解來制定該方法是在初始化之後還是銷燬之前調用
但他們之前並不等價。即使3個方法都用上了,也有先後順序.
Constructor > @PostConstruct >InitializingBean > init-method
5. @Primary
自動裝配時當出現多個Bean候選者時,被註解爲@Primary的Bean將作爲首選者,否則將拋出異常。
例如:
[html] view plain copy
- @Component
- public class Apple implements Fruit{
- @Override
- public String hello() {
- return ”我是蘋果”;
- }
- }
- @Component
- @Primary
- public class Pear implements Fruit{
- @Override
- public String hello(String lyrics) {
- return ”梨子”;
- }
- }
- public class FruitService {
- //Fruit有2個實例子類,因爲梨子用@Primary,那麼會使用Pear注入
- @Autowired
- private Fruit fruit;
- public String hello(){
- return fruit.hello();
- }
- }
@Component
public class Apple implements Fruit{
@Override
public String hello() {
return "我是蘋果";
}
}
@Component
@Primary
public class Pear implements Fruit{
@Override
public String hello(String lyrics) {
return "梨子";
}
}
public class FruitService {
//Fruit有2個實例子類,因爲梨子用@Primary,那麼會使用Pear注入
@Autowired
private Fruit fruit;
public String hello(){
return fruit.hello();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
6. @Lazy(true)
用於指定該Bean是否取消預初始化,用於註解類,延遲初始化。
7. @Autowired
Autowired默認先按byType,如果發現找到多個bean,則,又按照byName方式比對,如果還有多個,則報出異常。
1.可以手動指定按byName方式注入,使用@Qualifier。
//通過此註解完成從spring配置文件中 查找滿足Fruit的bean,然後按//@Qualifier指定pean
@Autowired
@Qualifier(“pean”)
public Fruit fruit;
2.如果要允許null 值,可以設置它的required屬性爲false,如:@Autowired(required=false)
public Fruit fruit;
8. @Resource
默認按 byName自動注入,如果找不到再按byType找bean,如果還是找不到則拋異常,無論按byName還是byType如果找到多個,則拋異常。
可以手動指定bean,它有2個屬性分別是name和type,使用name屬性,則使用byName的自動注入,而使用type屬性時則使用byType自動注入。
@Resource(name=”bean名字”)
或
@Resource(type=”bean的class”)
這個註解是屬於J2EE的,減少了與spring的耦合。
9. @Async
java裏使用線程用3種方法:
1. 繼承Thread,重寫run方法
2. 實現Runnable,重寫run方法
3. 使用Callable和Future接口創建線程,並能得到返回值。
前2種簡單,第3種方式特別提示一下,例子如下:
[html] view plain copy
- class MyCallable implements Callable<Integer> {
- private int i = 0;
- // 與run()方法不同的是,call()方法具有返回值
- @Override
- public Integer call() {
- int sum = 0;
- for (; i < 100; i++) {
- System.out.println(Thread.currentThread().getName() + ” ” + i);
- sum += i;
- }
- return sum;
- }
- }
class MyCallable implements Callable<Integer> {
private int i = 0;
// 與run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
main方法:
[html] view plain copy
- public static void main(String[] args) {
- Callable<Integer> myCallable = new MyCallable(); // 創建MyCallable對象
- FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable對象
- for (int i = 0; i < 100; i++) {
- System.out.println(Thread.currentThread().getName() + ” ” + i);
- if (i == 30) {
- Thread thread = new Thread(ft); //FutureTask對象作爲Thread對象的target創建新的線程
- thread.start(); //線程進入到就緒狀態
- }
- }
- System.out.println(“主線程for循環執行完畢..”);
- try {
- int sum = ft.get(); //取得新創建的新線程中的call()方法返回的結果
- System.out.println(“sum = ” + sum);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- }
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable(); // 創建MyCallable對象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable對象
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread thread = new Thread(ft); //FutureTask對象作爲Thread對象的target創建新的線程
thread.start(); //線程進入到就緒狀態
}
}
System.out.println("主線程for循環執行完畢..");
try {
int sum = ft.get(); //取得新創建的新線程中的call()方法返回的結果
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
而使用@Async可視爲第4種方法。基於@Async標註的方法,稱之爲異步方法,這個註解用於標註某個方法或某個類裏面的所有方法都是需要異步處理的。被註解的方法被調用的時候,會在新線程中執行,而調用它的方法會在原來的線程中執行。
application.xml形勢的配置:
第一步配置XML。
[html] view plain copy
- <!–掃描註解,其中包括@Async –>
- <context:component-scan base-package=“com.test”/>
- <!– 支持異步方法執行, 指定一個缺省的executor給@Async使用–>
- <task:annotation-driven executor=“defaultAsyncExecutor” />
- <!—配置一個線程執行器–>
- <task:executor id=” defaultAsyncExecutor ”pool-size=“100-10000” queue-capacity=“10” keep-alive =”5”/>
<!--掃描註解,其中包括@Async -->
<context:component-scan base-package="com.test"/>
<!-- 支持異步方法執行, 指定一個缺省的executor給@Async使用-->
<task:annotation-driven executor="defaultAsyncExecutor" />
<!—配置一個線程執行器-->
<task:executor id=" defaultAsyncExecutor "pool-size="100-10000" queue-capacity="10" keep-alive =”5”/>
- 1
- 2
- 3
- 4
- 5
- 6
參數解讀:
<task:executor />配置參數:
id:當配置多個executor時,被@Async(“id”)指定使用;也被作爲線程名的前綴。
pool-size:
core size:最小的線程數,缺省:1
max size:最大的線程數,缺省:Integer.MAX_VALUE
queue-capacity:當最小的線程數已經被佔用滿後,新的任務會被放進queue裏面,當這個queue的capacity也被佔滿之後,pool裏面會創建新線程處理這個任務,直到總線程數達到了max size,這時系統會拒絕這個任務並拋出TaskRejectedException異常(缺省配置的情況下,可以通過rejection-policy來決定如何處理這種情況)。缺省值爲:Integer.MAX_VALUE
keep-alive:超過core size的那些線程,任務完成後,再經過這個時長(秒)會被結束掉
rejection-policy:當pool已經達到max size的時候,如何處理新任務
ABORT(缺省):拋出TaskRejectedException異常,然後不執行DISCARD:不執行,也不拋出異常
DISCARD_OLDEST:丟棄queue中最舊的那個任務
CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
第二步在類或方法上添加@Async,當調用該方法時,則該方法即是用異常執行的方法單獨開個新線程執行。
[html] view plain copy
- @Async(“可以指定執行器id,也可以不指定”)
- public static void testAsyncVoid (){
- try {
- //讓程序暫停100秒,相當於執行一個很耗時的任務
- System.out.println(“異常執行打印字符串”);
- Thread.sleep(100000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
@Async(“可以指定執行器id,也可以不指定”)
public static void testAsyncVoid (){
try {
//讓程序暫停100秒,相當於執行一個很耗時的任務
System.out.println(“異常執行打印字符串”);
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
當在外部調用testAsync方法時即在新線程中執行,由上面<task: annotation-driven/>執行器去維護線程。
總結:先用context:component-scan去掃描註解,讓spring能識別到@Async註解,然後task:annotation-driven去驅動@Async註解,並可以指定默認的線程執行器executor。那麼當用@Async註解的方法或類得到調用時,線程執行器會創建新的線程去執行。
上面方法是無返回值的情況,還有異常方法有返回值的例子。
[html] view plain copy
- @Async
- public Future<String> testAsyncReturn () {
- System.out.println(“Execute method asynchronously - ”
- + Thread.currentThread().getName());
- try {
- Thread.sleep(5000);
- return new AsyncResult<String>(“hello world !!!!”);
- } catch (InterruptedException e) {
- //
- }
- return null;
- }
@Async
public Future<String> testAsyncReturn () {
System.out.println("Execute method asynchronously - "
+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
return new AsyncResult<String>("hello world !!!!");
} catch (InterruptedException e) {
//
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
返回的數據類型爲Future類型,接口實現類是AsyncResult.
調用方法如下:
[html] view plain copy
- public void test(){
- Future<String> future = cc.testAsyncReturn();
- while (true) { ///這裏使用了循環判斷,等待獲取結果信息
- if (future.isDone()) { //判斷是否執行完畢
- System.out.println(“Result from asynchronous process - ” + future.get());
- break;
- }
- System.out.println(“Continue doing something else. ”);
- Thread.sleep(1000);
- }
- }
public void test(){
Future<String> future = cc.testAsyncReturn();
while (true) { ///這裏使用了循環判斷,等待獲取結果信息
if (future.isDone()) { //判斷是否執行完畢
System.out.println("Result from asynchronous process - " + future.get());
break;
}
System.out.println("Continue doing something else. ");
Thread.sleep(1000);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
通過不停的檢查Future的狀態來獲取當前的異步方法是否執行完畢
編程的方式使用@Async:
[html] view plain copy
- @Configuration
- @EnableAsync
- public class SpringConfig {
- private int corePoolSize = 10;
- private int maxPoolSize = 200;
- private int queueCapacity = 10;
- private String ThreadNamePrefix = “MyLogExecutor-“;
- @Bean
- public Executor logExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(corePoolSize);
- executor.setMaxPoolSize(maxPoolSize);
- executor.setQueueCapacity(queueCapacity);
- executor.setThreadNamePrefix(ThreadNamePrefix);
- // rejection-policy:當pool已經達到max size的時候,如何處理新任務
- // CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- executor.initialize();
- return executor;
- }
- }
@Configuration
@EnableAsync
public class SpringConfig {
private int corePoolSize = 10;
private int maxPoolSize = 200;
private int queueCapacity = 10;
private String ThreadNamePrefix = "MyLogExecutor-";
@Bean
public Executor logExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix(ThreadNamePrefix);
// rejection-policy:當pool已經達到max size的時候,如何處理新任務
// CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
@Named和Spring的@Component功能相同。@Named可以有值,如果沒有值生成的Bean名稱默認和類名相同。比如
@Named public class Person
或
@Named(“cc”) public class Person
11. @Inject
使用@Inject需要引用javax.inject.jar,它與Spring沒有關係,是jsr330規範。
與@Autowired有互換性。
12. @Singleton
只要在類上加上這個註解,就可以實現一個單例類,不需要自己手動編寫單例實現類。
@Valid
網上一大片使用@Valid失效不能用的情況。爲什麼呢?
1.@Valid必需使用在以@RequestBody接收參數的情況下。
2.使用ajax以POST方式提示數據,禁止用Fiddler以及瀏覽器直接訪問的方式測試接口
3.用<mvc:annotation-driven />添加註解驅動。
4.@Valid是應用在javabean上的校驗。
5.
[html] view plain copy
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-validator</artifactId>
- <version>4.2.0.Final</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- <version>2.5.3</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- <version>2.5.3</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.5.3</version>
- </dependency>
- <dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-mapper-asl</artifactId>
- <version>1.9.8</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.module</groupId>
- <artifactId>jackson-module-jaxb-annotations</artifactId>
- <version>2.5.3</version>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.5.3</version>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
這些jar包是需要的。@Valid是使用hibernate validation的時候使用,可參數下面介紹的@RequestBody
6.@Valid下後面緊跟BindingResult result,驗證結果保存在result
例如:
[html] view plain copy
- @RequestMapping(“/test”)
- public String testValid(@Valid User user, BindingResult result){
- if (result.hasErrors()){
- List<ObjectError> errorList = result.getAllErrors();
- for(ObjectError error : errorList){
- System.out.println(error.getDefaultMessage());
- }
- }
- return “test”;
- }
@RequestMapping("/test")
public String testValid(@Valid User user, BindingResult result){
if (result.hasErrors()){
List<ObjectError> errorList = result.getAllErrors();
for(ObjectError error : errorList){
System.out.println(error.getDefaultMessage());
}
}
return "test";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在入參User上添加了@Valid做校驗,在User類裏屬性上實行實際的特定校驗。
例如在User的name屬性上加
@NotBlank
private String name;
全部參數校驗如下:
空檢查
@Null 驗證對象是否爲null
@NotNull 驗證對象是否不爲null, 無法查檢長度爲0的字符串
@NotBlank 檢查約束字符串是不是Null還有被Trim的長度是否大於0,只對字符串,且會去掉前後空格.
@NotEmpty 檢查約束元素是否爲NULL或者是EMPTY.
Booelan檢查
@AssertTrue 驗證 Boolean 對象是否爲 true
@AssertFalse 驗證 Boolean 對象是否爲 false
長度檢查
@Size(min=, max=) 驗證對象(Array,Collection,Map,String)長度是否在給定的範圍之內
@Length(min=, max=)驗證註解的元素值長度在min和max區間內
日期檢查
@Past 驗證 Date 和 Calendar 對象是否在當前時間之前
@Future 驗證 Date 和 Calendar 對象是否在當前時間之後
@Pattern 驗證 String 對象是否符合正則表達式的規則
數值檢查,建議使用在Stirng,Integer類型,不建議使用在int類型上,因爲表單值爲“”時無法轉換爲int,但可以轉換爲Stirng爲”“,Integer爲null
@Min(value=””) 驗證 Number 和 String 對象是否大等於指定的值
@Max(value=””) 驗證 Number 和 String 對象是否小等於指定的值
@DecimalMax(value=值) 被標註的值必須不大於約束中指定的最大值. 這個約束的參數是一個通過BigDecimal定義的最大值的字符串表示.小數存在精度
@DecimalMin(value=值) 被標註的值必須不小於約束中指定的最小值. 這個約束的參數是一個通過BigDecimal定義的最小值的字符串表示.小數存在精度
@Digits 驗證 Number 和 String 的構成是否合法
@Digits(integer=,fraction=)驗證字符串是否是符合指定格式的數字,interger指定整數精度,fraction指定小數精度。
@Range(min=, max=) 檢查數字是否介於min和max之間.
@Range(min=10000,max=50000,message=”range.bean.wage”)
private BigDecimal wage;
@Valid 遞歸的對關聯對象進行校驗, 如果關聯對象是個集合或者數組,那麼對其中的元素進行遞歸校驗,如果是一個map,則對其中的值部分進行校驗.(是否進行遞歸驗證)
@CreditCardNumber信用卡驗證
@Email 驗證是否是郵件地址,如果爲null,不進行驗證,算通過驗證。
@ScriptAssert(lang=,script=, alias=)
@URL(protocol=,host=,port=,regexp=, flags=)
@Validated
@Valid是對javabean的校驗,如果想對使用@RequestParam方式接收參數方式校驗使用@Validated
使用@Validated的步驟:
第一步:定義全局異常,讓該全局異常處理器能處理所以驗證失敗的情況,並返回給前臺失敗提示數據。如下,該類不用在任何xml裏配置。
[html] view plain copy
- import javax.validation.ValidationException;
- import org.springframework.context.annotation.Bean;
- import org.springframework.http.HttpStatus;
- import org.springframework.stereotype.Component;
- import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.ResponseStatus;
- @ControllerAdvice
- @Component
- public class GlobalExceptionHandler {
- @Bean
- public MethodValidationPostProcessor methodValidationPostProcessor() {
- return new MethodValidationPostProcessor();
- }
- @ExceptionHandler
- @ResponseBody
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- public String handle(ValidationException exception) {
- System.out.println(“bad request, ” + exception.getMessage());
- return “bad request, ” + exception.getMessage();
- }
- }
import javax.validation.ValidationException;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
@Component
public class GlobalExceptionHandler {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handle(ValidationException exception) {
System.out.println("bad request, " + exception.getMessage());
return "bad request, " + exception.getMessage();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
第二步。在XXController.java頭上添加@Validated,然後在@RequestParam後臺使用上面介紹的驗證註解,比如@NotBlank,@Rank.
如下:
[html] view plain copy
- @Controller
- @RequestMapping(“/test”)
- @Validated
- public class TestController extends BaseController {
- @RequestMapping(value = “testValidated”, method = RequestMethod.GET)
- @ResponseBody
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- public Object testValidated(@RequestParam(value = “pk”, required = true) @Size(min = 1, max = 3) String pk,
- @RequestParam(value = “age”, required = false) @Range(min = 1, max = 3) String age) {
- try {
- return “pk:” + pk + ”,age=” + age;
- } catch (Throwable t) {
- return buildFailure(“消息列表查詢失敗”);
- }
- }
- }
@Controller
@RequestMapping("/test")
@Validated
public class TestController extends BaseController {
@RequestMapping(value = "testValidated", method = RequestMethod.GET)
@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Object testValidated(@RequestParam(value = "pk", required = true) @Size(min = 1, max = 3) String pk,
@RequestParam(value = "age", required = false) @Range(min = 1, max = 3) String age) {
try {
return "pk:" + pk + ",age=" + age;
} catch (Throwable t) {
return buildFailure("消息列表查詢失敗");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
當入非法參數是,會被全局處理器攔截到,(Spring切面編程方式),如果參數非法即刻給前臺返回錯誤數據。
測試:http://127.0.0.1:8080/TestValidate/test/testValidated?pk=2&age=12
返回:
注意
@Valid是使用hibernateValidation.jar做校驗
@Validated是隻用springValidator校驗機制使用
@Validated與@RequestBody結合使用時,在接口方法裏要增加@Valid。例如:
[html] view plain copy
- public Object edit(@Valid @RequestBody AddrRo addrRo) {…..}
public Object edit(@Valid @RequestBody AddrRo addrRo) {.....}
14.@RequestBody
@RequestBody(required=true)
:有個默認屬性required,默認是true,當body裏沒內容時拋異常。
application/x-www-form-urlencoded:窗體數據被編碼爲名稱/值對。這是標準的編碼格式。這是默認的方式
multipart/form-data:窗體數據被編碼爲一條消息,頁上的每個控件對應消息中的一個部分。二進制數據傳輸方式,主要用於上傳文件
注意:必需使用POST方式提交參數,需要使用ajax方式請求,用Fiddler去模擬post請求不能。
引用jar包:
Spring相關jar包。
以及
[html] view plain copy
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- <version>2.5.3</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- <version>2.5.3</version>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.5.3</version>
- </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.3</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
dispatchServlet-mvc.xml配置
第一種,直接配置MappingJackson2HttpMessageCoverter:
[html] view plain copy
- <bean class=“org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping”></bean>
- <bean class=“org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter”>
- <property name=“messageConverters”>
- <bean class=“org.springframework.http.converter.json.MappingJackson2HttpMessageConverter”></bean>
- </property>
- </bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</property>
</bean>
- 1
- 2
- 3
- 4
- 5
- 6
第二種:<mvc:annotation-driven/> 就不用配置上面bean,默認會配好。
Ajax請求:
[html] view plain copy
- function testRequestBody() {
- var o = {“status”:9};
- jQuery.ajax({
- type: “POST”,
- url: “http://127.0.0.1:8080/TestValidate/test/testValid”,
- xhrFields:{
- withCredentials:true
- },
- data: JSON.stringify(o),
- contentType: “application/json”,
- dataType: “json”,
- async: false,
- success:function (data) {
- console.log(data);
- },
- error: function(res) {
- console.log(res);
- }
- });
- }
function testRequestBody() {
var o = {"status":9};
jQuery.ajax({
type: "POST",
url: "http://127.0.0.1:8080/TestValidate/test/testValid",
xhrFields:{
withCredentials:true
},
data: JSON.stringify(o),
contentType: "application/json",
dataType: "json",
async: false,
success:function (data) {
console.log(data);
},
error: function(res) {
console.log(res);
}
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
後臺XXXcontroller.java:
[html] view plain copy
- @RequestMapping(value=“/ testValid ”,method=RequestMethod.POST)
- @ResponseBody
- public Object setOrderInfo(@RequestBody InfoVO infoVO,HttpServletRequest request, HttpServletResponse response){
- InfoVO cVo = getInfoVo(infoVO);
- return “success”;
- }
@RequestMapping(value="/ testValid ",method=RequestMethod.POST)
@ResponseBody
public Object setOrderInfo(@RequestBody InfoVO infoVO,HttpServletRequest request, HttpServletResponse response){
InfoVO cVo = getInfoVo(infoVO);
return "success";
}
- 1
- 2
- 3
- 4
- 5
- 6
開發時,不是報415,就是400錯誤,頭都大了。還是細節沒做到位,注意下面幾個要點:
Content-Type必需是application/json
需要jackson-databind.jar
<mvc:annotation-driven/>要配置或直接配置bean
XXXController.jar在post方式接收數據
最最重要的,使用ajax以post方式請求。不能用Fiddler模擬,不然會出錯。
是Cross-Origin ResourceSharing(跨域資源共享)的簡寫
作用是解決跨域訪問的問題,在Spring4.2以上的版本可直接使用。在類上或方法上添加該註解
例如:
[html] view plain copy
- @CrossOrigin
- public class TestController extends BaseController {
- XXXX
- }
@CrossOrigin
public class TestController extends BaseController {
XXXX
}
- 1
- 2
- 3
- 4
- 5
- 6
如果失效則可能方法沒解決是GET還是POST方式,指定即可解決問題。
作用是提取和解析請求中的參數。@RequestParam支持類型轉換,類型轉換目前支持所有的基本Java類型
@RequestParam([value=”number”], [required=false]) String number
將請求中參數爲number映射到方法的number上。required=false表示該參數不是必需的,請求上可帶可不帶。
17. @PathVariable,@RequestHeader,@CookieValue,@RequestParam, @RequestBody,@SessionAttributes, @ModelAttribute;
@PathVariable:處理requet uri部分,當使用@RequestMapping URI template 樣式映射時, 即someUrl/{paramId}, 這時的paramId可通過 @Pathvariable註解綁定它傳過來的值到方法的參數上
例如:
[html] view plain copy
- @Controller
- @RequestMapping(“/owners/{a}”)
- public class RelativePathUriTemplateController {
- @RequestMapping(“/pets/{b}”)
- public void findPet(@PathVariable(“a”) String a,@PathVariable String b, Model model) {
- // implementation omitted
- }
- }
@Controller
@RequestMapping("/owners/{a}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{b}")
public void findPet(@PathVariable("a") String a,@PathVariable String b, Model model) {
// implementation omitted
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
@RequestHeader,@CookieValue: 處理request header部分的註解
將頭部信息綁定到方法參數上:
[html] view plain copy
- @RequestMapping(“/test”)
- public void displayHeaderInfo(@RequestHeader(“Accept-Encoding”) String encoding,
- @RequestHeader(“Keep-Alive”)long keepAlive) {
- //…
- }
@RequestMapping("/test")
public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive")long keepAlive) {
//...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
//將cookie裏JSESSIONID綁定到方法參數上
[html] view plain copy
- @RequestMapping(“/test”)
- public void displayHeaderInfo(@CookieValue(“JSESSIONID”) String cookie) {
- //…
- }
@RequestMapping("/test")
public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) {
//...
}
- 1
- 2
- 3
- 4
- 5
- 6
@RequestParam, @RequestBody: 處理request body部分的註解,已經介紹過,不用介紹了。
@SessionAttributes,@ModelAttribute:處理attribute類型是註解。XXXX
配置bean的作用域。
@Controller
@RequestMapping(“/test”)
@Scope(“prototype”)
public class TestController {
}
默認是單例模式,即@Scope(“singleton”),
singleton:單例,即容器裏只有一個實例對象。
prototype:多對象,每一次請求都會產生一個新的bean實例,Spring不無法對一個prototype bean的整個生命週期負責,容器在初始化、配置、裝飾或者是裝配完一個prototype實例後,將它交給客戶端,由程序員負責銷燬該對象,不管何種作用域,容器都會調用所有對象的初始化生命週期回調方法,而對prototype而言,任何配置好的析構生命週期回調方法都將不會被調用
request:對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效
web.xml增加如下配置:
[html] view plain copy
- <listener>
- <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
- </listener>
- session:該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效。也要在web.xml配置如下代碼:
- <listener>
- <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
- </listener>
- global session:作用不大,可不用管他。
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
session:該針對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP session內有效。也要在web.xml配置如下代碼:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
global session:作用不大,可不用管他。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
@ResponseStatus用於修飾一個類或者一個方法,修飾一個類的時候,一般修飾的是一個異常類,當處理器的方法被調用時,@ResponseStatus指定的code和reason會被返回給前端。value屬性是http狀態碼,比如404,500等。reason是錯誤信息
當修改類或方法時,只要該類得到調用,那麼value和reason都會被添加到response裏
例如:
[html] view plain copy
- @ResponseStatus(value=HttpStatus.FORBIDDEN, reason=“出現了錯誤”)
- public class UserException extends RuntimeException{
- XXXXX
- }
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="出現了錯誤")
public class UserException extends RuntimeException{
XXXXX
}
- 1
- 2
- 3
- 4
- 5
當某處拋出UserException時,則會把value和reason返回給前端。
[html] view plain copy
- @RequestMapping(“/testResponseStatus”)
- public String testResponseStatus(int i){
- if(i==0)
- throw new UserNotMatchException();
- return “hello”;
- }
@RequestMapping("/testResponseStatus")
public String testResponseStatus(int i){
if(i==0)
throw new UserNotMatchException();
return "hello";
}
- 1
- 2
- 3
- 4
- 5
- 6
修改方法:
[html] view plain copy
- @ControllerAdvice
- @Component
- public class GlobalExceptionHandler {
- @Bean
- public MethodValidationPostProcessor methodValidationPostProcessor() {
- return new MethodValidationPostProcessor();
- }
- @ExceptionHandler
- @ResponseBody
- @ResponseStatus(value=HttpStatus.BAD_REQUEST,reason=“哈哈”)
- public String handle(ValidationException exception) {
- System.out.println(“bad request, ” + exception.getMessage());
- return “bad request, ” + exception.getMessage();
- }
- }
@ControllerAdvice
@Component
public class GlobalExceptionHandler {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(value=HttpStatus.BAD_REQUEST,reason="哈哈")
public String handle(ValidationException exception) {
System.out.println("bad request, " + exception.getMessage());
return "bad request, " + exception.getMessage();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
結果如下:
正如上面所說,該方法得到調用,不論是否拋異常,都會把value和reason添加到response裏。
總結:@ResponseStatus是爲了在方法或類得到調用時將指定的code和reason添加到response裏返前端,就像服務器常給我們報的404錯誤一樣,我們可以自己指定高逼格錯誤提示。
20. @RestController
@RestController = @Controller + @ResponseBody。
是2個註解的合併效果,即指定了該controller是組件,又指定方法返回的是String或json類型數據,不會解決成jsp頁面,註定不夠靈活,如果一個Controller即有SpringMVC返回視圖的方法,又有返回json數據的方法即使用@RestController太死板。
靈活的作法是:定義controller的時候,直接使用@Controller,如果需要返回json可以直接在方法中添加@ResponseBody
官方解釋是:It is typically used todefine@ExceptionHandler,
@InitBinder, and@ModelAttribute methods that apply to all@RequestMapping methods
意思是:即把@ControllerAdvice註解內部使用@ExceptionHandler、@InitBinder、@ModelAttribute註解的方法應用到所有的 @RequestMapping註解的方法。非常簡單,不過只有當使用@ExceptionHandler最有用,另外兩個用處不大。
[html] view plain copy
- @ControllerAdvice
- public class GlobalExceptionHandler {
- @ExceptionHandler(SQLException.class)
- @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR,reason=”sql查詢錯誤”)
- @ResponseBody
- public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) {
- String message = ex.getMessage();
- return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
- }
- }
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(SQLException.class)
@ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR,reason=”sql查詢錯誤”)
@ResponseBody
public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) {
String message = ex.getMessage();
return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
即表示讓Spring捕獲到所有拋出的SQLException異常,並交由這個被註解的handleSQLException方法處理,同時使用@ResponseStatus指定了code和reason寫到response上,返回給前端。
22.元註解包括 @Retention @Target @Document @Inherited四種
元註解是指註解的註解,比如我們看到的ControllerAdvice註解定義如下。
[html] view plain copy
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Component
- public @interface ControllerAdvice {
- XXX
- }
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
XXX
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
@Retention: 定義註解的保留策略:
@Retention(RetentionPolicy.SOURCE) //註解僅存在於源碼中,在class字節碼文件中不包含
@Retention(RetentionPolicy.CLASS) //默認的保留策略,註解會在class字節碼文件中存在,但運行時無法獲得,
@Retention(RetentionPolicy.RUNTIME) //註解會在class字節碼文件中存在,在運行時可以通過反射獲取到
@Target:定義註解的作用目標:
@Target(ElementType.TYPE) //接口、類、枚舉、註解
@Target(ElementType.FIELD) //字段、枚舉的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法參數
@Target(ElementType.CONSTRUCTOR) //構造函數
@Target(ElementType.LOCAL_VARIABLE)//局部變量
@Target(ElementType.ANNOTATION_TYPE)//註解
@Target(ElementType.PACKAGE) ///包
由以上的源碼可以知道,他的elementType 可以有多個,一個註解可以爲類的,方法的,字段的等等
@Document:說明該註解將被包含在javadoc中
@Inherited:說明子類可以繼承父類中的該註解
比如@Valid註解定義是
表示該註解只能用在方法,屬性,構造函數及方法參數上。該注意會被編譯到class裏可通過反射得到。
處理映射請求的註解。用於類上,表示類中的所有響應請求的方法都是以該地址作爲父路徑。有6個屬性。
1、 value, method:
value:指定請求的實際地址,指定的地址可以是URI Template 模式;
method:指定請求的method類型, GET、POST、PUT、DELETE等;
比如:
[html] view plain copy
- @RequestMapping(value = “/testValid”, method = RequestMethod.POST)
- @ResponseBody
- public Object testValid(@RequestBody @Valid Test test,BindingResult result, HttpServletRequest request, HttpServletResponse response) {
- XXX
- }
@RequestMapping(value = "/testValid", method = RequestMethod.POST)
@ResponseBody
public Object testValid(@RequestBody @Valid Test test,BindingResult result, HttpServletRequest request, HttpServletResponse response) {
XXX
}
- 1
- 2
- 3
- 4
- 5
value的uri值爲以下三類:
A) 可以指定爲普通的具體值;如@RequestMapping(value =”/testValid”)
B) 可以指定爲含有某變量的一類值;如@RequestMapping(value=”/{day}”)
C) 可以指定爲含正則表達式的一類值;如@RequestMapping(value=”/{textualPart:[a-z-]+}.{numericPart:[\\d]+}”) 可以匹配../chenyuan122912請求。
2、 consumes,produces:
consumes: 指定處理請求的提交內容類型(Content-Type),例如@RequestMapping(value = ”/test”, consumes=”application/json”)處理application/json內容類型
produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;
3 params、headers:
params: 指定request中必須包含某些參數值是,才讓該方法處理。
例如:
[html] view plain copy
- @RequestMapping(value = “/test”, method = RequestMethod.GET, params=“name=chenyuan”)
- public void findOrd(String name) {
- // implementation omitted
- }
@RequestMapping(value = "/test", method = RequestMethod.GET, params="name=chenyuan")
public void findOrd(String name) {
// implementation omitted
}
- 1
- 2
- 3
- 4
僅處理請求中包含了名爲“name”,值爲“chenyuan”的請求.
headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。
[html] view plain copy
- @RequestMapping(value = “/test”, method = RequestMethod.GET, headers=“Referer=www.baidu.com”)
- public void findOrd(String name) {
- // implementation omitted
- }
@RequestMapping(value = "/test", method = RequestMethod.GET, headers="Referer=www.baidu.com")
public void findOrd(String name) {
// implementation omitted
}
- 1
- 2
- 3
- 4
僅處理request的header中包含了指定“Refer”請求頭和對應值爲“www.baidu.com”的請求
另贈spring提供的註解:
24. @GetMapping和@PostMapping
@GetMapping(value = “page”)等價於@RequestMapping(value = “page”, method = RequestMethod.GET)
@PostMapping(value = “page”)等價於@RequestMapping(value = “page”, method = RequestMethod.POST)