1.背景
剛開始接觸 spring 著蟹,一定不會(huì)少看到 IoC煞肾、DI 這兩個(gè)單詞命贴。
先把 spring 撇開道宅,單獨(dú)看看 IoC 和 DI 是什么意思呢?
找兩個(gè)定義:
IoC胸蛛,全稱 Inverse of Control污茵,控制反轉(zhuǎn)。是指葬项,由容器/第三方系統(tǒng)(例如 spring )來控制業(yè)務(wù)對(duì)象之間的依賴關(guān)系泞当,而不是像傳統(tǒng)方式中由代碼來直接控制。
DI民珍,全稱 Dependency Injection零蓉,依賴注入。是指穷缤,自身對(duì)象中的內(nèi)置對(duì)象是通過注入的方式進(jìn)行創(chuàng)建。
呃箩兽,看完了津肛,似懂非懂。汗贫。身坐。
2.舉個(gè)“吃飯”的例子
場(chǎng)景:在沙縣小吃/學(xué)校食堂吃飯。
沙縣小吃:
- 顧客到店:老板落包,我要一份炒米線部蛇。
- 老板:好嘞,這就給你做咐蝇。
學(xué)校食堂:
- 學(xué)生到食堂:阿姨涯鲁,我要這個(gè)西紅柿炒蛋,加4兩米飯有序。
- 食堂阿姨:好嘞抹腿,這就給你裝盤。
區(qū)別在哪呢旭寿?
- “沙縣小吃”是由顧客到店后主動(dòng)要求“我要吃什么”警绩,然后餐館才去 new 一個(gè)你要吃的菜。
- “學(xué)校食堂”是由食堂提前 new 很多可以提供給顧客吃的菜盅称,顧客來了之后直接挑選裝盤就可以了肩祥。
3.再舉個(gè)例子
場(chǎng)景:接口參數(shù)檢查
假設(shè)后室,我們的后臺(tái)服務(wù)拆兩個(gè)類:MyService 和 Validator 。很明顯混狠,MyService 依賴 Validator岸霹。
考慮一下, 最簡(jiǎn)單的實(shí)現(xiàn)方式是什么呢檀蹋?
3.1 傳統(tǒng)實(shí)現(xiàn)松申,new
簡(jiǎn)單介紹一下用到的類和接口:
-
IValidate.java
,接口定義了 validate() 方法俯逾,對(duì)一個(gè) String 類型參數(shù)做檢查贸桶。 -
NotnullValidator.java
實(shí)現(xiàn)了 IValidate 接口的validate()方法,要求參數(shù)不能為空桌肴。 -
MyService.java
皇筛,定義了一個(gè)Serve() 方法,用來接收外部參數(shù)坠七,然后對(duì)參數(shù)做非空檢查水醋。 -
Client.java
,模擬調(diào)用方彪置,請(qǐng)求MyService拄踪。
代碼示例:
// IValidator.java
interface IValidator.java {
public void validate(String param);
}
// NotnullValidator.java
class NotnullValidator implements IValidator{
@Override
public void validate(String param){
System.out.println(this.getClass().getSimpleName()+ " validating param {"+param+"}");
if(null == param || 0 == param.length()){
System.out.println(this.getClass().getSimpleName() + " param can not be null or empty");
}
}
}
// MyService.java
class MyService.java {
void Serve(String param){
System.out.println(this.getClass().getSimpleName() + " param = " + param);
NotnullValidator notnullValidator = new NotnullValidator();
notnullValidator.validate(param);
}
}
// Client.java
public class Client {
public static void main(String[] args){
MyService myService = new MyService();
myService.Serve("");
myService.Serve("123");
/**
* 這樣做的弊端是,依賴隱藏在函數(shù)內(nèi)部拳魁,模塊化惶桐、可用性、擴(kuò)展性潘懊、易測(cè)性都不好姚糊。
*/
}
}
在這個(gè)例子中,MyService 直接在 Serve() 方法內(nèi)部 new 了一個(gè) NotNullValidator 實(shí)例授舟,進(jìn)行參數(shù)檢查救恨。
這樣做的問題在于:
- 擴(kuò)展性差,如果需要其他校驗(yàn)器释树,就要在 MyService 內(nèi)部做大調(diào)整了肠槽。
- 依賴管理困難,IValidator 實(shí)例的生命周期在 MyService 之內(nèi)奢啥,無法管理署浩。
- 可測(cè)性差,耦合太嚴(yán)重扫尺,依賴和行為完全堆到一起去了筋栋,沒法做單元測(cè)試。
那么正驻,有什么解決方法呢弊攘?
很簡(jiǎn)單抢腐,把 new NotNullValidator 這個(gè)操作拿出來就可以了。
3.2 構(gòu)造子注入
同樣的例子襟交,我們可以在 MyService 內(nèi)部定義一個(gè) IValidator 成員變量迈倍,然后再 定義一個(gè) MyService 的帶參構(gòu)造函數(shù)。
代碼示例:
// IValidator.java
interface IValidator {
public void validate(String param);
}
// NotnullValidator.java
class NotnullValidator implements IValidator{
@Override
public void validate(String param){
System.out.println(this.getClass().getSimpleName()+ " validating param {"+param+"}");
if(null == param || 0 == param.length()){
System.out.println(this.getClass().getSimpleName() + " param can not be null or empty");
}
}
}
// MyService.java
class MyService {
private IValidator validator;
MyService(IValidator validator) {
System.out.println("構(gòu)造子注入");
this.validator = validator;
}
void Serve(String param){
System.out.println(this.getClass().getSimpleName() + " param = " + param);
validator.validate(param);
}
}
// Client.java
public class Client {
public static void main(String[] args){
NotnullValidator notnullValidator = new NotnullValidator();
MyService myService = new MyService(notnullValidator);
myService.Serve("");
myService.Serve("123");
/**
* 依賴由外部管理捣域,可測(cè)性變強(qiáng)了啼染。
* 這其實(shí)就是一種依賴注入方法。
* 通過構(gòu)造方法注入依賴焕梅,叫做"構(gòu)造子注入"迹鹅。
*/
}
}
在這里,Client 自己 new 了一個(gè) NotNullValidator 實(shí)例贞言,通過構(gòu)造方法傳給新的 MyService 實(shí)例斜棚。
這樣,類與類之間的依賴關(guān)系由外部管理该窗,可測(cè)試性弟蚀、可復(fù)用性都得到了很大的提升。
從應(yīng)用程序的角度描述酗失,依賴關(guān)系不再是 MyService 正向獲得一個(gè) IValidator 實(shí)現(xiàn)的實(shí)例义钉,而是 Client 先實(shí)例化一個(gè)自己想要的 IValidator 實(shí)現(xiàn),然后注入到 MyService规肴,這就是“控制反轉(zhuǎn)”(IoC)捶闸。
從 Client 的角度描述,Client 負(fù)責(zé) IValidator 的實(shí)例化奏纪,并將其注入到 MyService,這就是“依賴注入”(DI)斩启。
IoC其實(shí)就是把主動(dòng)變被動(dòng)序调,并有效的分離了對(duì)象和它所需要的外部資源,使得它們松散耦合兔簇,有利于功能復(fù)用发绢,更重要的是使得程序的整個(gè)體系結(jié)構(gòu)變得非常靈活。
除了通過構(gòu)造器注入外垄琐,還有另外兩種依賴注入方法边酒。
3.3 設(shè)值注入
設(shè)值注入是利用類成員變量的 setter 方法進(jìn)行依賴注入的一種方式。
代碼示例:
// IValidator.java
interface IValidator {
public void validate(String param);
}
// NotnullValidator.java
class NotnullValidator implements IValidator{
@Override
public void validate(String param){
System.out.println(this.getClass().getSimpleName()+ " validating param {"+param+"}");
if(null == param || 0 == param.length()){
System.out.println(this.getClass().getSimpleName() + " param can not be null or empty");
}
}
}
// MyService.java
class MyService {
private IValidator validator;
void setValidator(IValidator validator) {
System.out.println("設(shè)值注入");
this.validator = validator;
}
void Serve(String param){
System.out.println(this.getClass().getSimpleName() + " param = " + param);
validator.validate(param);
}
}
// Client.java
public class Client {
public static void main(String[] args){
NotnullValidator notnullValidator = new NotnullValidator();
MyService myService = new MyService();
myService.setValidator(notnullValidator);
myService.Serve("");
myService.Serve("123");
/**
* 通過成員變量的setter方法注入依賴狸窘,叫做"設(shè)值注入"
*/
}
}
這里墩朦,為 MyService 的成員變量 validator 定義了setValidator() 方法,從而使得外部可以通過這個(gè) setValidator() 方法注入 IValidator 依賴翻擒。
3.4 接口注入
接口注入是指氓涣,通過實(shí)現(xiàn)特定接口的依賴注入方法實(shí)現(xiàn)依賴注入的一種方式牛哺。
代碼示例:
// IValidator.java
interface IValidator {
public void validate(String param);
}
// MyInject.java
interface MyInject {
public void inject(IValidator validator);
}
// NotnullValidator.java
class NotnullValidator implements IValidator{
@Override
public void validate(String param){
System.out.println(this.getClass().getSimpleName()+ " validating param {"+param+"}");
if(null == param || 0 == param.length()){
System.out.println(this.getClass().getSimpleName() + " param can not be null or empty");
}
}
}
// MyService.java
class MyService implements MyInject {
private IValidator validator;
void Serve(String param){
System.out.println(this.getClass().getSimpleName() + " param = " + param);
validator.validate(param);
}
@Override
public void inject(IValidator validator) {
System.out.println("接口注入");
this.validator = validator;
}
}
// Client.java
public class Client {
public static void main(String[] args){
NotnullValidator notnullValidator = new NotnullValidator();
MyService myService = new MyService();
myService.inject(notnullValidator);
myService.Serve("");
myService.Serve("123");
/**
* 通過實(shí)現(xiàn)一個(gè)單獨(dú)的依賴注入接口,實(shí)現(xiàn)依賴注入劳吠,叫做"接口注入"引润。
* 不常見的用法,構(gòu)造函數(shù)和setter就夠用了痒玩。
*
* 通過構(gòu)造函數(shù)淳附、setter、接口注入依賴蠢古,仍然存在問題奴曙。
* 加入一個(gè)類A依賴非常多的其他類,每一個(gè)都要手動(dòng)new便瑟,然后set缆毁,太麻煩了。
* 怎么辦呢到涂?上框架脊框。
*/
}
}
這里定義了一個(gè) MyInject
接口,接口內(nèi)部參數(shù) inject()
用于注入 IValidator 践啄。
MyService 實(shí)現(xiàn)了這個(gè)接口浇雹,Client 可以通過 inject() 注入IValidator 實(shí)例。
上面提到的三種注入方式:構(gòu)造子注入屿讽、設(shè)值注入昭灵、接口注入,實(shí)際上都是提供一個(gè)“入口”函數(shù)伐谈,允許外部調(diào)用方向內(nèi)注入 IValidator 依賴烂完,只是形式不同罷了。
但是诵棵,外部系統(tǒng)管理這些 “new” 操作的話抠蚣,如果接口內(nèi)有多層傳遞依賴,或者依賴項(xiàng)比較多的時(shí)候履澳,Client 負(fù)擔(dān)就太重了嘶窄。
怎么辦呢?上框架距贷。
IoC/DI 框架要做的事情柄冲,就是:new 什么,什么時(shí)候 new 忠蝗,new 完了要給誰现横。。。
IoC/DI 框架有很多长赞,這里主要介紹兩個(gè)晦攒,Spring 和 Guice。
4. IoC/DI 框架
4.1 spring
IoC是spring的核心得哆,對(duì)于spring框架來說脯颜,就是由 spring 來負(fù)責(zé)控制對(duì)象的生命周期和對(duì)象間的關(guān)系。
想要使用 spring 贩据,至少需要兩個(gè)核心包:group: 'org.springframework', name: 'spring-core'
和group: 'org.springframework', name: 'spring-context'
栋操。
spring 通過一個(gè)叫做“容器”的東西來管理所有的 bean 對(duì)象。
當(dāng) Spring 應(yīng)用程序被加載到內(nèi)存中時(shí)饱亮,spring 框架通過讀取 “配置元數(shù)據(jù)”矾芙,來完成對(duì)象的實(shí)例化、配置和組裝近上。應(yīng)用程序可以從容器中取bean剔宪,方法是getBean()
。
在這里壹无,接口和類定義同上一章葱绒。
只是,額外增加 xml 配置文件用于定義 bean 的生命周期及依賴關(guān)系斗锭。
PS:“配置元數(shù)據(jù)” 可以通過 XML地淀、Java注解、Java代碼來標(biāo)識(shí)岖是,代碼示例只給出xml配置帮毁。
代碼有點(diǎn)多,我分開說哈豺撑。
記得以下幾步:
- 引入上面說的兩個(gè)包烈疚。
- 寫 java 代碼 定義 bean。
- 寫 xml 配置文件聪轿。
- Client爷肝。
構(gòu)建工具用的是gradle,所以這么引包:
// build.gradle
group 'com.ann.javas.topics'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.springframework', name: 'spring-core', version: '4.3.11.RELEASE'
compile group: 'org.springframework', name: 'spring-context', version: '4.3.11.RELEASE'
}
本例中屹电,給出了5種xml配置方法:構(gòu)造子注入阶剑、設(shè)值注入跃巡、自動(dòng)裝載byName危号,自動(dòng)裝載byType,自動(dòng)裝載constructor素邪。
先列下java代碼外莲,bean 定義沒變,就是Client.java里面的內(nèi)容多了點(diǎn):
// IValidator.java
public interface IValidator {
public void validate(String param);
}
// NotnullValidator.java
public class NotnullValidator implements IValidator{
@Override
public void validate(String param) {
System.out.println(this.getClass().getSimpleName()+ " validating param {"+param+"}");
if(null == param || 0 == param.length()){
System.out.println(this.getClass().getSimpleName() + " param can not be null or empty");
}
}
}
// MyService.java
public class MyService {
private IValidator validator;
public MyService(IValidator validator) {
this.validator = validator;
}
public void setValidator(IValidator validator) {
this.validator = validator;
}
public MyService() {
}
void Serve(String param){
System.out.println(this.getClass().getSimpleName() + " param = " + param);
validator.validate(param);
}
}
// Client.java
public class Client {
/**
* 構(gòu)造子注入 和 設(shè)值注入,唯一的區(qū)別就是標(biāo)簽中定義依賴的元素不同偷线。
* 構(gòu)造子注入使用:constructor-arg磨确,設(shè)值注入使用:property。
*
* @param args
*/
public static void main(String[] args) {
System.out.println("case1:測(cè)試spring容器構(gòu)造子注入(基于XML配置)...");
demo1();
System.out.println("..................................................");
System.out.println("case2:測(cè)試spring容器設(shè)值注入(基于XML配置)...");
demo2();
System.out.println("..................................................");
System.out.println("case3:測(cè)試spring容器自動(dòng)裝載byName(基于XML配置)...");
demo3();
System.out.println("..................................................");
System.out.println("case4:測(cè)試spring容器自動(dòng)裝載byType(基于XML配置)...");
demo4();
System.out.println("..................................................");
System.out.println("case5:測(cè)試spring容器自動(dòng)裝載constructor(基于XML配置)...");
demo5();
System.out.println("..................................................");
}
private static void demo1(){
// 讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("demo5_configioc-byxml-case1.xml");
// 構(gòu)造子注入實(shí)例
MyService service = (MyService)context.getBean("s");
service.Serve("");
service.Serve("123");
}
private static void demo2(){
// 讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("demo5_configioc-byxml-case2.xml");
// 設(shè)值注入實(shí)例
MyService service = (MyService)context.getBean("s");
service.Serve("");
service.Serve("123");
}
private static void demo3(){
// 讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("demo5_configioc-byxml-case3.xml");
// 自動(dòng)裝載声邦,byName
MyService service = (MyService)context.getBean("s");
service.Serve("");
service.Serve("123");
}
private static void demo4(){
// 讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("demo5_configioc-byxml-case4.xml");
// 自動(dòng)裝載乏奥,byType
MyService service = (MyService)context.getBean("s");
service.Serve("");
service.Serve("123");
}
private static void demo5(){
// 讀取配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("demo5_configioc-byxml-case5.xml");
// 自動(dòng)裝載,constructor
MyService service = (MyService)context.getBean("s");
service.Serve("");
service.Serve("123");
}
}
然后是xml配置文件亥曹,有5個(gè):
// demo5_configioc-byxml-case1.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置兩個(gè) bean邓了,并指明他們的依賴關(guān)系: MyService 依賴 NotnullValidator -->
<!-- 被依賴的 NotnullValidator 會(huì)在 MyService 構(gòu)造之前構(gòu)造 -->
<!-- 用 constructor-arg 配置,就是構(gòu)造子注入 -->
<bean id="s" class="com.ann.javas.topics.iocdi.demo5.usespring.MyService">
<constructor-arg ref="p"/>
</bean>
<bean id="p" class="com.ann.javas.topics.iocdi.demo5.usespring.NotnullValidator">
</bean>
</beans>
// demo5_configioc-byxml-case2.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置兩個(gè) bean媳瞪,并指明他們的依賴關(guān)系: MyService 依賴 NotnullValidator -->
<!-- 被依賴的 NotnullValidator 會(huì)在 MyService 構(gòu)造之前構(gòu)造 -->
<!-- 用 property 配置骗炉,就是設(shè)值注入 -->
<bean id="s" class="com.ann.javas.topics.iocdi.demo5.usespring.MyService">
<property name="validator" ref="p"/>
</bean>
<bean id="p" class="com.ann.javas.topics.iocdi.demo5.usespring.NotnullValidator">
</bean>
</beans>
// demo5_configioc-byxml-case3.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置兩個(gè) bean,并指明他們的依賴關(guān)系: MyService 依賴 NotnullValidator -->
<!-- 被依賴的 NotnullValidator 會(huì)在 MyService 構(gòu)造之前構(gòu)造 -->
<!-- 自動(dòng)裝載 byName蛇受,根據(jù) id 或 name 查找 bean 并自動(dòng)裝配 -->
<bean id="s" class="com.ann.javas.topics.iocdi.demo5.usespring.MyService" autowire="byName">
</bean>
<!-- 這里的 id 必須和 MyService 中定義的IValidator 變量名一致句葵,否則 byName 無法識(shí)別 -->
<bean id="validator" class="com.ann.javas.topics.iocdi.demo5.usespring.NotnullValidator">
</bean>
</beans>
// demo5_configioc-byxml-case4.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置兩個(gè) bean,并指明他們的依賴關(guān)系: MyService 依賴 NotnullValidator -->
<!-- 被依賴的 NotnullValidator 會(huì)在 MyService 構(gòu)造之前構(gòu)造 -->
<!-- 自動(dòng)裝載byType兢仰,根據(jù) class 查找并自動(dòng)裝配 bean -->
<bean id="s" class="com.ann.javas.topics.iocdi.demo5.usespring.MyService" autowire="byType">
</bean>
<bean id="b" class="com.ann.javas.topics.iocdi.demo5.usespring.NotnullValidator">
</bean>
</beans>
// demo5_configioc-byxml-case5.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置兩個(gè) bean乍丈,并指明他們的依賴關(guān)系: MyService 依賴 NotnullValidator -->
<!-- 被依賴的 NotnullValidator 會(huì)在 MyService 構(gòu)造之前構(gòu)造 -->
<!-- 自動(dòng)裝載 constructor,根據(jù) bean 的構(gòu)造函數(shù)定義進(jìn)行查找并自動(dòng)裝配 -->
<bean id="s" class="com.ann.javas.topics.iocdi.demo5.usespring.MyService" autowire="constructor">
</bean>
<bean id="b" class="com.ann.javas.topics.iocdi.demo5.usespring.NotnullValidator">
</bean>
</beans>
代碼中的注釋有簡(jiǎn)單介紹旨别,詳細(xì)的內(nèi)容就不再贅述~
4.2 guice
guice 是谷歌開發(fā)的輕量級(jí) DI 框架诗赌,號(hào)稱比 spring 快10倍,雖然我沒測(cè)過秸弛,但確實(shí)蠻好用的就是了铭若。
想要使用guice,需要引包:group: 'com.google.inject', name: 'guice'
guice 允許你使用 @Inject 進(jìn)行注解構(gòu)造函數(shù)的方式递览,來進(jìn)行自動(dòng)實(shí)例化構(gòu)造參數(shù)叼屠。也可以通過實(shí)現(xiàn) Module 的 configure(Binder binder) 方法定義依賴注入規(guī)則。
在程序啟動(dòng)時(shí)绞铃,你可以使用 Guice.createInjector(自定義Module)
來生成一個(gè)injector镜雨,之后就可以從 injector 中獲取你需要 guice 為你實(shí)例化的類,方法是 injector.getInstance(你想要的類.class)
儿捧。
用guice非常簡(jiǎn)單:
- 引包荚坞。
- java代碼。
同樣gradle構(gòu)建:
// build.gradle
group 'com.ann.javas.topics'
version '1.0-SNAPSHOT'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile group: 'com.google.inject', name: 'guice', version: '4.0'
}
java代碼:
// IValidator.java
interface IValidator {
public void validate(String param);
}
// NotnullValidator.java
class NotnullValidator implements IValidator {
@Override
public void validate(String param){
System.out.println(this.getClass().getSimpleName()+ " validating param {"+param+"}");
if(null == param || 0 == param.length()){
System.out.println(this.getClass().getSimpleName() + " param can not be null or empty");
}
}
}
// MyService.java
class MyService {
@Inject
private IValidator validator;
void Serve(String param){
System.out.println(this.getClass().getSimpleName() + " param = " + param);
validator.validate(param);
}
}
// MyModule.java
public class MyModule implements Module{
@Override
public void configure(Binder binder) {
System.out.println("guice注入規(guī)則菲盾,綁定 IValidator 到 NotnullValidator");
binder.bind(IValidator.class).to(NotnullValidator.class);
}
}
// Client.java
public class Client {
public static void main(String[] args){
// 定義依賴注入規(guī)則
MyModule module = new MyModule();
// 根據(jù)注入規(guī)則颓影,生成 injector
Injector injector = Guice.createInjector(module);
// 獲取 MyService 實(shí)例
MyService myService = injector.getInstance(MyService.class);
// 來兩個(gè)case
myService.Serve("");
myService.Serve("123");
}
}
如你所見,MyModule中定義了依賴注入規(guī)則懒鉴,將 IValidator 綁定到 NotnullValidator诡挂。這樣碎浇,當(dāng)某個(gè)類需要一個(gè)IValidator的時(shí)候,guice就會(huì)給它一個(gè)NotNullValidator實(shí)例璃俗。
其他的請(qǐng)看注釋說明~