該類等價 與XML中配置beans,至關於Ioc容器,它的某個方法頭上若是註冊了@Bean,就會做爲這個Spring容器中的Bean,與xml中配置的bean意思同樣。javascript
@Configuration註解的類必需使用<context:component-scanbase-package="XXX"/>
掃描.以下:html
@Configuration public class TestConfiguration { //在properties文件裏配置 @Value("${wx_appid}") public String appid; @Bean public WxMpService wxMpService() { WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } }
測試:前端
package com.test.spring.support.configuration; public class TestMain { public static void main(String[] args) { //@Configuration註解的spring容器加載方式,用AnnotationConfigApplicationContext替換ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); //若是加載spring-context.xml文件: //ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); } }
定義一個MainConfig,用@Configuration註解,那MainConfig至關於xml裏的beans,裏面用@Bean註解的和xml裏定義的bean等價,用<context:component-scanbase-package=」XXX」/>
掃描該類,最終咱們能夠在程序裏用@AutoWired或@Resource註解取得用@Bean註解的bean,和用xml先配置bean而後在程序裏自動注入同樣。目的是減小xml裏配置。java
注: (1)、@Bean註解在返回實例的方法上,若是未經過@Bean指定bean的名稱,則默認與標註的方法名相同;git
(2)、@Bean註解默認做用域爲單例singleton做用域,可經過@Scope(「prototype」)設置爲原型做用域;程序員
(3)、既然@Bean的做用是註冊bean對象,那麼徹底可使用@Component、@Controller、@Service、@Ripository等註解註冊bean,固然須要配置@ComponentScan註解進行自動掃描。github
爲了簡化從properties裏取配置,可使用@Value, 能夠從properties文件中的配置值。web
在dispatcher-servlet.xml裏引入properties文件。ajax
<context:property-placeholder location="classpath:test.properties" />
在程序裏使用@Value:正則表達式
@Value("${wx_appid}") publicString appid;
即便給變量賦了初值也會以配置文件的值爲準。
目前4種註解意思是同樣,並無什麼區別,區別只是名字不一樣。使用方法:
<context:component-scanbase-package="XXX"/>
掃描被註解的類@Controller public class TestController { }
實現初始化和銷燬bean以前進行的操做,只能有一個方法能夠用此註釋進行註釋,方法不能有參數,返回值必需是void,方法須要是非靜態的。
例如:
public class TestService { @PostConstruct public void init(){ System.out.println("初始化"); } @PreDestroy public void dostory(){ System.out.println("銷燬"); } }
@PostConstruct:在構造方法和init方法(若是有的話)之間獲得調用,且只會執行一次。 被@PostConstruct修飾的方法會在服務器加載Servlet的時候運行,而且只會被服務器調用一次,相似於Serclet的inti()方法。被@PostConstruct修飾的方法會在構造函數以後,init()方法以前運行。
@PreDestory:註解的方法在destory()方法調用後獲得執行。被@PreDestory修飾的方法會在服務器卸載Servlet的時候運行,而且只會被服務器調用一次,相似於Servlet的destroy()方法。被@PreConstruct修飾的方法會在destroy()方法以後運行,在Servlet被完全卸載以前。
網上拷貝的流程圖:
引深一點,Spring 容器中的 Bean 是有生命週期的,Spring 容許在 Bean 在初始化完成後以及 Bean 銷燬前執行特定的操做,經常使用的設定方式有如下三種:
1.經過實現InitializingBean/DisposableBean
接口來定製初始化以後/銷燬以前的操做方法;
2.經過 <bean>
元素的init-method/destroy-method
屬性指定初始化以後 /銷燬以前調用的操做方法;
3.在指定方法上加上@PostConstruct
或@PreDestroy
註解來制定該方法是在初始化以後仍是銷燬以前調用
但他們以前並不等價。即便3個方法都用上了,也有前後順序.
Constructor > @PostConstruct >InitializingBean > init-method
自動裝配時當出現多個Bean候選者時,被註解爲@Primary的Bean將做爲首選者,不然將拋出異常。
例如:
@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(); } }
用於指定該Bean是否取消預初始化,用於註解類,延遲初始化。
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;
默認按 byName自動注入,若是找不到再按byType找bean,若是仍是找不到則拋異常,不管按byName仍是byType若是找到多個,則拋異常。
能夠手動指定bean,它有2個屬性分別是name和type,使用name屬性,則使用byName的自動注入,而使用type屬性時則使用byType自動注入。
@Resource(name=」bean名字」)
或
@Resource(type=」bean的class」)
這個註解是屬於J2EE的,減小了與spring的耦合。
基於@Async標註的方法,稱之爲異步方法,這個註解用於標註某個方法或某個類裏面的全部方法都是須要異步處理的。被註解的方法被調用的時候,會在新線程中執行,而調用它的方法會在原來的線程中執行。
application.xml形勢的配置:
<!--掃描註解,其中包括@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(「能夠指定執行器id,也能夠不指定」) public static void testAsyncVoid (){ try { //讓程序暫停100秒,至關於執行一個很耗時的任務 System.out.println(「異常執行打印字符串」); Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } }
當在外部調用testAsync方法時即在新線程中執行,由上面<task: annotation-driven/>執行器去維護線程。
總結:先用context:component-scan去掃描註解,讓spring能識別到@Async註解,而後task:annotation-driven去驅動@Async註解,並能夠指定默認的線程執行器executor。那麼當用@Async註解的方法或類獲得調用時,線程執行器會建立新的線程去執行。
@Valid
網上一大片使用@Valid失效不能用的狀況。爲何呢?
@Valid必需使用在以@RequestBody接收參數的狀況下。
使用ajax以POST方式提交數據,禁止用Fiddler以及瀏覽器直接訪問的方式測試接口
用<mvc:annotation-driven />添加註解驅動。
@Valid是應用在javabean上的校驗。
<!--引入jar包--> <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>
這些jar包是須要的。@Valid是使用hibernate validation,可參考下面介紹的@RequestBody
@Valid下後面緊跟BindingResult result,驗證結果保存在result
例如:
@RequestMapping("/test") @RequestBody 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"; }
在入參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裏配置。
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(); } }
第二步。在XXController.java頭上添加@Validated,而後在@RequestParam後臺使用上面介紹的驗證註解,好比@NotBlank,@Rank.
以下:
@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("消息列表查詢失敗"); } }
}
當傳入非法參數時,會被全局處理器攔截到,(Spring切面編程方式),若是參數非法即刻給前臺返回錯誤數據。
測試:http://127.0.0.1:8080/TestValidate/test/testValidated?pk=2&age=12
返回:
注意
@Valid是使用hibernateValidation.jar作校驗
@Validated是隻用springValidator校驗機制使用
@Validated與@RequestBody結合使用時,在接口方法裏要增長@Valid。例如:
public Object edit(@Valid @RequestBody AddrRo addrRo) {.....}
處理映射請求的註解。用於類上,表示類中的全部響應請求的方法都是以該地址做爲父路徑。有6個屬性。
一、 value, method: value:指定請求的實際地址,指定的地址能夠是URI Template 模式; method:指定請求的method類型, GET、POST、PUT、DELETE等; 好比:
@RequestMapping(value = "/testValid", method = RequestMethod.POST) @ResponseBody public Object testValid(@RequestBody @Valid Test test,BindingResult result, HttpServletRequest request, HttpServletResponse response) { XXX }
value的uri值爲如下三類:
A) 能夠指定爲普通的具體值;如@RequestMapping(value ="/testValid")
B) 能夠指定爲含有某變量的一類值;如@RequestMapping(value="/{day}")
,配合@PathVariable使用
C) 能夠指定爲含正則表達式的一類值;如@RequestMapping(value="/{textualPart:[a-z-]+}.{numericPart:[\\d]+}")
能夠匹配../chenyuan122912
請求。配合@PathVariable使用
二、 consumes,produces: consumes: 指定處理請求的提交內容類型(Content-Type),例如@RequestMapping(value = "/test", consumes="application/json")
處理application/json
內容類型
produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;
3 params、headers: params: 指定request中必須包含某些參數值是,才讓該方法處理。
例如:
@RequestMapping(value = "/test", method = RequestMethod.GET, params="name=chenyuan") public void findOrd(String name) { // implementation omitted }
僅處理請求中包含了名爲「name」,值爲「chenyuan」的請求.
headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求。
@RequestMapping(value = "/test", method = RequestMethod.GET, headers="Referer=www.baidu.com") public void findOrd(String name) { // implementation omitted }
僅處理request的header中包含了指定「Refer」請求頭和對應值爲「www.baidu.com」的請求
@GetMapping(value = "page")等價於@RequestMapping(value = "page", method = RequestMethod.GET)
@PostMapping(value = "page")等價於@RequestMapping(value = "page", method = RequestMethod.POST)
@RequestBody(required=true)
:有個默認屬性required,默認是true,當body裏沒內容時拋異常。
引用jar包:
Spring相關jar包。
以及
<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>
dispatchServlet-mvc.xml配置 第一種,直接配置MappingJackson2HttpMessageCoverter:
<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>
第二種:配置註解驅動<mvc:annotation-driven/>
就不用配置上面bean,默認會配好。
Ajax請求:
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); } }); }
後臺XXXcontroller.java:
@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 List<Map<String,Object>> param,HttpServletRequest request, HttpServletResponse response){ return "success"; }
注意下面幾個要點:
必需使用POST方式提交參數,須要使用ajax方式請求,用Fiddler去模擬post請求不能實現。
Content-Type必需是application/json
是Cross-Origin ResourceSharing(跨域資源共享)的簡寫
做用是解決跨域訪問的問題,在Spring4.2以上的版本可直接使用。在類上或方法上添加該註解
controller設置請求地址:
import java.util.HashMap; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * */ @RestController @RequestMapping(value = "/api", method = RequestMethod.POST) public class ApiController { @CrossOrigin(origins = "http://172.16.71.27:8080") @RequestMapping(value = "/get") public HashMap<String, Object> get(@RequestParam String name) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("title", "hello world"); map.put("name", name); return map; } }
ajax訪問:
<script> $(function() { $('#title').click(function() { // alert('點擊了'); $.ajax({ url : "http://localhost:8081/api/get", type : "POST", data : { name : "測試" }, success : function(data, status, xhr) { console.log(data); alert(data.name); } }); }); }) </script>
特別注意:
1.必定要在某類 或者某方法上 添加相似 method = RequestMethod.POST 的屬性
eg: @RequestMapping(value = "/api", method = RequestMethod.POST)
2:在某個方法上添加@CrossOrigin 註解時 origins 屬性必定要寫ip號 若是輸入localhost有時會出現403錯誤
eg:@CrossOrigin(origins = "http://172.16.71.27:8080")
做用是提取和解析請求中的參數。@RequestParam支持類型轉換,類型轉換目前支持全部的基本Java類型
@RequestParam(value="number", required=false,defaultValue =0) String number
將請求中參數爲number映射到方法的number上。required=false表示該參數不是必需的,請求上可帶可不帶。
@PathVariable:處理request uri部分,當使用@RequestMapping URI template 樣式映射時, 即someUrl/{paramId}
, 這時的paramId可經過 @Pathvariable註解綁定它傳過來的值到方法的參數上
例如:
@Controller @RequestMapping("/owners/{a}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{b}") public void findPet(@PathVariable("a") String a,@PathVariable String b, Model model) { // implementation omitted } }
匹配正則表達式
不少時候,須要對URL變量進行更加精確的定義,例如-用戶名只可能包含小寫字母,數字,下劃線,咱們但願:
除了簡單地定義{username}變量,還能夠定義正則表達式進行更精確的控制,定義語法是{變量名:正則表達式}[a-zA-Z0-9_]+
一個正則表達式,表示只能包含小寫字母,大寫字母,數字,下劃線。如此設置URL變量規則後,不合法的URL則不會被處理,直接由SpringMVC框架返回404Not Found。
@RequestMapping("/user/{username:[a-zA-Z0-9_]+}/blog/{blogId}")
總結
處理request header部分的註解
//將頭部信息綁定到方法參數上: @RequestMapping("/test") public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive")long keepAlive) { //... } //將cookie裏JSESSIONID綁定到方法參數上 @RequestMapping("/test") public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie){ //... }
@ModelAttribute
在Spring mvc中,註解@ModelAttribute是一個很是經常使用的註解,其功能主要在兩方面:
通常開發中,第一種用法居多
@SessionAttributes
@sessionattributes註解應用到Controller上面,能夠將Model中的屬性同步到session當中。
@Controller @RequestMapping("/Demo.do") @SessionAttributes(value={"attr1","attr2"}) public class Demo { @RequestMapping(params="method=index") public ModelAndView index1() { ModelAndView mav = new ModelAndView("index.jsp"); mav.addObject("attr1", "attr1Value"); mav.addObject("attr2", "attr2Value"); return mav; } @RequestMapping(params="method=index2") public ModelAndView index2(@ModelAttribute("attr1")String attr1, @ModelAttribute("attr2")String attr2) { ModelAndView mav = new ModelAndView("success.jsp"); return mav; } }
index1方法返回一個ModelAndView 其中包括視圖index.jsp 和 兩個鍵值放入model當中,在沒有加入@sessionattributes註解的時候,放入model當中的鍵值是request級別的。
如今由於在Controller上面標記了@SessionAttributes(value={"attr1","attr2"}) 那麼model中的attr1,attr2會同步到session中,這樣當你訪問index 而後在去訪問index2的時候也會獲取這倆個屬性的值。
當須要清除session當中的值得時候,咱們只須要在controller的方法中傳入一個SessionStatus的類型對象 經過調用setComplete方法就能夠清除了。
@RequestMapping(params="method=index3") public ModelAndView index4(SessionStatus status) { ModelAndView mav = new ModelAndView("success.jsp"); status.setComplete(); return mav; }
在controller中獲取sessionAttributes的只有兩種方式。
1、public ModelAndView index2(@ModelAttribute("attr1")String attr1, @ModelAttribute("attr2")String attr2)
2、public ModelAndView index2(ModelMap map)
這倆種方式的區別是,若是使用@ModelAttribute屬性獲取值,而且@SessionAttributes註解當中還設置了該屬性,當屬性爲null時會跑出異常,由於這幾行代碼。
配置bean的做用域。
@Controller @RequestMapping("/test") @Scope("prototype") public class TestController { }
@Scope 註解 目前用到的大多就是 單列 singleton 和多例 prototype 。默認是單例模式,即@Scope("singleton"),
一般使用狀況: @Service 直接默認就行,因此不用加@Scope註解 順便提醒一下,既然是單例。。。屬性的值但是下一次訪問還在哦 ,@Controller 控制層用多例 @Scope("prototype") 畢竟咱們不想兩個用戶的訪問結果互相串。若是有其它具體需求再具體分析
singleton:單例,即容器裏只有一個實例對象。
prototype:多對象,每一次請求都會產生一個新的bean實例,Spring不沒法對一個prototype bean的整個生命週期負責,容器在初始化、配置、裝飾或者是裝配完一個prototype實例後,將它交給客戶端,由程序員負責銷燬該對象,無論何種做用域,容器都會調用全部對象的初始化生命週期回調方法,而對prototype而言,任何配置好的析構生命週期回調方法都將不會被調用
這裏牽扯到單利模式的線程安全的設計。
咱們知道sping ioc注入的bean;通常都是無狀態的【dao,service等,這種不會牽涉到值或者狀態改變的狀況】,也就是在多線程下能夠基本保證線程安全的;可是,有些狀況下多是有狀態的;有狀態的bean ,是要注意線程安全的;spring是有考慮到這方面的需求的;prototype 原型類型,應運而生;
1.1 xml配置:
<bean id="virtualProductService" class="com.san.mpa.service.VirtualProductService" scope="prototype">
1.2 註解配置:
@Scope("prototype") public class CustInfoList extends HttpServlet {}
2.1有狀態會話bean (多對象) :
每一個用戶有本身特有的一個實例,在用戶的生存期內,bean保持了用戶的信息,即「有狀態」;一旦用戶滅亡(調用結束或實例結束),bean的生命期也告結束。即每一個用戶最初都會獲得一個初始的bean。
2.2無狀態會話bean (單例) :
bean一旦實例化就被加進會話池中,各個用戶均可以共用。即便用戶已經消亡,bean 的生命期也不必定結束,它可能依然存在於會話池中,供其餘用戶調用。因爲沒有特定的用戶,那麼也就不能保持某一用戶的狀態,因此叫無狀態bean。但無狀態會話bean 並不是沒有狀態,若是它有本身的屬性(變量),那麼這些變量就會受到全部調用它的用戶的影響,這是在實際應用中必須注意的。
使用prototype後可使併發調用在不一樣的實例中完成,不會產生線程安全的問題。
request:對每一次HTTP請求都會產生一個新的bean,同時該bean僅在當前HTTP request內有效
web.xml增長以下配置:
<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:做用不大,可不用管他。
測試:
@Controller @RequestMapping("/demo/lsh/ch5") @Scope("prototype") public class MultViewController { private static int st = 0; //靜態的 private int index = 0; //非靜態 @RequestMapping("/test") public String test() { System.out.println(st++ + " | " + index++); return "/lsh/ch5/test"; } } 屢次請求:/test 後運行結果: 單例的@Scope("singleton"): 0 | 0 1 | 1 2 | 2 3 | 3 4 | 4 改成多例的@Scope("prototype"): 0 | 0 1 | 0 2 | 0 3 | 0 4 | 0 最佳實踐:定義一個非靜態成員變量時候,則經過註解@Scope("prototype"),將其設置爲多例模式。
@ResponseStatus用於修飾一個類或者一個方法。
例如:
首先定義一個異常類
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="出現了錯誤") public class UserException extends RuntimeException{ XXXXX }
當訪問此接口,某處拋出UserException時,則會把value和reason返回給前端。
@RequestMapping("/testResponseStatus") public String testResponseStatus(int i){ if(i==0) throw new UserNotMatchException(); return "hello"; }
前端顯示異常信息:
修飾方法:
若是@ResponseStatus做用於方法上,不管方法執行成功與否都會返回到自定義的異常頁面。
@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(); } }
結果以下:
正如上面所說,該方法獲得調用,不管是否拋異常,都會把value和reason添加到response裏。
總結:@ResponseStatus是爲了在方法或類獲得調用時將指定的code和reason添加到response裏返前端,就像服務器常給咱們報的404錯誤同樣,咱們能夠本身指定高逼格錯誤提示。
官方解釋是:It is typically used todefine@ExceptionHandler,@InitBinder, and@ModelAttribute methods that apply to all@RequestMapping methods
意思是:即在@ControllerAdvice註解的類中。把使用了@ExceptionHandler、@InitBinder、@ModelAttribute註解的方法應用到全部(全局)的使用了 @RequestMapping註解的方法。很是簡單,不過只有當使用@ExceptionHandler最有用,另外兩個用處不大。
@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); } }
即表示讓Spring捕獲到全部被@RequestMapping註解的方法所拋出的SQLException異常,並交由這個被註解的handleSQLException方法處理,同時使用@ResponseStatus指定了code和reason寫到response上,返回給前端。
注意:ControllerAdvice只能捕獲到全局Controller範圍內的,以外的異常就沒法捕獲了,如filter中拋出異常的話,ControllerAdvice是沒法捕獲的。
元註解是指註解的註解,好比咱們看到的ControllerAdvice註解定義以下。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { XXX }
@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:說明子類能夠繼承父類中的該註解
好比自定義註解是:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface DBTable { public String name() default ""; }
表示該註解只能用在接口、類、枚舉、註解上。該註解會被編譯到class裏可經過反射獲得。
@Cacheable能夠標記在一個方法上,也能夠標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的,當標記在一個類上時則表示該類全部的方法都是支持緩存的。對於一個支持緩存的方法,Spring會在其被調用後將其返回值緩存起來,以保證下次利用一樣的參數來執行該方法時能夠直接從緩存中獲取結果,而不須要再次執行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果,至於鍵的話,Spring又支持兩種策略,默認策略和自定義策略,這個稍後會進行說明。須要注意的是當一個支持緩存的方法在對象內部被調用時是不會觸發緩存功能的。@Cacheable能夠指定三個屬性,value、key和condition。
1.value屬性指定Cache名稱
value屬性是必須指定的,其表示當前方法的返回值是會被緩存在哪一個Cache上的,對應Cache的名稱。其能夠是一個Cache也能夠是多個Cache,當須要指定多個Cache時其是一個數組。
@Cacheable("cache1")//Cache是緩存在cache1上的 public User find(Integer id) { return null; } @Cacheable({"cache1", "cache2"})//Cache是緩存在cache1和cache2上的 public User find(Integer id) { return null; }
2. 使用key屬性自定義key
key屬性是用來指定Spring緩存方法的返回結果時對應的key的。該屬性支持SpringEL表達式。當咱們沒有指定該屬性時,Spring將使用默認策略生成key。咱們這裏先來看看自定義策略,至於默認策略會在後文單獨介紹。
自定義策略是指咱們能夠經過Spring的EL表達式來指定咱們的key。這裏的EL表達式可使用方法參數及它們對應的屬性。使用方法參數時咱們能夠直接使用「#參數名」或者「#p參數index」。下面是幾個使用參數做爲key的示例。
@Cacheable(value="users", key="#id") public User find(Integer id) { return null; } @Cacheable(value="users", key="#p0") public User find(Integer id) { returnnull; } @Cacheable(value="users", key="#user.id") public User find(User user) { returnnull; } @Cacheable(value="users", key="#p0.id") public User find(User user) { returnnull; }
除了上述使用方法參數做爲key以外,Spring還爲咱們提供了一個root對象能夠用來生成key。經過該root對象咱們能夠獲取到如下信息。
屬性名稱 | 描述 | 示例 |
---|---|---|
methodName | 當前方法名 | #root.methodName |
method | 當前方法 | #root.method.name |
target | 當前被調用的對象 | #root.target |
targetClass | 當前被調用的對象的class | #root.targetClass |
args | 當前方法參數組成的數組 | #root.args[0] |
caches | 當前被調用的方法使用的Cache | #root.caches[0].name |
當咱們要使用root對象的屬性做爲key時咱們也能夠將「#root」省略,由於Spring默認使用的就是root對象的屬性。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name") public User find(User user) { returnnull; }
3.condition屬性指定發生的條件
有的時候咱們可能並不但願緩存一個方法全部的返回結果。經過condition屬性能夠實現這一功能。condition屬性默認爲空,表示將緩存全部的調用情形。其值是經過SpringEL表達式來指定的,當爲true時表示進行緩存處理;當爲false時表示不進行緩存處理,即每次調用該方法時該方法都會執行一次。以下示例表示只有當user的id爲偶數時纔會進行緩存。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0") public User find(User user) { System.*out*.println("find user by user " + user); return user; }
@CacheEvict是用來標註在須要清除緩存元素的方法或類上的。當標記在一個類上時表示其中全部的方法的執行都會觸發緩存的清除操做。@CacheEvict能夠指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應的屬性相似。即value表示清除操做是發生在哪些Cache上的(對應Cache的名稱);key表示須要清除的是哪一個key,如未指定則會使用默認策略生成的key;condition表示清除操做發生的條件。下面咱們來介紹一下新出現的兩個屬性allEntries和beforeInvocation。
1. allEntries屬性
allEntries是boolean類型,表示是否須要清除緩存中的全部元素。默認爲false,表示不須要。當指定了allEntries爲true時,Spring Cache將忽略指定的key。有的時候咱們須要Cache一下清除全部的元素,這比一個一個清除元素更有效率。
@CacheEvict(value="users", allEntries=true) public void delete(Integer id) { System.*out*.println("delete user by id: " + id); }
2. beforeInvocation屬性
清除操做默認是在對應方法成功執行以後觸發的,即方法若是由於拋出異常而未能成功返回時也不會觸發清除操做。使用beforeInvocation能夠改變觸發清除操做的時間,當咱們指定該屬性值爲true時,Spring會在調用該方法以前清除緩存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true) public void delete(Integer id) { System.*out*.println("delete user by id: " + id); }
其實除了使用@CacheEvict清除緩存元素外,當咱們使用Ehcache做爲實現時,咱們也能夠配置Ehcache自身的驅除策略,其是經過Ehcache的配置文件來指定的。