BeanFactory和它的物件註冊和依賴繫結的三種方式
一、Spring中的BeanFactory是什麼?
BeanFactory是個基本的IoC容器,提供完整的IoC服務支援,負責物件建立的管理和依賴注入服務
如果沒有特殊指定,預設採用延遲初始化策略(lazy-load)。只有當客戶端物件需要訪問容器中的某個受管物件的時候,才對該受管物件進行初始化以及依賴注入操作
public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; Object getBean(String var1) throws BeansException; <T> T getBean(String var1, Class<T> var2) throws BeansException; <T> T getBean(Class<T> var1) throws BeansException; Object getBean(String var1, Object... var2) throws BeansException; <T> T getBean(Class<T> var1, Object... var2) throws BeansException; boolean containsBean(String var1); boolean isSingleton(String var1) throws NoSuchBeanDefinitionException; boolean isPrototype(String var1) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException; Class<?> getType(String var1) throws NoSuchBeanDefinitionException; String[] getAliases(String var1); }
二、BeanFactory的物件註冊和依賴繫結的三種方式
1、直接編碼方式(無需xml檔案)
1.1 專案結構
1.2 各個程式碼
public class App { private String says = "App"; private App(){ System.out.println("呼叫App無參構造"); } public App(String says) { System.out.println("呼叫App有參構造, 引數says為:" + says); this.says = says; } public void say(){ System.out.println("App.say():" + says); } public String getSays() { return says; } public void setSays(String says) { this.says = says; } @Override public String toString() { return "App{" + "says='" + says + '\'' + '}'; } }
public class Coder { private App app; private Coder(){ System.out.println("呼叫Coder無參構造"); } public Coder(App app) { System.out.println("呼叫Coder有參構造,引數app為:" + app.toString()); this.app = app; } public App getApp() { return app; } public void setApp(App app) { System.out.println("呼叫Coder.setApp(), 引數app為: " + app.toString()); this.app = app; } }
public class BeanFactoryTest extends TestCase {
/**
* 直接編碼方式
*/
public void testRedirectCode() {
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory beanFactory = bindViaCode(beanRegistry);
Coder coder = (Coder) beanFactory.getBean("coder");
coder.getApp().say();
/**
* 輸出為:
* //通過構造方法注入的情況
* 呼叫App無參構造
* 呼叫Coder有參構造,引數app為:App{says='App'}
* App.say():App
*
* //通過setter方式注入的情況下
* 呼叫Coder無參構造
* 呼叫App無參構造
* 呼叫Coder.setApp(), 引數app為: App{says='App'}
* App.say():App
*/
}
public static BeanFactory bindViaCode(BeanDefinitionRegistry registry){
AbstractBeanDefinition appBeanDefinition = new RootBeanDefinition(App.class);
AbstractBeanDefinition coderBeanDefinition = new RootBeanDefinition(Coder.class);
//將beanDefinition註冊到容器中
registry.registerBeanDefinition("app", appBeanDefinition);
registry.registerBeanDefinition("coder", coderBeanDefinition);
//指定依賴關係
//1、可以通過構造方法注入
// ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
// argumentValues.addIndexedArgumentValue(0, appBeanDefinition);
// coderBeanDefinition.setConstructorArgumentValues(argumentValues);
//2、可以通過setter方法注入
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
mutablePropertyValues.addPropertyValue("app", appBeanDefinition);
coderBeanDefinition.setPropertyValues(mutablePropertyValues);
//繫結完成
return (BeanFactory)registry;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--這是 pom.xml檔案 -->
<groupId>com.me.sourcecode</groupId>
<artifactId>spring-explore</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.1.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
1.3 原理 (摘自《spring揭祕》)
打個比方說,BeanDefinitionRegistry就像圖書館的書架,所有的書是放在書架上的。雖然你
還書或者借書都是跟圖書館(也就是BeanFactory,或許BookFactory可能更好些)打交道,但書架才
是圖書館存放各類圖書的地方。所以,書架相對於圖書館來說,就是它的“BookDefinitionRegistry”。
每一個受管的物件,在容器中都會有一個BeanDefinition的例項(instance)與之相對應,該
BeanDefinition的例項負責儲存物件的所有必要資訊,包括其對應的物件的class型別、是否是抽象
類、構造方法引數以及其他屬性等。當客戶端向BeanFactory請求相應物件的時候,BeanFactory會
通過這些資訊為客戶端返回一個完備可用的物件例項。RootBeanDefinition和ChildBean-
Definition是BeanDefinition的兩個主要實現類。
2、xml配置檔案方式
2.1 專案結構(上面的基礎上)
2.2 新增檔案SpringBean.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" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<bean id="app" class="com.me.sourcecode.bean.App"></bean>
<!--<bean id="coder" class="com.me.sourcecode.bean.Coder">-->
<!--<constructor-arg index="0">-->
<!--<ref bean="app" />-->
<!--</constructor-arg>-->
<!--</bean>-->
<bean id="coder" class="com.me.sourcecode.bean.Coder">
<property name="app" ref="app"></property>
</bean>
</beans>
BeanFactoryTest類裡,增加對xml檔案配置方式的測試
/**
* xml配置檔案方式
*/
public void testXmlFile(){
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
BeanFactory beanFactory = bindViaXmlFile(beanRegistry);
Coder coder = (Coder) beanFactory.getBean("coder");
coder.getApp().say();
}
public static BeanFactory bindViaXmlFile(BeanDefinitionRegistry registry) {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
xmlBeanDefinitionReader.loadBeanDefinitions("classpath*:SpringBean.xml");
return (BeanFactory) registry;
}
2.3 原理(摘自《spring揭祕》)
XmlBeanDefinitionReader負責讀取Spring指定格式的XML配置檔案並解析,之後將解析後的檔案內
容對映到相應的BeanDefinition,並載入到相應的BeanDefinitionRegistry中(在這裡是DefaultListableBeanFactory)
3、基於註解的方式
3.1 專案結構(不變)
3.2 有修改的內容
@Component
public class App {
private String says = "App";
private App(){
System.out.println("呼叫App無參構造");
}
public App(String says) {
System.out.println("呼叫App有參構造, 引數says為:" + says);
this.says = says;
}
public void say(){
System.out.println("App.say():" + says);
}
public String getSays() {
return says;
}
public void setSays(String says) {
this.says = says;
}
@Override
public String toString() {
return "App{" +
"says='" + says + '\'' +
'}';
}
}
@Component
public class Coder {
@Autowired
private App app;
private Coder(){
System.out.println("呼叫Coder無參構造");
}
public Coder(App app) {
System.out.println("呼叫Coder有參構造,引數app為:" + app.toString());
this.app = app;
}
public App getApp() {
return app;
}
public void setApp(App app) {
System.out.println("呼叫Coder.setApp(), 引數app為: " + app.toString());
this.app = app;
}
}
SpringBean.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" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<context:component-scan base-package="com.me.sourcecode.bean" />
</beans>
BeanFactoryTest增加基於註解方式的測試
/**
* 基於註解的方式
*/
public void testAnnoation(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringBean.xml");
Coder coder = (Coder) applicationContext.getBean("coder");
coder.getApp().say();
/**
* 輸出為:
* 呼叫App無參構造
* 呼叫Coder無參構造
*
* App.say():App
*/
}
3.3 原理
可以看到輸出結果中,沒有呼叫Coder.setApp()這一句,而又有App.say(): App這一句。它是Coder裡面的app是怎麼注入進入的呢?
1) 通過SpringBean.xml檔案的<context:component-scan base-package="com.me.sourcecode.bean" />,開始掃描這個路徑,處理所有@Component註解標記的類(即App,Coder),生成對應的BeanDefinition
2)根據BeanDefinition建立Bean,此時Coder裡面的app還是null,也就說還沒有進行依賴注入
3)建立Bean之後,有個例項化Bean的流程。在這個流程中,AutowiredAnnotationBeanPostProcessor起了作用,通過反射的方式將App注入到Coder物件裡面了。
反射 field.set(bean, value) ,此時bean是Coder的例項,value是App的例項