基于@Bean修飾的方法參數(shù)的注入方式
@Bean修飾的方法參數(shù)的注入
方法參數(shù)默認注入方式為Autowired,即先根據(jù)類型匹配,若有多個在根據(jù)名稱進行匹配。
1:復雜類型可以通過@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:復雜類型可以通過@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標簽,如下所示:
<!-- 定義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標簽為構(gòu)造器傳入?yún)?shù)值,但是每個constructor-arg標簽對應(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ù)下標
上面是通過構(gòu)造器參數(shù)的名稱,匹配需要傳入的值,那種方式最為直觀,而Spring還提供另外兩種方式匹配參數(shù),這里就來說說通過參數(shù)在參數(shù)列表中的下標進行匹配的方式。下面的配置,請結(jié)合User和Car的構(gòu)造方法一起閱讀,配置方式如下:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 下標編號從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ù)的下標為構(gòu)造器的參數(shù)賦值,需要注意的是,參實的下標從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è)當前car的配置是這樣的:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 兩個下標的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)造器,這和上面通過下標匹配是一致的。需要說明的一點是,這三種使用構(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-07Netty分布式ByteBuf使用的底層實現(xiàn)方式源碼解析
這篇文章主要為大家介紹了Netty分布式ByteBuf使用底層實現(xiàn)方式源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03JavaMail整合Spring實現(xiàn)郵件發(fā)送功能
這篇文章主要為大家詳細介紹了JavaMail整合Spring實現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08java使用TimerTask定時器獲取指定網(wǎng)絡(luò)數(shù)據(jù)
java.util.Timer定時器,實際上是個線程,定時調(diào)度所擁有的TimerTasks。一個TimerTask實際上就是一個擁有run方法的類,需要定時執(zhí)行的代碼放到run方法體內(nèi),TimerTask一般是以匿名類的方式創(chuàng)建,下面的就用示例來學習他的使用方法2014-01-01關(guān)于java關(guān)鍵字this和super的區(qū)別和理解
這篇文章主要給大家介紹了關(guān)于java關(guān)鍵字this和super的區(qū)別和理解的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01使用Java如何對復雜的數(shù)據(jù)類型排序和比大小
我相信大家在第一次接觸算法的時候,最先接觸的肯定也是從排序算法開始的,下面這篇文章主要給大家介紹了關(guān)于使用Java如何對復雜的數(shù)據(jù)類型排序和比大小的相關(guān)資料,需要的朋友可以參考下2023-12-12