使用Spring的IOC容器擷取一個Bean的實質,其實可以簡化成使用反射的一個過程。
首先
這是一個Bean的定義。
Student bean
package cn.edu.au.selection.service;
public class Student {
private String name;
private int age;
public Student() {
}
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;
}
}
然後
啟動Spring的時候,也就是Spring初始化的時候。有經驗的人都知道ApplicationContext的初始化過程主要是在
AbstractApplicationContext
類中
refresh()
方法中進行。而且該方法中最重要的兩個子方法分别是
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
和
finishBeanFactoryInitialization(beanFactory);
。
第一個方法
第一個方法的主要作用就是建立可供上下文使用的基本IOC容器,也就是
DefaultListableBeanFactory
。由該IOC容器對我們所注冊的
BeanDefinition
以及Bean進行托管。在該IOC容器的初始化過程中,主要做了三件事:
1. `BeanDefinition`的`Resource`的定位。也就是說我們配置`<bean></bean>`标簽的Spring`*.xml`檔案的定位。或是`FileSystemResource`,或是`ClassPathContextResource`。這些Resource其實就是對Xml檔案的一個封裝。
2. 當定位到`Resource`之後,便可以解析 `BeanDefinition`了。該`BeanDefinition`其實是對Bean定義的一個抽象。包含你定義的Bean的一些中繼資料、`ClassLoader`等等。該過程又可能分成兩個步驟,第一步,進行Xml檔案解析,該步隻是将檔案當成普通的Xml檔案進行解析。而第二步就是Spring按照`<bean></bean>`标簽的定義對`BeanDefinition`進行解析生成`BeanDefinition`對象。這個對象由`AbstractBeanDefinition`指向,放在IOC容器中統一托管。
3. 第三步就是`BeanDefination`在IoC容器中的注冊,也就是正式托管的過程。這個過程其實就是将`BeanDefination`放入`DefaultListableBeanFactory`的一個`ConcurrentHashMap`中,叫做`beanDefinitionMap`。供随後建立Bean的實體做準備。
注意:在第二個步驟中,不僅僅将
*.xml
檔案中定義的Bean的
BeanDefinition
解析出來,而且将有
<context:component-scan/>
标簽的,有
@Component
等注解的那些Bean的
BeanDefinition
也解析出來。當然還有Spring内部自己使用的一些Bean的
BeanDefinition
。
第二個方法
第二個方法
finishBeanFactoryInitialization(beanFactory);
的主要作用就是初始化所有剩餘的非延遲加載
(non-lazy-init)
的單例的Bean。注意,在這個方法的調用棧中就包含了我們會使用的
ApplicationContext.getBean()
的調用。也就是說,其實所有Bean的建立都是在
getBean()
方法中進行的。方法調用中有一個比較關鍵的方法就是
AbstractBeanFactory.doGetBean()
方法。這個方法的邏輯路線是這樣的。
- 首先當初始化上下文的時候,非延遲加載的單例Bean先會通過
方法查找。當使用Object sharedInstance = getSingleton(beanName);
方法的時候,首先也是在getBean
方法中查找有沒有之前建立好的執行個體。這一步如果沒有找到,當然隻能找到單例,prototype類型的肯定找不到,還有getSingleton(beanName)
的也會找不到。lazy-init
- 然後進行一個判斷。首先從IOC容器中擷取
,從其中判斷待擷取的Bean是單例還是原型BeanDefination
模式。如果是原型模式,每次建立一個Bean的執行個體即可。如果是單例模式,當第一次建立完執行個體之後需要儲存到IOC容器中,第二次擷取的時候就無需再次建立了。同樣的,該單例Bean也是儲存在(prototype)
的另一個DefaultListableBeanFactory
中,叫做ConcurrentHashMap
。singletonObjects
- 在建立Bean執行個體,調用
方法時有兩個關鍵的方法,分别是AbstractAutowiredCapableBeanFactory.doCreateBean()
和createBeanInstance()
。其中第一個方法就是使用反射,調用類的構造方法來建立一個該類的執行個體對象。而第二個方法就是對該執行個體對象進行初始化,也就是執行個體中屬性的初始化。有些populateBean()
或者@Autowired
的注解的屬性的初始化都會在這裡進行。并且有@Value
注解的屬性還會觸發遞歸調用建立Bean的方法。說白了,就是依賴關系處理的過程。@Autowired
- 在完成依賴關系處理之後,在該方法中接着會執行一個這個方法
,這個方法中又有三個重要的方法,分别是initializeBean()
,applyBeanPostProcessorsBeforeInitialization()
和invokeInitMethods()
。其中含有applyBeanPostProcessorsAfterInitialization()
注解的方法會在第一個方法中執行。第二個方法中一般執行@PostConstruct
中指定的BeanDefinition
方法或者init-method
實作方法。afterPropertitiesSet()
建立Bean執行個體的簡化過程
至此,Spring之IOC容器的初始化過程以及依賴注入都說完了。
那麼建立Bean執行個體的過程可以簡化成以下代碼:
package cn.edu.au.selection.service;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection {
private static Logger logger = LoggerFactory.getLogger(Reflection.class);
public static void main(String[] args){
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clazz;
String className = "cn.edu.au.selection.service.Student";
Object object = null;
String name = "Ethan Hunt";
int age = ;
try {
clazz = classLoader.loadClass(className);
Constructor<?>[] ctors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : ctors){
constructor.setAccessible(true);
object = constructor.newInstance();
}
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(object, name);
Method method = clazz.getDeclaredMethod("setAge", int.class);
method.setAccessible(true);
method.invoke(object, age);
} catch (ClassNotFoundException e) {
logger.error("class {} cannot be found!", className);
e.printStackTrace();
} catch (InvocationTargetException e) {
logger.error("被調用的方法{}的内部抛出了異常而沒有被捕獲!", "Student()/setAge()");
e.printStackTrace();
} catch (InstantiationException e) {
logger.error("{}類無法被執行個體化!", className);
e.printStackTrace();
} catch (IllegalAccessException e) {
logger.error("方法{}或屬性{}無法被通路!", "setAge()", "name");
e.printStackTrace();
} catch (NoSuchFieldException e) {
logger.error("沒有這樣的屬性{}!", "name");
e.printStackTrace();
} catch (NoSuchMethodException e) {
logger.error("沒有這樣的方法{}!", "setAge()");
e.printStackTrace();
}
logger.info(JSON.toJSONString(object));
}
}
輸出結果為:
{
"age": ,
"name": "Ethan Hunt"
}