@repository
@repository跟@Service,@Compent,@Controller這4種注解是沒什么本質區別,都是聲明作用,取不同的名字只是為了更好區分各自的功能.下圖更多的作用是mapper注冊到類似于以前mybatis.xml中的mappers里.
也是因為接口沒辦法在spring.xml中用bean的方式來配置實現類吧(接口配不了),所以只能用注解或者mybatis.xml中掃描bean的方式來生成實現類吧
一,首先:@repository是用來注解接口,如下圖:這個注解是將接口BookMapper的一個實現類(具體這個實現類的name叫什么,還需要再分析源碼找找看)交給spring管理(在spring中有開啟對@repository注解的掃描),當哪些地方需要用到這個實現類作為依賴時,就可以注入了.當然我們也可以主動給這個實現類命名,如下圖
二,為什么有時候我們不用@repository來注解接口,我們照樣可以注入到這個接口的實現類呢?如下圖,下圖是在接口沒有用
@repository注解的情況下,依然可以實現注入它的實現類.
上面是在idea中報了紅線警告,說找不到這個實現類,但依然是可以運行,沒有問題(只是單純的警告),而在myeclipse中,是連警告都沒有的,運行完全沒問題.這是因為如下圖:
是因為我們在mybatis的xml文件配置了上圖這個bean,它會去將dao這個層中的mapper(也就是我們的接口)都生成實現類,然后交給spring管理(因為mybatis.xml文件我們最終還是導入了spring容器中),所以我們這里不對這些接口用@repository注解,也是一樣可以用它的實現類,(這也是我們寫項目時,有時感覺完全是沒用到@repository注解的原因,因為沒有什么必要)而idea報紅線警告,可能是idea自己的原因,這個在我們對它對應的接口用@repository注解后,紅線警告會消失,運行也完全沒問題
@RequestBody:
作用:
主要用來接收前端傳遞給后端的json字符串中的數據的(請求體中的數據的);
要求:
GET方式無請求體,所以使用@RequestBody接收數據時,前端不能使用GET方式提交數據,而是用POST方式進行提交。
在后端的同一個接收方法里,@RequestBody與@RequestParam()可以同時使用,@RequestBody最多只能有一個,而@RequestParam()可以有多個。
簡言之:
一個請求——》只有一個@RequestBody;
一個請求——》可以有多個@RequestParam。
******①同時使用@RequestParam()和@RequestBody
@RequestParam( )指定的參數可以是普通元素、數組、集合、對象等等
(即: @RequestBody 與@RequestParam()可以同時使用時,原SpringMVC接收參數的機制不變,只不過RequestBody 接收的是請求體里面的數據(get是默認的請求體,post是提交表單需要的請求體);而RequestParam接收的是key-value里面的參數,所以它會被切面進行處理從而可以用普通元素、數組、集合、對象等接收)。
因此:
1、如果參數時放在請求體中,傳入后臺的話,那么后臺要用@RequestBody才能接收到
否則就會在數據庫中不能完成curd操作;
2、如果不是放在 請求體中的話,那么后臺接收前臺傳過來的參數時,要用@RequestParam來接收,或則形參前什么也不寫也能接收。
******②參數前寫了@RequestParam(xxx)
1、前端必須有對應的xxx名字才行(不管是否有值,可通過設置該注解的required屬性來調節是否必須傳)2、如果沒有xxx名的話,那么請求會出錯,報400。
******③參數前不寫@RequestParam(xxx)
1、前端是否有對應的xxx名字都行,如果有xxx名的話,那么就會自動匹配
2、沒有的話,請求也能正確發送。
&&&追注:這里與feign消費服務時不同;feign消費服務時,如果參數前什么也不寫,那么會被默認@RequestBody的。
******④如果后端參數是一個對象,且該參數前是以@RequestBody修飾的,那么前端傳遞json參數時,必須滿足以下要求:
后端@RequestBody注解對應的類在將HTTP的輸入流(含請求體)裝配到目標類
(也就是:@RequestBody后面的類)時,會根據json字符串中的key來匹配對應實體類的屬性,如果匹配一致且json中的該key對應的值符合)
(或者說:實體類的對應屬性的類型要求時,會調用實體類的setter方法將值賦給該屬性。)
1、json字符串中,如果value為 “” 的話(空串),后端對應屬性如果是String類型的,那么接受到的就是 “”
如果是后端屬性的類型是Integer、Double等類型,那么接收到的就是null。
2、json字符串中,如果value為null的話,后端對應收到的就是null。
3、如果某個參數沒有value的話,在傳json字符串給后端時,要么干脆就不把該字段寫到json字符串中;要么寫value時, 必須有值,null 或""都行。
千萬不能有類似"stature":,這樣的寫法,如:
總結:
結論①:@JsonAlias注解,實現:json轉模型時,使json中的特定key能轉化為特定的模型屬性;但是模型轉json時,
對應的轉換后的key仍然與屬性名一致
結論②:@JsonProperty注解,實現:json轉模型時,使json中的特定key能轉化為指定的模型屬性;同樣的,模
型轉json時,對應的轉換后的key為指定的key
結論③:@JsonAlias注解需要依賴于setter、getter,而@JsonProperty注解不需要。
結論④:在不考慮上述兩個注解的一般情況下,key與屬性匹配時,默認大小寫敏感。
結論⑤:有多個相同的key的json字符串中,轉換為模型時,會以相同的幾個key中,排在最后的那個key的值給模
型屬性復制,因為setter會覆蓋原來的值。見示例中的gender屬性。
結論⑥:后端@RequestBody注解對應的類在將HTTP的輸入流(含請求體)裝配到目標類(即:@RequestBody后面
的類)時,會根據json字符串中的key來匹配對應實體類的屬性,如果匹配一致且json中的該key對應的值
符合(或可轉換為)實體類的對應屬性的類型要求時,會調用實體類的setter方法將值賦給該屬性。
@ComponentScan
- @ComponentScan注解是什么
其實很簡單,@ComponentScan主要就是定義掃描的路徑從中找出標識了需要裝配的類自動裝配到spring的bean容器中
- @ComponentScan注解的詳細使用
做過web開發的同學一定都有用過@Controller,@Service,@Repository注解,查看其源碼你會發現,他們中有一個共同的注解@Component,沒錯@ComponentScan注解默認就會裝配標識了@Controller,@Service,@Repository,@Component注解的類到spring容器中,好下面咱們就先來簡單演示一下這個例子
在包com.zhang.controller下新建一個UserController帶@Controller注解如下:
package com.zhang.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
}
在包com.zhang.service下新建一個UserService帶@Service注解如下:
package com.zhang.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
}
在包com.zhang.dao下新建一個UserDao帶@Repository注解如下:
package com.zhang.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
}
新建一個配置類如下:
/*** 主配置類 包掃描com.zhang** @author zhangqh* @date 2018年5月12日*/
@ComponentScan(value="com.zhang")
@Configuration
public class MainScanConfig {
}
新建測試方法如下:
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainScanConfig.class);String[] definitionNames = applicationContext2.getBeanDefinitionNames();for (String name : definitionNames) {System.out.println(name);
}
運行結果如下:
mainScanConfig
userController
userDao
userService
怎么樣,包掃描的方式比以前介紹的通過@Bean注解的方式是不是方便很多,這也就是為什么web開發的同學經常使用此方式的原因了
上面只是簡單的介紹了@ComponentScan注解檢測包含指定注解的自動裝配,接下來讓我們來看看@ComponentScan注解的更加詳細的配置,在演示詳細的配置之前,讓我們先看看@ComponentScan的源代碼如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {/*** 對應的包掃描路徑 可以是單個路徑,也可以是掃描的路徑數組* @return*/@AliasFor("basePackages")String[] value() default {};/*** 和value一樣是對應的包掃描路徑 可以是單個路徑,也可以是掃描的路徑數組* @return*/@AliasFor("value")String[] basePackages() default {};/*** 指定具體的掃描的類* @return*/Class<?>[] basePackageClasses() default {};/*** 對應的bean名稱的生成器 默認的是BeanNameGenerator* @return*/Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;/*** 處理檢測到的bean的scope范圍*/Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;/*** 是否為檢測到的組件生成代理* 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;/*** 控制符合組件檢測條件的類文件 默認是包掃描下的 **/*.class* @return*/String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;/*** 是否對帶有@Component @Repository @Service @Controller注解的類開啟檢測,默認是開啟的* @return*/boolean useDefaultFilters() default true;/*** 指定某些定義Filter滿足條件的組件 FilterType有5種類型如:* ANNOTATION, 注解類型 默認ASSIGNABLE_TYPE,指定固定類ASPECTJ, ASPECTJ類型REGEX,正則表達式CUSTOM,自定義類型* @return*/Filter[] includeFilters() default {};/*** 排除某些過來器掃描到的類* @return*/Filter[] excludeFilters() default {};/*** 掃描到的類是都開啟懶加載 ,默認是不開啟的* @return*/boolean lazyInit() default false;
}
a,演示basePackageClasses參數,如我們把配置文件改成如下:
@ComponentScan(value="com.zhang.dao",useDefaultFilters=true,basePackageClasses=UserService.class)
@Configuration
public class MainScanConfig {
}
測試結果如下:
mainScanConfig
userDao
userService
b,演示includeFilters參數的使用如下:
在com.zhang.service包下新建一個UserService2類如下:注意沒有帶@Service注解
package com.zhang.service;
public class UserService2 {
}
配置類改成:
@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 {
}
運行結果如下:
mainScanConfig
userController
userDao
userService
userService2
userService2同樣被加入到了spring容器
新增一個自定義的實現了TypeFilter的MyTypeFilter類如下:
/*** 自定義過濾** @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);// 檢測名字包含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 {
}
運行結果如下:
mainScanConfig
userController
userDao
userService
userService2
可以發現同樣userService2被加入到了spring容器中
好了includeFilters參數就演示到這,另外一個參數excludeFilters和includeFilters用戶一摸一樣,只是他是過濾出不加入spring容器中,感興趣的同學可以自己試試,我這邊就不演示了
總結一下@ComponentScan的常用方式如下
- 自定掃描路徑下邊帶有@Controller,@Service,@Repository,@Component注解加入spring容器
- 通過includeFilters加入掃描路徑下沒有以上注解的類加入spring容器
- 通過excludeFilters過濾出不用加入spring容器的類
- 自定義增加了@Component注解的注解方式