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

