基于@Bean修飾的方法參數(shù)的注入方式
@Bean修飾的方法參數(shù)的注入
方法參數(shù)默認(rèn)注入方式為Autowired,即先根據(jù)類(lèi)型匹配,若有多個(gè)在根據(jù)名稱(chēng)進(jìn)行匹配。
1:復(fù)雜類(lèi)型可以通過(guò)@Qualifier(value=“XXX”)限定
2:對(duì)于普通類(lèi)型使用@Value(XXX)指定
@PropertySource("classpath:db.properties")
public class SpringConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* 方法參數(shù)默認(rèn)注入方式為Autowired: <br>
* 1:復(fù)雜類(lèi)型可以通過(guò)@Qualifier(value="dataSource")限定; <br>
* 2:對(duì)于普通類(lèi)型使用@Value指定; <br>
*/
@Bean(name = "dataSource")
public DataSource dataSource(@Value("${jdbc.driverClass}") String driverClassName,
@Value("${jdbc.jdbcUrl}") String url, @Value("${jdbc.user}") String username,
@Value("${jdbc.password}") String password) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate(@Qualifier(value = "dataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
Bean的四種注入方式
我使用下面兩個(gè)類(lèi)來(lái)進(jìn)行注入的演示,這兩個(gè)類(lèi)分別是User和Car類(lèi):
Car類(lèi):
public class Car {
// 只包含基本數(shù)據(jù)類(lèi)型的屬性
private int speed;
private double price;
public Car() {
}
public Car(int speed, double price) {
this.speed = speed;
this.price = price;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"speed=" + speed +
", price=" + price +
'}';
}
}
User類(lèi):
public class User {
private String name;
private int age;
// 除了上面兩個(gè)基本數(shù)據(jù)類(lèi)型的屬性,User還依賴(lài)Car
private Car car;
public User() {
}
public User(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", car=" + car +
'}';
}
}
1、set注入
有了上面兩個(gè)類(lèi),就可以演示set注入了。需要注意一點(diǎn),如果需要使用set注入,那么必須要為屬性提供set方法,Spring容器就是通過(guò)調(diào)用bean的set方法為屬性注入值的。而在xml文件中,使用set注入的方式就是通過(guò)property標(biāo)簽,如下所示:
<!-- 定義car這個(gè)bean,id為myCar -->
<bean id="myCar" class="cn.tewuyiang.pojo.Car">
<!--
為car的屬性注入值,因?yàn)閟peed和price都是基本數(shù)據(jù)類(lèi)型,所以使用value為屬性設(shè)置值;
注意,這里的name為speed和price,不是因?yàn)閷傩悦褪莝peed和price,
而是set方法分別為setSpeed和setPrice,名稱(chēng)是通過(guò)將set刪除,然后將第一個(gè)字母變小寫(xiě)得出;
-->
<property name="speed" value="100"/>
<property name="price" value="99999.9"/>
</bean>
<!-- 定義user這個(gè)bean -->
<bean id="user" class="cn.tewuyiang.pojo.User">
<property name="name" value="aaa" />
<property name="age" value="123" />
<!-- car是引用類(lèi)型,所以這里使用ref為其注入值,注入的就是上面定義的myCar
基本數(shù)據(jù)類(lèi)型或Java包裝類(lèi)型使用value,
而引用類(lèi)型使用ref,引用另外一個(gè)bean的id
-->
<property name="car" ref="myCar" />
</bean>
通過(guò)上面的配置,就可以為Car和User這兩個(gè)類(lèi)型的bean注入值了。需要注意的是,property的name屬性,填寫(xiě)的不是屬性的名稱(chēng),而是set方法去除set,然后將第一個(gè)字符小寫(xiě)后的結(jié)果。對(duì)于基本數(shù)據(jù)類(lèi)型,或者是Java的包裝類(lèi)型(比如String),使用value注入值,而對(duì)于引用類(lèi)型,則使用ref,傳入其他bean的id。接下來(lái)就可以測(cè)試效果了:
@Test
public void test1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 獲取user這個(gè)bean
User user = context.getBean(User.class);
// 輸出產(chǎn)看結(jié)果
System.out.println(user);
}
由于user包含car的引用,所以直接輸出user,也能夠看到car的情況,輸入結(jié)果如下:
User{name='aaa', age=123, car=Car{speed=100, price=99999.9}}
2、構(gòu)造器注入
下面來(lái)說(shuō)第二種方式——構(gòu)造器注入。聽(tīng)名字就可以知道,這種注入值的方式,就是通過(guò)調(diào)用bean所屬類(lèi)的帶參構(gòu)造器為bean的屬性注入值。這也就意味著,如果需要使用構(gòu)造器注入,就得為類(lèi)提供包含參數(shù)的構(gòu)造方法。構(gòu)造器注入,實(shí)際上有多種匹配屬性值的方式,下面就來(lái)一一列舉。這里依然使用定義的Car和User這兩個(gè)類(lèi),測(cè)試方法以及類(lèi)的定義都不需要變,需要改變的僅僅是xml配置文件。
(一)匹配構(gòu)造器的參數(shù)名稱(chēng)
需要通過(guò)constructor-arg標(biāo)簽為構(gòu)造器傳入?yún)?shù)值,但是每個(gè)constructor-arg標(biāo)簽對(duì)應(yīng)哪一個(gè)參數(shù)值呢?這就有多種方式指定了。第一種就是直接匹配參數(shù)名,配置如下:
<bean id="myCar" class="cn.tewuyiang.pojo.Car">
<!-- 通過(guò)constructor-arg的name屬性,指定構(gòu)造器參數(shù)的名稱(chēng),為參數(shù)賦值 -->
<constructor-arg name="speed" value="100" />
<constructor-arg name="price" value="99999.9"/>
</bean>
<bean id="user" class="cn.tewuyiang.pojo.User">
<constructor-arg name="name" value="aaa" />
<constructor-arg name="age" value="123" />
<!--
和之前一樣,基本數(shù)據(jù)類(lèi)型或Java包裝類(lèi)型使用value,
而引用類(lèi)型使用ref,引用另外一個(gè)bean的id
-->
<constructor-arg name="car" ref="myCar" />
</bean>
這樣就完成了,測(cè)試代碼和之前一樣,運(yùn)行結(jié)果也一樣,這里的配置和set注入時(shí)的配置幾乎一樣,除了一個(gè)使用property,一個(gè)使用constructor-arg。寫(xiě)法上一樣,但是表示的含義卻完全不同。property的name屬性,是通過(guò)set方法的名稱(chēng)得來(lái);而constructor-arg的name,則是構(gòu)造器參數(shù)的名稱(chēng)。
(二)匹配構(gòu)造器的參數(shù)下標(biāo)
上面是通過(guò)構(gòu)造器參數(shù)的名稱(chēng),匹配需要傳入的值,那種方式最為直觀,而Spring還提供另外兩種方式匹配參數(shù),這里就來(lái)說(shuō)說(shuō)通過(guò)參數(shù)在參數(shù)列表中的下標(biāo)進(jìn)行匹配的方式。下面的配置,請(qǐng)結(jié)合User和Car的構(gòu)造方法一起閱讀,配置方式如下:
<bean id="car" class="cn.tewuyiang.pojo.Car">
<!-- 下標(biāo)編號(hào)從0開(kāi)始,構(gòu)造器的第一個(gè)參數(shù)是speed,為它賦值100 -->
<constructor-arg index="0" value="100" />
<!-- 構(gòu)造器的第二個(gè)參數(shù)是price,為它賦值99999.9 -->
<constructor-arg index="1" value="99999.9"/>
</bean>
<bean id="user" class="cn.tewuyiang.pojo.User">
<!-- 與上面car的配置同理 -->
<constructor-arg index="0" value="aaa" />
<constructor-arg index="1" value="123" />
<constructor-arg index="2" ref="car" />
</bean>
上面就是通過(guò)參數(shù)的下標(biāo)為構(gòu)造器的參數(shù)賦值,需要注意的是,參實(shí)的下標(biāo)從0開(kāi)始。使用上面的方式配置,若賦值的類(lèi)型與參數(shù)的類(lèi)型不一致,將會(huì)在容器初始化bean的時(shí)候拋出異常。如果bean存在多個(gè)參數(shù)數(shù)量一樣的構(gòu)造器,Spring容器會(huì)自動(dòng)找到類(lèi)型匹配的那個(gè)進(jìn)行調(diào)用。比如說(shuō),Car有如下兩個(gè)構(gòu)造器,Spring容器將會(huì)調(diào)用第二個(gè),因?yàn)樯厦娴呐渲弥?,index = 1對(duì)應(yīng)的value是double類(lèi)型,與第二個(gè)構(gòu)造器匹配,而第一個(gè)不匹配:
public Car(double price, int speed) {
this.speed = speed;
this.price = price;
}
// 將使用匹配這個(gè)構(gòu)造器
public Car(int speed, double price) {
this.speed = speed;
this.price = price;
}
還存在另外一種特殊情況,那就是多個(gè)構(gòu)造器都滿足bean的配置,此時(shí)選擇哪一個(gè)?假設(shè)當(dāng)前car的配置是這樣的:
<bean id="car" class="cn.tewuyiang.pojo.Car">
<!-- 兩個(gè)下標(biāo)的value值都是整數(shù) -->
<constructor-arg index="0" value="100" />
<constructor-arg index="1" value="999"/>
</bean>
假設(shè)Car還是有上面兩個(gè)構(gòu)造器,兩個(gè)構(gòu)造器都是一個(gè)int類(lèi)型一個(gè)double類(lèi)型的參數(shù),只是位置不同。而配置中,指定的兩個(gè)值都是int類(lèi)型。但是,int類(lèi)型也可以使用double類(lèi)型存儲(chǔ),所以上面兩個(gè)構(gòu)造器都是匹配的,此時(shí)調(diào)用哪一個(gè)呢?結(jié)論就是調(diào)用第二個(gè)。自己去嘗試就會(huì)發(fā)現(xiàn),若存在多個(gè)構(gòu)造器匹配bean的定義,Spring容器總是使用最后一個(gè)滿足條件的構(gòu)造器。
(三)匹配構(gòu)造器的參數(shù)類(lèi)型
下面說(shuō)最后一種匹配方式——匹配構(gòu)造器的參數(shù)類(lèi)型。直接看配置文件吧:
<bean id="car" class="cn.tewuyiang.pojo.Car">
<!-- 使用type屬性匹配類(lèi)型,car的構(gòu)造器包含兩個(gè)參數(shù),一個(gè)是int類(lèi)型,一個(gè)是double類(lèi)型 -->
<constructor-arg type="int" value="100" />
<constructor-arg type="double" value="99999.9"/>
</bean>
<bean id="user" class="cn.tewuyiang.pojo.User">
<!-- 對(duì)于引用類(lèi)型,需要使用限定類(lèi)名 -->
<constructor-arg type="java.lang.String" value="aaa" />
<constructor-arg type="int" value="123" />
<constructor-arg type="cn.tewuyiang.pojo.Car" ref="car" />
</bean>
上面應(yīng)該不難理解,直接通過(guò)匹配構(gòu)造器的參數(shù)類(lèi)型,從而選擇一個(gè)能夠完全匹配的構(gòu)造器,調(diào)用這個(gè)構(gòu)造器完成bean的創(chuàng)建和屬性注入。需要注意的是,上面的配置中,類(lèi)型并不需要按構(gòu)造器中聲明的順序編寫(xiě),Spring也能進(jìn)行匹配。這也就意味著可能出現(xiàn)多個(gè)能夠匹配的構(gòu)造器,和上一個(gè)例子中一樣。比如說(shuō),Car還是有下面兩個(gè)構(gòu)造器:
public Car(double price, int speed) {
// 輸出一句話,看是否調(diào)用這個(gè)構(gòu)造器
System.out.println(111);
this.speed = speed;
this.price = price;
}
// 將使用匹配這個(gè)構(gòu)造器
public Car(int speed, double price) {
// 輸出一句話,看是否調(diào)用這個(gè)構(gòu)造器
System.out.println(222);
this.speed = speed;
this.price = price;
}
上面兩個(gè)構(gòu)造器都是一個(gè)int,一個(gè)double類(lèi)型的參數(shù),都符合x(chóng)ml文件中,car這個(gè)bean的配置。通過(guò)測(cè)試發(fā)現(xiàn),Spring容器使用的永遠(yuǎn)都是最后一個(gè)符合條件的構(gòu)造器,這和上面通過(guò)下標(biāo)匹配是一致的。需要說(shuō)明的一點(diǎn)是,這三種使用構(gòu)造器注入的方式,可以混用。
3、靜態(tài)工廠注入
靜態(tài)工廠注入就是編寫(xiě)一個(gè)靜態(tài)的工廠方法,這個(gè)工廠方法會(huì)返回一個(gè)需要的值,然后在配置文件中,指定使用這個(gè)工廠方法創(chuàng)建bean。首先需要一個(gè)靜態(tài)工廠,如下所示:
public class SimpleFactory {
/**
* 靜態(tài)工廠,返回一個(gè)Car的實(shí)例對(duì)象
*/
public static Car getCar() {
return new Car(12345, 5.4321);
}
}
下面需要在xml中配置car這個(gè)bean,并指定它由工廠方法進(jìn)行創(chuàng)建。配置如下:
<!--
注意,這里的配置并不是創(chuàng)建一個(gè)SimpleFactory對(duì)象,取名為myCar,
這一句配置的意思是,調(diào)用SimpleFactory的getCar方法,創(chuàng)建一個(gè)car實(shí)例對(duì)象,
將這個(gè)car對(duì)象取名為myCar。
-->
<bean id="car" class="cn.tewuyiang.factory.SimpleFactory" factory-method="getCar"/>
<bean id="user" class="cn.tewuyiang.pojo.User">
<!-- name和age使用set注入 -->
<property name="name" value="aaa"/>
<property name="age" value="123"/>
<!-- 將上面配置的car,注入到user的car屬性中 -->
<property name="car" ref="car"/>
</bean>
以上就配置成功了,測(cè)試方法以及執(zhí)行效果如下,注意看car的屬性值,就是在靜態(tài)工廠中配置的那樣,這說(shuō)明,Spring容器確實(shí)是使用定義的靜態(tài)工廠方法,創(chuàng)建了car這個(gè)bean:
@Test
public void test1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 獲取靜態(tài)工廠創(chuàng)建的car
Car car = (Car) context.getBean("car");
// 獲取user
User user = context.getBean(User.class);
System.out.println(car);
System.out.println(user);
}
輸出如下所示:
Car{speed=12345, price=5.4321}
User{name='aaa', age=123, car=Car{speed=12345, price=5.4321}}
4、實(shí)例工廠注入
實(shí)例工廠與靜態(tài)工廠類(lèi)似,不同的是,靜態(tài)工廠調(diào)用工廠方法不需要先創(chuàng)建工廠類(lèi)的對(duì)象,因?yàn)殪o態(tài)方法可以直接通過(guò)類(lèi)調(diào)用,所以在上面的配置文件中,并沒(méi)有聲明工廠類(lèi)的bean。但是,實(shí)例工廠,需要有一個(gè)實(shí)例對(duì)象,才能調(diào)用它的工廠方法。先看看實(shí)例工廠的定義:
public class SimpleFactory {
/**
* 實(shí)例工廠方法,返回一個(gè)Car的實(shí)例對(duì)象
*/
public Car getCar() {
return new Car(12345, 5.4321);
}
/**
* 實(shí)例工廠方法,返回一個(gè)String
*/
public String getName() {
return "tewuyiang";
}
/**
* 實(shí)例工廠方法,返回一個(gè)int,在Spring容器中會(huì)被包裝成Integer
*/
public int getAge() {
return 128;
}
}
在上面的工廠類(lèi)中,共定義了三個(gè)工廠方法,分別用來(lái)返回user所需的car,name以及age,而配置文件如下:
<!-- 聲明實(shí)例工廠bean,Spring容器需要先創(chuàng)建一個(gè)SimpleFactory對(duì)象,才能調(diào)用工廠方法 -->
<bean id="factory" class="cn.tewuyiang.factory.SimpleFactory" />
<!--
通過(guò)實(shí)例工廠的工廠方法,創(chuàng)建三個(gè)bean,通過(guò)factory-bean指定工廠對(duì)象,
通過(guò)factory-method指定需要調(diào)用的工廠方法
-->
<bean id="name" factory-bean="factory" factory-method="getName" />
<bean id="age" factory-bean="factory" factory-method="getAge" />
<bean id="car" factory-bean="factory" factory-method="getCar" />
<bean id="user" class="cn.tewuyiang.pojo.User">
<!-- 將上面通過(guò)實(shí)例工廠方法創(chuàng)建的bean,注入到user中 -->
<property name="name" ref="name"/>
<property name="age" ref="age"/>
<property name="car" ref="car"/>
</bean>
嘗試從Spring容器中取出name,age,car以及user,看看它們的值,測(cè)試代碼如下:
@Test
public void test1() {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 獲取靜態(tài)工廠創(chuàng)建的car,name和age這三個(gè)bean
Car car = (Car) context.getBean("car");
String name = (String) context.getBean("name");
Integer age = (Integer) context.getBean("age");
// 獲取user這個(gè)bean
User user = context.getBean(User.class);
System.out.println(car);
System.out.println(name);
System.out.println(age);
System.out.println(user);
}
以下就是輸出結(jié)果,可以看到,通過(guò)工廠創(chuàng)建的bean,都在Spring容器中能夠獲取到:
Car{speed=12345, price=5.4321}
tewuyiang
128
User{name='tewuyiang', age=128, car=Car{speed=12345, price=5.4321}}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot讀取自定義配置文件方式(properties,yaml)
這篇文章主要介紹了SpringBoot讀取自定義配置文件方式(properties,yaml),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Netty分布式ByteBuf使用的底層實(shí)現(xiàn)方式源碼解析
這篇文章主要為大家介紹了Netty分布式ByteBuf使用底層實(shí)現(xiàn)方式源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
JavaMail整合Spring實(shí)現(xiàn)郵件發(fā)送功能
這篇文章主要為大家詳細(xì)介紹了JavaMail整合Spring實(shí)現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
java使用TimerTask定時(shí)器獲取指定網(wǎng)絡(luò)數(shù)據(jù)
java.util.Timer定時(shí)器,實(shí)際上是個(gè)線程,定時(shí)調(diào)度所擁有的TimerTasks。一個(gè)TimerTask實(shí)際上就是一個(gè)擁有run方法的類(lèi),需要定時(shí)執(zhí)行的代碼放到run方法體內(nèi),TimerTask一般是以匿名類(lèi)的方式創(chuàng)建,下面的就用示例來(lái)學(xué)習(xí)他的使用方法2014-01-01
一文帶你了解Spring中@Enable開(kāi)頭注解的使用
前面的文章給大家介紹?Spring?的重試機(jī)制的時(shí)候有提到過(guò)?Spring?有很多?@Enable?開(kāi)頭的注解,平時(shí)在使用的時(shí)候也沒(méi)有注意過(guò)為什么會(huì)有這些注解,今天就給大家介紹一下2022-09-09
關(guān)于java關(guān)鍵字this和super的區(qū)別和理解
這篇文章主要給大家介紹了關(guān)于java關(guān)鍵字this和super的區(qū)別和理解的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
使用Java如何對(duì)復(fù)雜的數(shù)據(jù)類(lèi)型排序和比大小
我相信大家在第一次接觸算法的時(shí)候,最先接觸的肯定也是從排序算法開(kāi)始的,下面這篇文章主要給大家介紹了關(guān)于使用Java如何對(duì)復(fù)雜的數(shù)據(jù)類(lèi)型排序和比大小的相關(guān)資料,需要的朋友可以參考下2023-12-12
Java 設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解建造者模式
建造者模式,是一種對(duì)象構(gòu)建模式 它可以將復(fù)雜對(duì)象的建造過(guò)程抽象出來(lái),使這個(gè)抽象過(guò)程的不同實(shí)現(xiàn)方法可以構(gòu)造出不同表現(xiàn)的對(duì)象。本文將通過(guò)示例講解建造者模式,需要的可以參考一下2022-04-04

