spring-boot注解详解(四)
@repository
@repository跟@Service,@Compent,@Controller這4種注解是沒(méi)什么本質(zhì)區(qū)別,都是聲明作用,取不同的名字只是為了更好區(qū)分各自的功能.下圖更多的作用是mapper注冊(cè)到類(lèi)似于以前mybatis.xml中的mappers里.
也是因?yàn)榻涌跊](méi)辦法在spring.xml中用bean的方式來(lái)配置實(shí)現(xiàn)類(lèi)吧(接口配不了),所以只能用注解或者mybatis.xml中掃描bean的方式來(lái)生成實(shí)現(xiàn)類(lèi)吧
一,首先:@repository是用來(lái)注解接口,如下圖:這個(gè)注解是將接口BookMapper的一個(gè)實(shí)現(xiàn)類(lèi)(具體這個(gè)實(shí)現(xiàn)類(lèi)的name叫什么,還需要再分析源碼找找看)交給spring管理(在spring中有開(kāi)啟對(duì)@repository注解的掃描),當(dāng)哪些地方需要用到這個(gè)實(shí)現(xiàn)類(lèi)作為依賴(lài)時(shí),就可以注入了.當(dāng)然我們也可以主動(dòng)給這個(gè)實(shí)現(xiàn)類(lèi)命名,如下圖
二,為什么有時(shí)候我們不用@repository來(lái)注解接口,我們照樣可以注入到這個(gè)接口的實(shí)現(xiàn)類(lèi)呢?如下圖,下圖是在接口沒(méi)有用
@repository注解的情況下,依然可以實(shí)現(xiàn)注入它的實(shí)現(xiàn)類(lèi).
上面是在idea中報(bào)了紅線(xiàn)警告,說(shuō)找不到這個(gè)實(shí)現(xiàn)類(lèi),但依然是可以運(yùn)行,沒(méi)有問(wèn)題(只是單純的警告),而在myeclipse中,是連警告都沒(méi)有的,運(yùn)行完全沒(méi)問(wèn)題.這是因?yàn)槿缦聢D:
是因?yàn)槲覀冊(cè)趍ybatis的xml文件配置了上圖這個(gè)bean,它會(huì)去將dao這個(gè)層中的mapper(也就是我們的接口)都生成實(shí)現(xiàn)類(lèi),然后交給spring管理(因?yàn)閙ybatis.xml文件我們最終還是導(dǎo)入了spring容器中),所以我們這里不對(duì)這些接口用@repository注解,也是一樣可以用它的實(shí)現(xiàn)類(lèi),(這也是我們寫(xiě)項(xiàng)目時(shí),有時(shí)感覺(jué)完全是沒(méi)用到@repository注解的原因,因?yàn)闆](méi)有什么必要)而idea報(bào)紅線(xiàn)警告,可能是idea自己的原因,這個(gè)在我們對(duì)它對(duì)應(yīng)的接口用@repository注解后,紅線(xiàn)警告會(huì)消失,運(yùn)行也完全沒(méi)問(wèn)題
@RequestBody:
作用:
主要用來(lái)接收前端傳遞給后端的json字符串中的數(shù)據(jù)的(請(qǐng)求體中的數(shù)據(jù)的);
要求:
GET方式無(wú)請(qǐng)求體,所以使用@RequestBody接收數(shù)據(jù)時(shí),前端不能使用GET方式提交數(shù)據(jù),而是用POST方式進(jìn)行提交。
在后端的同一個(gè)接收方法里,@RequestBody與@RequestParam()可以同時(shí)使用,@RequestBody最多只能有一個(gè),而@RequestParam()可以有多個(gè)。
簡(jiǎn)言之:
一個(gè)請(qǐng)求——》只有一個(gè)@RequestBody;
一個(gè)請(qǐng)求——》可以有多個(gè)@RequestParam。
******①同時(shí)使用@RequestParam()和@RequestBody
@RequestParam( )指定的參數(shù)可以是普通元素、數(shù)組、集合、對(duì)象等等
(即: @RequestBody 與@RequestParam()可以同時(shí)使用時(shí),原SpringMVC接收參數(shù)的機(jī)制不變,只不過(guò)RequestBody 接收的是請(qǐng)求體里面的數(shù)據(jù)(get是默認(rèn)的請(qǐng)求體,post是提交表單需要的請(qǐng)求體);而RequestParam接收的是key-value里面的參數(shù),所以它會(huì)被切面進(jìn)行處理從而可以用普通元素、數(shù)組、集合、對(duì)象等接收)。因此:
1、如果參數(shù)時(shí)放在請(qǐng)求體中,傳入后臺(tái)的話(huà),那么后臺(tái)要用@RequestBody才能接收到
否則就會(huì)在數(shù)據(jù)庫(kù)中不能完成curd操作;
2、如果不是放在 請(qǐng)求體中的話(huà),那么后臺(tái)接收前臺(tái)傳過(guò)來(lái)的參數(shù)時(shí),要用@RequestParam來(lái)接收,或則形參前什么也不寫(xiě)也能接收。
******②參數(shù)前寫(xiě)了@RequestParam(xxx)
1、前端必須有對(duì)應(yīng)的xxx名字才行(不管是否有值,可通過(guò)設(shè)置該注解的required屬性來(lái)調(diào)節(jié)是否必須傳)2、如果沒(méi)有xxx名的話(huà),那么請(qǐng)求會(huì)出錯(cuò),報(bào)400。******③參數(shù)前不寫(xiě)@RequestParam(xxx)
1、前端是否有對(duì)應(yīng)的xxx名字都行,如果有xxx名的話(huà),那么就會(huì)自動(dòng)匹配
2、沒(méi)有的話(huà),請(qǐng)求也能正確發(fā)送。
&&&追注:這里與feign消費(fèi)服務(wù)時(shí)不同;feign消費(fèi)服務(wù)時(shí),如果參數(shù)前什么也不寫(xiě),那么會(huì)被默認(rèn)@RequestBody的。******④如果后端參數(shù)是一個(gè)對(duì)象,且該參數(shù)前是以@RequestBody修飾的,那么前端傳遞json參數(shù)時(shí),必須滿(mǎn)足以下要求:
后端@RequestBody注解對(duì)應(yīng)的類(lèi)在將HTTP的輸入流(含請(qǐng)求體)裝配到目標(biāo)類(lèi)
(也就是:@RequestBody后面的類(lèi))時(shí),會(huì)根據(jù)json字符串中的key來(lái)匹配對(duì)應(yīng)實(shí)體類(lèi)的屬性,如果匹配一致且json中的該key對(duì)應(yīng)的值符合)
(或者說(shuō):實(shí)體類(lèi)的對(duì)應(yīng)屬性的類(lèi)型要求時(shí),會(huì)調(diào)用實(shí)體類(lèi)的setter方法將值賦給該屬性。)
1、json字符串中,如果value為 “” 的話(huà)(空串),后端對(duì)應(yīng)屬性如果是String類(lèi)型的,那么接受到的就是 “”
如果是后端屬性的類(lèi)型是Integer、Double等類(lèi)型,那么接收到的就是null。
2、json字符串中,如果value為null的話(huà),后端對(duì)應(yīng)收到的就是null。
3、如果某個(gè)參數(shù)沒(méi)有value的話(huà),在傳json字符串給后端時(shí),要么干脆就不把該字段寫(xiě)到j(luò)son字符串中;要么寫(xiě)value時(shí), 必須有值,null 或""都行。
千萬(wàn)不能有類(lèi)似"stature":,這樣的寫(xiě)法,如:
總結(jié):
結(jié)論①:@JsonAlias注解,實(shí)現(xiàn):json轉(zhuǎn)模型時(shí),使json中的特定key能轉(zhuǎn)化為特定的模型屬性;但是模型轉(zhuǎn)json時(shí),
對(duì)應(yīng)的轉(zhuǎn)換后的key仍然與屬性名一致
結(jié)論②:@JsonProperty注解,實(shí)現(xiàn):json轉(zhuǎn)模型時(shí),使json中的特定key能轉(zhuǎn)化為指定的模型屬性;同樣的,模
型轉(zhuǎn)json時(shí),對(duì)應(yīng)的轉(zhuǎn)換后的key為指定的key
結(jié)論③:@JsonAlias注解需要依賴(lài)于setter、getter,而@JsonProperty注解不需要。
結(jié)論④:在不考慮上述兩個(gè)注解的一般情況下,key與屬性匹配時(shí),默認(rèn)大小寫(xiě)敏感。
結(jié)論⑤:有多個(gè)相同的key的json字符串中,轉(zhuǎn)換為模型時(shí),會(huì)以相同的幾個(gè)key中,排在最后的那個(gè)key的值給模
型屬性復(fù)制,因?yàn)閟etter會(huì)覆蓋原來(lái)的值。見(jiàn)示例中的gender屬性。
結(jié)論⑥:后端@RequestBody注解對(duì)應(yīng)的類(lèi)在將HTTP的輸入流(含請(qǐng)求體)裝配到目標(biāo)類(lèi)(即:@RequestBody后面
的類(lèi))時(shí),會(huì)根據(jù)json字符串中的key來(lái)匹配對(duì)應(yīng)實(shí)體類(lèi)的屬性,如果匹配一致且json中的該key對(duì)應(yīng)的值
符合(或可轉(zhuǎn)換為)實(shí)體類(lèi)的對(duì)應(yīng)屬性的類(lèi)型要求時(shí),會(huì)調(diào)用實(shí)體類(lèi)的setter方法將值賦給該屬性。
@ComponentScan
其實(shí)很簡(jiǎn)單,@ComponentScan主要就是定義掃描的路徑從中找出標(biāo)識(shí)了需要裝配的類(lèi)自動(dòng)裝配到spring的bean容器中
做過(guò)web開(kāi)發(fā)的同學(xué)一定都有用過(guò)@Controller,@Service,@Repository注解,查看其源碼你會(huì)發(fā)現(xiàn),他們中有一個(gè)共同的注解@Component,沒(méi)錯(cuò)@ComponentScan注解默認(rèn)就會(huì)裝配標(biāo)識(shí)了@Controller,@Service,@Repository,@Component注解的類(lèi)到spring容器中,好下面咱們就先來(lái)簡(jiǎn)單演示一下這個(gè)例子
在包c(diǎn)om.zhang.controller下新建一個(gè)UserController帶@Controller注解如下:
package com.zhang.controller; import org.springframework.stereotype.Controller; @Controller public class UserController { }在包c(diǎn)om.zhang.service下新建一個(gè)UserService帶@Service注解如下:
package com.zhang.service; import org.springframework.stereotype.Service; @Service public class UserService { }在包c(diǎn)om.zhang.dao下新建一個(gè)UserDao帶@Repository注解如下:
package com.zhang.dao; import org.springframework.stereotype.Repository; @Repository public class UserDao { }新建一個(gè)配置類(lèi)如下:
/*** 主配置類(lèi) 包掃描com.zhang** @author zhangqh* @date 2018年5月12日*/ @ComponentScan(value="com.zhang") @Configuration public class MainScanConfig { }新建測(cè)試方法如下:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainScanConfig.class);String[] definitionNames = applicationContext2.getBeanDefinitionNames();for (String name : definitionNames) {System.out.println(name); }運(yùn)行結(jié)果如下:
mainScanConfig userController userDao userService怎么樣,包掃描的方式比以前介紹的通過(guò)@Bean注解的方式是不是方便很多,這也就是為什么web開(kāi)發(fā)的同學(xué)經(jīng)常使用此方式的原因了
上面只是簡(jiǎn)單的介紹了@ComponentScan注解檢測(cè)包含指定注解的自動(dòng)裝配,接下來(lái)讓我們來(lái)看看@ComponentScan注解的更加詳細(xì)的配置,在演示詳細(xì)的配置之前,讓我們先看看@ComponentScan的源代碼如下:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Repeatable(ComponentScans.class) public @interface ComponentScan {/*** 對(duì)應(yīng)的包掃描路徑 可以是單個(gè)路徑,也可以是掃描的路徑數(shù)組* @return*/@AliasFor("basePackages")String[] value() default {};/*** 和value一樣是對(duì)應(yīng)的包掃描路徑 可以是單個(gè)路徑,也可以是掃描的路徑數(shù)組* @return*/@AliasFor("value")String[] basePackages() default {};/*** 指定具體的掃描的類(lèi)* @return*/Class<?>[] basePackageClasses() default {};/*** 對(duì)應(yīng)的bean名稱(chēng)的生成器 默認(rèn)的是BeanNameGenerator* @return*/Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;/*** 處理檢測(cè)到的bean的scope范圍*/Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;/*** 是否為檢測(cè)到的組件生成代理* Indicates whether proxies should be generated for detected components, which may be* necessary when using scopes in a proxy-style fashion.* <p>The default is defer to the default behavior of the component scanner used to* execute the actual scan.* <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)*/ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;/*** 控制符合組件檢測(cè)條件的類(lèi)文件 默認(rèn)是包掃描下的 **/*.class* @return*/String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;/*** 是否對(duì)帶有@Component @Repository @Service @Controller注解的類(lèi)開(kāi)啟檢測(cè),默認(rèn)是開(kāi)啟的* @return*/boolean useDefaultFilters() default true;/*** 指定某些定義Filter滿(mǎn)足條件的組件 FilterType有5種類(lèi)型如:* ANNOTATION, 注解類(lèi)型 默認(rèn)ASSIGNABLE_TYPE,指定固定類(lèi)ASPECTJ, ASPECTJ類(lèi)型REGEX,正則表達(dá)式CUSTOM,自定義類(lèi)型* @return*/Filter[] includeFilters() default {};/*** 排除某些過(guò)來(lái)器掃描到的類(lèi)* @return*/Filter[] excludeFilters() default {};/*** 掃描到的類(lèi)是都開(kāi)啟懶加載 ,默認(rèn)是不開(kāi)啟的* @return*/boolean lazyInit() default false; }a,演示basePackageClasses參數(shù),如我們把配置文件改成如下:
@ComponentScan(value="com.zhang.dao",useDefaultFilters=true,basePackageClasses=UserService.class) @Configuration public class MainScanConfig { }測(cè)試結(jié)果如下:
mainScanConfig userDao userServiceb,演示includeFilters參數(shù)的使用如下:
在com.zhang.service包下新建一個(gè)UserService2類(lèi)如下:注意沒(méi)有帶@Service注解
package com.zhang.service; public class UserService2 { }配置類(lèi)改成:
@ComponentScan(value="com.zhang",useDefaultFilters=true,includeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService2.class})}) @Configuration public class MainScanConfig { }運(yùn)行結(jié)果如下:
mainScanConfig userController userDao userService userService2userService2同樣被加入到了spring容器
新增一個(gè)自定義的實(shí)現(xiàn)了TypeFilter的MyTypeFilter類(lèi)如下:
/*** 自定義過(guò)濾** @author zhangqh* @date 2018年5月12日*/ public class MyTypeFilter implements TypeFilter {public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)throws IOException {AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();ClassMetadata classMetadata = metadataReader.getClassMetadata();Resource resource = metadataReader.getResource();String className = classMetadata.getClassName();System.out.println("--->"+className);// 檢測(cè)名字包含Service的beanif(className.contains("Service")){return true;}return false;} }修改主配置如下:
@ComponentScan(value="com.zhang",useDefaultFilters=true,includeFilters={@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})}) @Configuration public class MainScanConfig { }運(yùn)行結(jié)果如下:
mainScanConfig userController userDao userService userService2可以發(fā)現(xiàn)同樣userService2被加入到了spring容器中
好了includeFilters參數(shù)就演示到這,另外一個(gè)參數(shù)excludeFilters和includeFilters用戶(hù)一摸一樣,只是他是過(guò)濾出不加入spring容器中,感興趣的同學(xué)可以自己試試,我這邊就不演示了
總結(jié)一下@ComponentScan的常用方式如下
- 自定掃描路徑下邊帶有@Controller,@Service,@Repository,@Component注解加入spring容器
- 通過(guò)includeFilters加入掃描路徑下沒(méi)有以上注解的類(lèi)加入spring容器
- 通過(guò)excludeFilters過(guò)濾出不用加入spring容器的類(lèi)
- 自定義增加了@Component注解的注解方式
總結(jié)
以上是生活随笔為你收集整理的spring-boot注解详解(四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 答题小程序/刷题微信小程序/考试小程序2
- 下一篇: spring-boot注解详解(五)