Spring創建bean實例的幾種方式分享
前言
Spring常見的創建bean實例的方式有:
1.通過bean的class屬性創建實例
- 無參構造器
- 帶參構造器
2.工廠方法
- 靜態工廠方法
- 實例工廠方法
3.工廠bean
關於每種方式的Spring配置,詳見文末總結。
環境
- Ubuntu 22.04
- IntelliJ IDEA 2022.1.3
- JDK 17.0.3
- Spring 5.3.21 準備
創建Maven項目 test0705
。
修改 pom.xml 文件,添加依賴:
...... <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.21</version> </dependency> ......
在 src/main/resources
目錄下創建 applicationContext.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"> </beans>
在 src/test/java
目錄下創建測試:
public class Test0705 { }
通過bean的class屬性創建實例(無參構造器)
註:這是最常見的創建bean的方式。我其它文檔也用過相似代碼。如果已熟悉可直接略過。
創建如下POJO:
Axe
:Axe接口;StoneAxe
:Axe實現類;SteelAxe
:Axe實現類;Person
:Person持有Axe;
package pojo; public interface Axe { public void chop(); }
package pojo; public class StoneAxe implements Axe{ public StoneAxe() { System.out.println("StoneAxe constructor"); } @Override public void chop() { System.out.println("Stone axe!"); } }
package pojo; public class SteelAxe implements Axe{ public SteelAxe() { System.out.println("SteelAxe constructor"); } @Override public void chop() { System.out.println("Steel axe!"); } }
package pojo; public class Person { private String name; private Axe axe; public void setAxe(Axe axe) { this.axe = axe; } public void setName(String name) { this.name = name; } public Person() { System.out.println("Person constructor"); } public void useAxe() { System.out.println("I am " + name); axe.chop(); } }
在 applicationContext.xml
中註冊bean:
...... <bean id="stoneAxe" class="pojo.StoneAxe"/> <bean id="steelAxe" class="pojo.SteelAxe"/> <bean id="person" class="pojo.Person"> <property name="name" value="Tom"/> <property name="axe" ref="stoneAxe"/> </bean> ......
創建測試用例:
@Test public void test1() { var ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("before getBean person"); var person = ctx.getBean("person", Person.class); person.useAxe(); }
運行測試,如下:
StoneAxe constructor SteelAxe constructor Person constructor before getBean person I am Tom Stone axe!
通過bean的class屬性創建實例(帶參構造器)
註:這是較為常見的創建bean的方式。我其它文檔也用過相似代碼。如果已熟悉可直接略過。
創建如下POJO:
Book
:Book接口;PlayBook
:Book實現類;StudyBook
:Book實現類;Student
:Student持有Book;
package pojo; public interface Book { public void show(); }
package pojo; public class PlayBook implements Book{ public PlayBook() { System.out.println("PlayBook constructor"); } @Override public void show() { System.out.println("Play book!"); } }
package pojo; public class StudyBook implements Book{ public StudyBook() { System.out.println("StudyBook constructor"); } @Override public void show() { System.out.println("Study book!"); } }
package pojo; public class Student { private String name; private Book book; public Student(String name, Book book) { System.out.println("Student constructor"); this.name = name; this.book = book; } public void readBook() { System.out.println("I am " + name); book.show(); } }
在 applicationContext.xml
中註冊bean:
...... <bean id="playBook" class="pojo.PlayBook"/> <bean id="studyBook" class="pojo.StudyBook"/> <bean id="student" class="pojo.Student"> <constructor-arg index="0" value="Jerry"/> <constructor-arg index="1" ref="playBook"/> </bean> ......
創建測試用例:
@Test public void test2() { var ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("before getBean student"); var student = ctx.getBean("student", Student.class); student.readBook(); }
運行測試,如下:
……
PlayBook constructor
StudyBook constructor
Student constructor
before getBean student
I am Jerry
Play book!
工廠方法(靜態工廠方法)
配置:
class
屬性指向靜態工廠類factory-method
屬性指向靜態工廠方法
註:如果靜態工廠方法需要參數,則通過 constructor-arg
來指定。
例如:
<bean id="dog" class="factory.AnimalFactory" factory-method="getAnimal"> <constructor-arg value="dog"/> <property name="name" value="Snoopy"/> </bean>
當調用 ctx.getBean("dog")
時,Spring會調用 AnimalFactory
類的靜態方法 getAnimal()
,並傳入參數 "dog"
,創建一個Animal的實例並返回。
註:同普通的bean一樣,其默認scope是 singleton
,多次調用 ctx.getBean("dog")
返回的是同一個實例(在Spring初始化時生產bean)。
完整例子如下:
創建如下POJO:
Animal
:Animal接口;Dog
:Animal實現類;Cat
:Animal實現類;
package pojo; public interface Animal { public void cry(); }
package pojo; public class Dog implements Animal{ private String name; public void setName(String name) { this.name = name; } public Dog() { System.out.println("Dog constructor"); } @Override public void cry() { System.out.println("I am " + name); System.out.println("Wang wang..."); } }
package pojo; public class Cat implements Animal{ private String name; public void setName(String name) { this.name = name; } public Cat() { System.out.println("Cat constructor"); } @Override public void cry() { System.out.println("I am " + name); System.out.println("Miao Miao..."); } }
創建工廠類 AnimalFactory
:
package factory; import pojo.Animal; import pojo.Cat; import pojo.Dog; public class AnimalFactory { public static Animal getAnimal(String type) { System.out.println("creating new Animal object"); if (type.equalsIgnoreCase("dog")) { return new Dog(); } else if (type.equalsIgnoreCase("cat")) { return new Cat(); } else { return null; } } }
在 applicationContext.xml
中註冊bean:
<bean id="dog" class="factory.AnimalFactory" factory-method="getAnimal"> <constructor-arg value="dog"/> <property name="name" value="Snoopy"/> </bean> <bean id="cat" class="factory.AnimalFactory" factory-method="getAnimal"> <constructor-arg value="cat"/> <property name="name" value="Kitty"/> </bean>
創建測試用例:
@Test public void test3() { var ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("before getBean dog cat"); var animal1 = ctx.getBean("dog", Animal.class); animal1.cry(); var animal2 = ctx.getBean("cat", Animal.class); animal2.cry(); }
運行測試,如下:
creating new Animal object
Dog constructor
creating new Animal object
Cat constructor
before getBean dog cat
I am Snoopy
Wang wang…
I am Kitty
Miao Miao…
工廠方法(實例工廠方法)
實例工廠方法跟靜態工廠方法很相像,主要區別是:
- 靜態工廠方法:不用實例化工廠,隻需直接調用工廠類的靜態工廠方法;
- 實例工廠方法:工廠方法不是靜態的,因此需要通過
factory-bean
來指定工廠類的實例。
比如:
<bean id="fruitFactory" class="factory.FruitFactory"/> <bean id="apple" factory-bean="fruitFactory" factory-method="getFruit"> <constructor-arg value="apple"/> <property name="name" value="Fuji"/> </bean>
其它都一樣。
完整例子如下:
創建如下POJO:
Fruit
:Fruit接口;Apple
:Fruit實現類;Banana
:Fruit實現類;
package pojo; public interface Fruit { public void describe(); }
package pojo; public class Apple implements Fruit { private String name; public void setName(String name) { this.name = name; } public Apple() { System.out.println("Apple constructor"); } @Override public void describe() { System.out.println("I am " + name); System.out.println("Juicy!"); } }
package pojo; public class Banana implements Fruit { private String name; public void setName(String name) { this.name = name; } public Banana() { System.out.println("Banana constructor"); } @Override public void describe() { System.out.println("I am " + name); System.out.println("Sweet!"); } }
創建工廠類 FruitFactory
:
package factory; import pojo.Apple; import pojo.Banana; import pojo.Fruit; public class FruitFactory { public Fruit getFruit(String type) { System.out.println("creating new Fruit object"); if (type.equalsIgnoreCase("apple")) { return new Apple(); } else if (type.equalsIgnoreCase("banana")) { return new Banana(); } else { return null; } } }
在 applicationContext.xml
中註冊bean:
...... <bean id="fruitFactory" class="factory.FruitFactory"/> <bean id="apple" factory-bean="fruitFactory" factory-method="getFruit"> <constructor-arg value="apple"/> <property name="name" value="Fuji"/> </bean> <bean id="banana" factory-bean="fruitFactory" factory-method="getFruit"> <constructor-arg value="banana"/> <property name="name" value="Pisang"/> </bean> ......
創建測試用例:
@Test public void test4() { var ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("before getBean apple banana"); var fruit1 = ctx.getBean("apple", Fruit.class); fruit1.describe(); var fruit2 = ctx.getBean("banana", Fruit.class); fruit2.describe(); }
運行測試,如下:
creating new Fruit object
Apple constructor
creating new Fruit object
Banana constructor
before getBean apple banana
I am Fuji
Juicy!
I am Pisang
Sweet!
工廠bean
工廠bean是指實現瞭 FactoryBean
接口的類。
FactoryBean
接口有3個方法:
T getObject() throws
:創建產品實例;Class<?> getObjectType()
:獲取產品的類;default boolean isSingleton() {return true;}
:是否單例,默認值為true;
實現 FactoryBean
接口,則無需再配置 factory-method
,Spring知道如何創建產品(通過 getObject()
方法)。
比如:
<bean id="benz" class="factory.CarFactoryBean"> <property name="type" value="benz"/> </bean>
註意:該配置看起來很像普通的bean,實際上因為 CarFactoryBean
實現瞭 FactoryBean
接口,當調用 ctx.getBean("benz")
方法時,返回的並不是 CarFactoryBean
的實例,而是其 getObject()
方法的返回值。
註:默認scope是 singleton
,多次調用 ctx.getBean("dog")
返回的是同一個實例。如果在 CarFactoryBean
中override isSingleton()
方法並返回false,則每次調用ctx.getBean("benz")
返回的是不同對象。
註:工廠bean跟其它bean有一點不同,即使是singleton,它也不是在Spring初始化時生產bean,而是在第一次調用 ctx.getBean()
時才生產bean。
完整例子如下:
創建如下POJO:
Car
:Car接口;Benz
:Car實現類;Audi
:Car實現類;
package pojo; public interface Car { public void run(); }
package pojo; public class Benz implements Car{ public Benz() { System.out.println("Benz constructor"); } @Override public void run() { System.out.println("Go go go!"); } }
package pojo; public class Audi implements Car{ public Audi() { System.out.println("Audi constructor"); } @Override public void run() { System.out.println("OOOO"); } }
創建工廠類 FactoryBean
:
package factory; import org.springframework.beans.factory.FactoryBean; import pojo.Audi; import pojo.Benz; import pojo.Car; public class CarFactoryBean implements FactoryBean { private String type; public void setType(String type) { this.type = type; } @Override public Object getObject() throws Exception { System.out.println("creating new Car object"); if (type.equalsIgnoreCase("benz")) { return new Benz(); } else if (type.equalsIgnoreCase("audi")) { return new Audi(); } else return null; } @Override public Class<?> getObjectType() { return Car.class; } }
在 applicationContext.xml
中註冊bean:
...... <bean id="benz" class="factory.CarFactoryBean"> <property name="type" value="benz"/> </bean> <bean id="audi" class="factory.CarFactoryBean"> <property name="type" value="audi"/> </bean> ......
創建測試用例:
@Test public void test5() { var ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println("before getBean benz audi"); var car1 = ctx.getBean("benz", Car.class); car1.run(); var car2 = ctx.getBean("audi", Car.class); car2.run(); }
運行測試,如下:
before getBean benz audi
creating new Car object
Benz constructor
Go go go!
creating new Car object
Audi constructor
OOOO
總結
...... <!-- 通過bean的class屬性創建bean實例(無參構造器) --> <bean id="stoneAxe" class="pojo.StoneAxe"/> <bean id="steelAxe" class="pojo.SteelAxe"/> <bean id="person" class="pojo.Person"> <property name="name" value="Tom"/> <property name="axe" ref="stoneAxe"/> </bean> <!-- 通過bean的class屬性創建bean實例(帶參構造器) --> <bean id="playBook" class="pojo.PlayBook"/> <bean id="studyBook" class="pojo.StudyBook"/> <bean id="student" class="pojo.Student"> <constructor-arg index="0" value="Jerry"/> <constructor-arg index="1" ref="playBook"/> </bean> <!-- 通過靜態工廠方法創建bean實例 --> <bean id="dog" class="factory.AnimalFactory" factory-method="getAnimal"> <constructor-arg value="dog"/> <property name="name" value="Snoopy"/> </bean> <bean id="cat" class="factory.AnimalFactory" factory-method="getAnimal"> <constructor-arg value="cat"/> <property name="name" value="Kitty"/> </bean> <!-- 通過實例工廠方法創建bean實例 --> <bean id="fruitFactory" class="factory.FruitFactory"/> <bean id="apple" factory-bean="fruitFactory" factory-method="getFruit"> <constructor-arg value="apple"/> <property name="name" value="Fuji"/> </bean> <bean id="banana" factory-bean="fruitFactory" factory-method="getFruit"> <constructor-arg value="banana"/> <property name="name" value="Pisang"/> </bean> <!-- 通過工廠bean創建bean實例 --> <bean id="benz" class="factory.CarFactoryBean"> <property name="type" value="benz"/> </bean> <bean id="audi" class="factory.CarFactoryBean"> <property name="type" value="audi"/> </bean> ......
此外,有幾點說明:
使用工廠bean來管理的bean,即使scope是 singleton
,也並不是在Spring初始化時生產對象的,而是在第一次調用 ctx.getBean()
方法時生產的。
Spring整合MyBatis,使用瞭 SqlSessionFactoryBean
、 MapperFactoryBean
,這兩個就是工廠bean,它們實現瞭 FactoryBean
接口。前者生產 SqlSessionFactory
,後者生產 Mapper
。比如:
<!-- sqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- <property name="configLocation" value="classpath:mybatis-config.xml"/>--> <property name="mapperLocations" value="classpath:dao/*.xml"/> <property name="transactionFactory"> <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" /> </property> </bean> <bean id="myMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="dao.MyMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
到此這篇關於Spring創建bean實例的幾種方式分享的文章就介紹到這瞭,更多相關Spring創建bean 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Spring為singleton bean註入prototype bean
- Spring依賴註入的幾種方式分享梳理總結
- Spring Bean屬性註入的兩種方式詳解
- Spring從入門到源碼之IOC基本用法
- Spring的DI依賴註入詳解