1.使用簡單,改動靈活
2.不須要改動代碼
複製代碼
此時咱們能夠簡單地作一下對比,如下是上一篇的測試代碼中截取的一小段java
GeneralBeanDefinition bd = new GeneralBeanDefinition();
bd.setBeanClass(ABean.class);
List<Object> args = new ArrayList<>();
args.add("abean01");
args.add(new BeanReference("cbean"));
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("abean", bd);
bd = new GeneralBeanDefinition();
bd.setBeanClass(CBean.class);
args = new ArrayList<>();
args.add("cbean01");
bd.setConstructorArgumentValues(args);
bf.registerBeanDefinition("cbean", bd);
// 前置加強advice bean註冊
bd = new GeneralBeanDefinition();
bd.setBeanClass(MyBeforeAdvice.class);
bf.registerBeanDefinition("myBeforeAdvice", bd);
複製代碼
咱們平常使用bean的方式有xml和註解的方式,以上的方式太過於繁瑣了,因此咱們以後但願提供一個使用xml這樣的方式spring
<bean id = "abean" class = "MySpring.samples.ABean">
<constructor-arg type = "String" value = "abean01">
<constructor-arg ref = "cbean">
</bean>
複製代碼
這樣用戶就會用的更加輕便編程
定義一套xml標記---> 用戶使用xml標記配置bean定義--->
用戶xml配置文件---> 加載xml配置---> 解析xml配置--->
建立bean定義對象---> 註冊bean定義到beanFactory
定義一套註解---> 用戶在類上用註解標註bean定義--->
用戶指定掃描的包---> 掃描指定包下的類---> 反射獲取bean定義
---> 建立bean定義對象---> 註冊bean定義到beanFactory
最後兩步是同樣的,前面是比較類似的
複製代碼
咱們此時還不知道該從何入手,這些標記/註解該怎麼定義,咱們不難發現上面工做過程當中它們都是對bean定義進行處理的,因此咱們就能夠開始分析了後端
Class<?> getBeanClass();
String getScope();
boolean isSingleton();
boolean isPrototype();
String getFactoryBeanName();
String getFactoryMethodName();
String getInitMethodName();
String getDestroyMethodName();
List<?> getConstructorArgumentValues();
public Object[] getConstructorArgumentRealValues();
public Constructor<?> getConstructor();
public Method getFactoryMethod();
List<PropertyValue> getPropertyValues();
複製代碼
比照咱們平常使用時的xmlapp
<beans>
<bean id="" class="" init-method="" destory-method="" scope="">
<constructor-arg type = "String" value = "abean01">
<constructor-arg ref = "cbean">
<property name="" value="">
...
</bean>
...
</beans>
複製代碼
此時或許咱們須要爲此提供一個DTD或者XSD文檔來講明用戶該如何使用xml文檔框架
1.指定類
2.指定beanName
3.指定scope
4.指定工廠方法
5.指定工廠bean
6.指定init method
7.指定destory method
8.指定構造參數依賴
9.指定屬性依賴
複製代碼
其中 1~7 咱們能夠定義一個 @Component 註解來完成post
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
String name() default "";
String scope() default BeanDefinition.SCOPE_SINGLETON;
String factoryBeanName() default "";
String factoryMethodName() default "";
String initMethodName() default "";
String destoryMethodName() default "";
}
複製代碼
8,9咱們可能就須要一個 @Autowired 和 @Qualifier,如下代碼舉例說明咱們如何使用學習
@Component(initMethodName = "init",destoryMethodName = "destory")
public class ABean {
private String name;
private CBean cb;
@Autowired
private DBean dBean;
@Autowired
public ABean(@Value("說出你的願望吧")String name, @Qualifier("CBean01")CBean cb){
super();
this.name = name;
this.cb = cb;
System.out.println("調用了含有CBean的構造方法");
}
}
複製代碼
此時 @Component 註解加到類上面,表面它要成爲一個bean,而後指定了初始化和銷燬方法,此時咱們已經不須要在 @Component 中再指定bean的名稱了,由於咱們已經在取到註解的時候是經過反射來取的,那時候已經取到了類的名稱了。構造方法上面加入了 @Autowired ,代表咱們建立該bean時應該調用此構造方法,在 ABean 的構造方法中,咱們依賴了 CBean ,此時咱們可能建立了 CBean 的多個實例呀,因此 @Qualifier 能夠指定咱們須要取到的就是名字叫作 CBean01 的那個實例嗎,@Value 就是給定直接值的作法,好比名字。測試
① Java標準規範---javax.inject包 this
這裏面定義了一些註解是用於實現依賴注入的,其中Named等同於剛剛的 @Component 註解,還有Qualifier和Scope等等也是同樣的
② javax.Annocation(類同)
這裏Resource是組件,postConstruct是指定初始化方法,preDestroy是銷燬方法等
以上的這些在咱們本身沒法實現注入的狀況下能夠直接使用或者說借鑑它們來實現
還記得前面咱們總結的工做流程嗎?
用戶xml配置文件---> 加載xml配置---> 解析xml配置
用戶指定掃描的包---> 掃描指定包下的類---> 反射獲取bean定義
以後都是:
建立bean定義對象---> 註冊bean定義到beanFactory
咱們首先搞清楚這些步驟應該都會出如今哪一個位置中
複製代碼
首先看 用戶指定掃描的包---> 掃描指定包下的類---> 反射獲取bean定義 這裏所做的事情爲解析bean配置,向BeanFactory註冊bean定義,它不是beanFactory的事情,因此咱們應該把它獨立出來做爲一個接口
此時咱們發現了,它們都要使用BeanFactory--->BeanDefinitionRegistry,因此咱們在中間給它加一層抽象,持有BeanFactory
此時咱們也能夠進一步的設想,XmlApplicationContext中完成bean的加載,解析,建立註冊,AnnocationApplicationContext中掃描後獲取再建立註冊,他們對bean進行了這些處理,是必定用到了咱們以前實現了的BeanFactory和BeanDefinitionRegistry的(bean定義註冊器).因此XmlApplicationContext和AnnocationApplicationContext都必須持有bean工廠,這時候就以下圖,抽象類AbstractApplicationContext就持有了beanFactory
要知道以上圖中的接口和類才能清楚瞭解他如何使用咱們的框架,固然還有獲取bean時使用的BeanFactory,可是咱們此時會想到,用戶可能使用框架的時候不想去了解太多這些東西,若是能把用戶的這個學習成本降下來的話會更好,因此最好就是讓用戶只須要知道ApplicationContext及其子類是最好的,此時咱們很容易想到的就是外觀模式,把二者合併,讓ApplicationContext也實現BeanFactory,而後beanFactory的行爲實現統一寫在抽象類中便可
① xml的來源會有多種
文件系統,Classpath,URL,String···
它們加載的方式也不同,對於xml解析,想得到的僅僅爲加載過程當中的InputStream
因此咱們只須要提供一個getInputStream的方式,後端得到流去解析便可,前面的加載過程能夠再也不關心
複製代碼
此時咱們將會這樣設計
加入Resource接口提供了更多的方法,這樣用途會更廣,並且咱們以後也基於Resource接口去實現,以後就是對於不一樣來源的處理,編寫出FileSystemResource,ClassPathResource,UrlResource
② 這裏咱們定義不一樣的Resource類對應不一樣的xml來源
如何分辨建立它們的對象,由於用戶給出的確定是對於他們來講最容易接受的仍是提供字符串形式的數據,這個分辨字符串建立對應的resource的工做就是加載,這個分辨能夠交由給ApplicationContext來作
然而根據不一樣類型建立不一樣對象,其實也就是有點工廠模式的味道了,那咱們的計劃就是編寫一個ResourceLoader接口去提供一個getResource()方法便可
那分辨的功能到底如何來作,咱們只能對xml的來源指定規則,FileSystem的前綴是file,Classpath的前綴是classpath,url開頭的不須要指定,自己就是有協議的,那工廠根據前綴來區分建立不一樣的Resource的對象
③ 註解方式如何進行掃描
到指定的包目錄下找出全部的類文件,此時咱們也是要獨立出一個接口PathMatcher去實現這個功能的,咱們這時候採用的是Ant Path表達式的方式(好比springMVC中的RequestMapping裏面的路徑也是用ant).
以後找到全部的.class文件後,咱們須要的是類的名稱(全限定類名),此時咱們回憶,剛剛咱們玩xml的時候,是否有一個接口Resource提供了getFile()方法,此時咱們的實現中存在一個FileSystemResource的實現類,裏面是持有File類型的file對象的
此時咱們又回到誰來掃描的這件事情來,咱們決定將掃描的事情外包給一個類 ClassPathBeanDefinitionScanner 去作,裏面提供 scan(String...package) 方法,裏面能夠給入多個包,裏面持有 BeanDefinitionRegistry 對象,以後以前的 AnnocationApplicationContext 就直接持有 ClassPathBeanDefinitionScanner的scanner 對象便可
在哪裏啓動掃描纔是正確的呢,答案是 AnnocationApplicationContext 的構造方法中便可
加載和掃描的輸出是什麼?
Resource!!!
複製代碼
這裏就是兩個方法,從resource裏面加載bean定義,放入bean工廠裏面去進行註冊,抽象裏面持有bean定義註冊器便可
到目前爲止,咱們已經把整個架子都搭好了,接下來只是往架子裏面填寫相應的代碼便可,相應的代碼由於時間問題沒來得及放入文章,之後會找機會補上,之後會繼續更源碼的東西,共勉,謝謝