【Java】模擬Sping,實現其IOC和AOP核心(一)

在這裏我要實現的是Spring的IOC和AOP的核心,並且有關IOC的實現,註解+XML能混合使用!java

參考資料:
IOC:控制反轉(Inversion of Control,縮寫爲IoC),是面向對象編程中的一種設計原則,能夠用來減低計算機代碼之間的耦合度。其中最多見的方式叫作依賴注入(Dependency Injection,簡稱DI),還有一種方式叫「依賴查找」(Dependency Lookup)。經過控制反轉,對象在被建立的時候,由一個調控系統內全部對象的外界實體,將其所依賴的對象的引用傳遞給它。也能夠說,依賴被注入到對象中。
AOP:Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。
(以上是度娘給出的說法,能夠看到這樣的說法很不容易讓人理解,過於官方化,下面是個人理解)spring

IOC:咱們平時在寫Java程序時,總要經過new的方式來產生一個對象,對象的生死存亡是與咱們直接掛鉤的,咱們須要它時,就new一個,不須要他時就經過GC幫咱們回收;控制反轉的意思就是將對象的生死權交給第三方,由第三方來生成和銷燬對象,而咱們只在須要時從第三方手中取獲取,其他無論,這樣,對象的控制權就在第三方手裏,咱們只有使用權!這就是所謂的控制反轉!
在Spring中,提供了XML文件配置和註解的方式來向第三方代表咱們須要第三方幫咱們建立什麼對象,Spring就是這個第三方!它負責經過XML文件的解析或者包掃描的方式,找到咱們給出的映射關係,利用反射機制,在其內部幫咱們「new」出對象,再存儲起來,供咱們使用!編程

AOP :就是動態代理的體現,在Spring中就是利用JDKProxy或者CGLibProxy技術,對方法進行攔截!
好比說有一個叫作fun()的方法,咱們在其執行先後對其攔截:緩存

 

就像這樣,fun當作是縱向的線,那麼就至關於用平面把這條線截斷了!app

 

有了上面的鋪墊,咱們能夠大概知道,Sping的IOC和AOP能夠幫咱們建立並管理對象,能夠對對象的方法進行攔截,那麼這兩個技術合在一塊兒,就能夠達到自動幫咱們建立、管理、並對對象進行攔截!ide

下面先給出我簡化的SpringIOC容器框圖:
模擬的IOC框圖函數

這是我簡化後的IOC框圖,實際上的SpringIOC是很是龐大的,裏面包含了許多接口,以及繼承關係,它把要處理的事務區分的很是細膩,將問題由大化小,層層遞減,把面向接口,高內聚低耦合體現的淋漓盡致。
Spring提供了註解和Xml方式的注入,因此後面會有兩個分支,分別處理註解和XML文件的配置!工具

BeanFactory:
在別的地方說法是一個最底層容器,其實不要被其「誤導」,在我這它僅僅只是一個接口,只提供了最基礎的一些方法,而方法的具體實現就須要真正的高級容器了!代碼以下:ui

 1 public interface BeanFactory {  2     String FACTORY_BEAN_PREFIX = "&";  3     Object getBean(String var1) throws BeansException;  4     <T> T getBean(String var1, Class<T> var2) throws BeansException;  5     <T> T getBean(Class<T> var1) throws BeansException;  6     Object getBean(String var1, Object... var2) throws BeansException;  7     <T> T getBean(Class<T> var1, Object... var2) throws BeansException;  8     boolean containsBean(String var1);  9     boolean isSingleton(String var1) throws NoSuchBeanDefinitionException; 10     boolean isPrototype(String var1) throws NoSuchBeanDefinitionException; 11     boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException; 12     Class<?> getType(String var1) throws NoSuchBeanDefinitionException; 13  String[] getAliases(String var1); 14 }

(這裏我直接挪用了Spring的源碼,因爲是模擬實現,因此後面只實現了其getBean的方法)this

 

ApplicationContext:
在別的地方的說法是一個高級容器,其實,它仍是一個接口,只不過在源碼中其繼承了許多接口(核心仍是BeanFactory),是一個集大成者,提供了遠比BeanFactory更多的功能。但目前所要實現的核心暫時用不上它,因此暫時留一個空接口吧...

1 public interface ApplicationContext extends BeanFactory { 2     // TODO
3 }

說到這裏,就不能往下繼續了,由於在上面咱們看到的,所謂的「容器」,僅僅是定義了接口,徹底不能裝東西啊,還有,所謂的容器裏又要裝什麼?這裏就要引入Bean!

Bean:
其實就是IOC容器裏存放的東西!前面我說過,Spring會根據咱們給出的映射關係,幫咱們建立對象並存儲起來,那麼是否這個對象就是Bean?是!但也不是!若是說Spring僅僅只是幫咱們管理對象,那麼它的功能也太單一了,那麼,如今不得再也不次提到前面說過的AOP!
前面說到Spring中的AOP使用了JDKProxy和CGLibProxy這兩種代理技術,這兩種技術的目的都是產生代理對象,對方法進行攔截。那麼,是否這個代理對象就是咱們的Bean?不徹底是!Bean實際上是原對象+代理對象!先不急,看到後面就會明白!

下面介紹兩種動態代理技術:
JDKProxy
使用JDK代理,其所代理的類,必需要實現某一個接口:

 1 @SuppressWarnings("unchecked")  2 public <E> E getJDKProxy(Class<?> klass, Object tagObject) {  3     return (E) Proxy.newProxyInstance(klass.getClassLoader(),  4  klass.getInterfaces(),  5             new InvocationHandler() {  6  @Override  7                 public Object invoke(Object proxy, Method method, Object[] args)  8                         throws Throwable {  9                     // TODO 置前攔截,可對參數args進行判斷
10                     Object result = null; 11                     try { 12                         result = method.invoke(tagObject, args); 13                     } catch (Exception e) { 14                         // TODO 對異常進行攔截
15                         throw e; 16  } 17                     // TODO 置後攔截,可對方法返回值進行修改
18                     return result; 19  } 20  }); 21 }

使用JDK代理的話就不得不傳入一個原始對象,因此若是不考慮CGLib代理,那麼Bean就是原始對象+代理對象!

 

CGLibProxy:
使用CGLib代理,是讓被代理的類做爲代理對象的父類,故原類不能被final修飾,也不能對final修飾的方法攔截!

如下是網上絕大多數人給出的用法:

 1 @SuppressWarnings("unchecked")  2 public <E> E getCGLibProxy(Class<?> klass) {  3     Enhancer enhancer = new Enhancer();  4     enhancer.setSuperclass(klass); // 從這裏能夠明顯看到,讓被代理的類做爲了代理對象的父類
 5     enhancer.setCallback(new MethodInterceptor() {  6  @Override  7         public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy)  8                 throws Throwable {  9             // TODO 置前攔截,可對參數args進行判斷
10             Object result = null; 11             try { 12                 result = methodProxy.invokeSuper(proxyObject, args); 13             } catch (Exception e) { 14                 // TODO 對異常進行攔截
15                 throw e; 16  } 17             // TODO 置後攔截,可對方法返回值進行修改
18             return result; 19  } 20  }); 21     return (E) enhancer.create(); 22 }

這種方式是沒錯,可是不適用於後面要作的,至於緣由,後面分析到了就會明白!
因此使用以下方式:

 1 @SuppressWarnings("unchecked")  2 public <E> E getCGLibProxy(Class<?> klass, Object tagObject) {  3     Enhancer enhancer = new Enhancer();  4  enhancer.setSuperclass(klass);  5     enhancer.setCallback(new MethodInterceptor() {  6  @Override  7         public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy)  8                 throws Throwable {  9             // TODO 置前攔截,可對參數args進行判斷
10             Object result = null; 11             try { 12                 result = method.invoke(tagObject, args); 13             } catch (Exception e) { 14                 // TODO 對異常進行攔截
15                 throw e; 16  } 17             // TODO 置後攔截,可對方法返回值進行修改
18             return result; 19  } 20  }); 21     return (E) enhancer.create(); 22 }

因爲是模擬實現,後面就所有采用CGLib代理!
能夠看到以上面這種方式進行CGLib代理就須要原始對象,那麼前面說到的Bean就必須是原對象+代理對象!
固然我知道以invokeSuper那種方式是不須要原始對象,可是緣由不是由於Bean,還在後面!

綜上,Bean的定義以下:

1 public interface BeanElement { 2     <E> E getProxy(); 3  Object getObject(); 4     boolean isDI(); // 用於之後判斷是否完成注入
5 }

在這裏我把BeanElement定義爲了一個接口,後面會產生兩個分支,會產生兩種不一樣處理方式的Bean,用接口更靈活,耦合也低!

如今知道了Bean究竟是什麼,咱們就能夠往下繼續進行:


AbstractApplicationContext:
ApplicationContext的具體實現類,但它是一個抽象類,只能實現部分功能,日後在它的基礎上還要分化成兩支,那麼,把全部的Bean存在這裏面再合適不過了!

 1 public abstract class AbstractApplicationContext implements ApplicationContext {  2     protected static final Map<String, String> beanNameMap;  // key : id value : className
 3     protected static final Map<String, BeanElement> beanMap; // key : className value : Bean
 4     protected AopFactory aopFactory; // Aop工廠,生產代理對象
 5     
 6     static {  7         beanNameMap = new HashMap<>();  8         beanMap = new HashMap<>();  9  } 10     
11     protected AbstractApplicationContext() { 12         aopFactory = new AopFactory(); 13         // 設置切面
14  aopFactory.setAdvice( 15                 (new AdviceHander()) 16                 .setIntercepterFactory(new IntercepterLoader())); 17  } 18     
19     protected void add(String id, String className, BeanElement bean) throws BeansException { 20         if (beanMap.containsKey(className)) { 21             // TODO 能夠拋異常!
22             return; 23  } 24  beanMap.put(className, bean); 25         if (id.length() <= 0) return; 26         if (beanNameMap.containsKey(id)) { 27             throw new BeansException("bean:" + id + "已定義!"); 28  } 29  beanNameMap.put(id, className); 30  } 31 }

其中的aopFactory是代理工廠,負責生產代理,會在後面給出,先不急。
能夠看到,AbstractApplicationContext這個類持有了兩張靜態的map,第一組是用來存取Bean的別名(id),第二組用來存放真正的Bean,這就是咱們真正意義上的容器,因爲其map都是static修飾的,在類加載的時候就存在了,因此日後有別的類繼承它時,這兩個map是共享的!只增長了一個add方法,只要是繼承自它的子類,都會經過這種方式添加Bean!而且這個類是protected的,不對外提供使用!

 

咱們先進行左邊的分支,用註解的方式完成IOC
這裏說的註解都是自定義註解,屬於RUNTIME,就必須經過反射機制來獲取,反射機制就要獲得類或者類名稱,那麼就先獲得符合要求的類,這裏就要用到包掃描,我在前面的博客中有介紹過:【Java】包、jar包的掃描
首先是對類的註解:
@Component

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.TYPE) 3 public @interface Component { 4     String id() default ""; 5 }

其中的id至關於類名稱的別名,具備惟一性,若是不設置則不處理!經過這個註解咱們就能夠判斷哪一個類是須要進行操做的,就應該自動地爲這個類生成一個對象和代理對象,將其添加到beanMap中,就是bean的標誌!
若是說用過Spring的XML配置,這其實就至關於一個bean標籤:

1 <bean id="XXX" class="XXX">
2  ...... 3 </bean>

註解中的id就至關於id屬性,咱們是經過反射機制獲得註解的,固然能獲得類名稱,那就至關於有了class屬性!

可是僅僅有這個註解是徹底不夠的,咱們只能經過反射機制產生一個對象,但它的成員都沒賦值,僅僅是一具軀殼,因此就須要對成員添加註解,將咱們須要的值注入進來;固然也能夠給方法添加註解,經過setter方法給成員賦值,Spring就是使用的這種方式!
這是對成員的註解:
@Autowired

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) 3 public @interface Autowired { 4     boolean requisite() default true; 5 }

這裏直接使用的spring源碼,在源碼中,這個註解能夠成員、方法以及構造方法添加。其中的requisite是是否必須注入,這是Spring提供的更爲細膩的操做,個人實現暫時不考慮它。

若是說這個註解是給成員添加的,那麼標誌着它須要賦值!使用這個註解的前提是它自己是一個複雜類型,不是基本類型,它的賦值,是將咱們beanMap中的符合要求的Bean注入進去!至於基本類型後面有別的解決辦法。
用Component 和Autowired註解其實就至關於以下XML的配置:

1 <bean id="XXX" class="XXX">
2     <property name="XXX" ref="XXX">
3 </bean>

咱們一樣是經過反射機制獲得的Autowired註解,那麼必定能夠獲得成員名稱,和成員類型,成員名稱就至關於name屬性,經過成員類型就能夠獲得類型名稱,就至關於ref屬性!

若是說是給構造方法添加,那麼就規定了咱們在反射機制執行時須要調用哪一個構造方法,至關於以下:

1 <bean id="XXX" class="XXX">
2     <constructor-arg index="0" ref="XXX">
3     <constructor-arg index="1" ref="XXX">
4 </bean>

對於構造方法的處理我以爲使用註解的方式比XML配置要更好,註解能夠直接定位到某一個構造方法,可是XML文件的方式就須要遍歷比較,找出符合要求的,並且關於這個符合要求這一說還有更爲複雜的問題,我會在後面用XML的方式詳細介紹!

還有一種就是對方法添加,實際上就是提供給setter方法使用的,經過執行setter方法給成員賦值,可能看到這裏會以爲這樣作是否是畫蛇添足,其實不是,由於這樣作會避免我後面會談到的循環依賴的問題!因此給方法添加,和對成員添加等效:

1 <bean id="XXX" class="XXX">
2     <property name="XXX" ref="XXX">
3 </bean>


上面我說過使用Autowired 是不處理基本類型的,它是將bean注入的,基本類型徹底沒有必要做爲bean,那麼,咱們就能夠給出一個註解,直接賦值:

@Value

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({ElementType.FIELD, ElementType.PARAMETER}) 3 public @interface Value { 4  String value(); 5 }

其中value就是咱們要注入的值,可是它是String類型的,可能須要強制類型轉換
演示以下:

 1 @Component  2 public class TestA {  3  @Autowired  4     private TestB testB;  5     @Value(value="直接賦值")  6     private String member;  7     
 8     public TestA() {  9  } 10 
11 } 12 
13 @Component(id = "testB") 14 public class TestB { 15     private int a; 16     private TestA testA; 17     
18  @Autowired 19     public TestB(@Value(value="10")int a, TestA testA) { 20         this.a = a; 21         this.testA = testA; 22  } 23     
24 }

就至關於:

1 <bean class="xxx.xxx.TestA">
2     <property name="testB" ref="xxx.xxx.TestB">
3     <property name="member" value="直接賦值">
4 </bean>
5 <bean id="testB" class="xxx.xxx.TestB">
6     <constructor-arg index="0" value="10"></constructor-arg>
7     <constructor-arg index="1" ref="xxx.xxx.TestA"></constructor-arg>
8 </bean>

爲了簡單處理,Autowired註解我在後面就只處理成員的。

有了上面的兩個註解是否夠呢?固然不夠。仔細想想,若是說咱們須要Spring幫咱們建立的對象,其對應的類又被打成了jar包,那麼咱們徹底沒有辦法對已經造成jar包的代碼添加註解;又或者說是咱們須要建立的對象不是經過反射機制就能產生的,它是一個工廠對象,須要走工廠模式那一套來建立,上面的兩個註解就遠遠不能知足咱們的要求了!所以,咱們還須要一個做爲補充的註解:

 

@Bean

1 @Retention(RetentionPolicy.RUNTIME) 2 @Target(ElementType.METHOD) 3 public @interface Bean { 4     String id() default ""; 5 }

能夠看出這是對方法的註解,由於咱們能夠經過反射機制執行方法,將方法的返回值做爲Bean的原始對象,再產生一個代理對象,這樣就能解決上面的所說的問題!

 1 @Component  2 public class Action {  3     
 4     @Bean(id="getDocumentBuilderFactory")  5     public DocumentBuilderFactory getDocumnet() throws Exception {  6         return DocumentBuilderFactory.newInstance();  7  }  8     
 9  @Bean 10     public DocumentBuilder getDocumentBuilder(DocumentBuilderFactory factory) throws Exception { 11         return factory.newDocumentBuilder(); 12  } 13     
14 }

就至關於:

1 <bean class="xxx.xxx.Action "></bean>
2 <bean class="javax.xml.parsers.DocumentBuilderFactory" id="getDocumentBuilderFactory" 
3  factory-method="newInstance"></bean>
4 <bean class="javax.xml.parsers.DocumentBuilderFactory" 
5  factory-bean="getDocumentBuilderFactory" factory-method="newDocumentBuilder">
6 </bean>

有了這些註解,咱們就能對包進行掃描,能夠繼續向下進行!

AnnotationContext:
這個類繼承自AbstractApplicationContext,它是protected的,不對外提供,我用它來進行有關注解的掃描和解析,但它的功能有限,不能完成所有的注入,這會涉及到注入的順序,以及注入之間的依賴關係:

 1 /**
 2  * 執行包掃描  3  * 將符合要求的結果添加到父類AbstractApplicationContext的beanMap中  4  * 只處理@Component和@Bean註解  5  * @Autowired註解留給下一級處理  6  */
 7 public class AnnotationContext extends AbstractApplicationContext {  8     // method緩衝區,保存暫時不能執行的方法,其中的MethodBuffer用來封裝method,以及invoke時所須要的內容
 9     private List<MethodBuffer> methodList;  10 
 11     protected AnnotationContext() {  12  }  13     
 14     protected AnnotationContext(String packageName) {  15  scanPackage(packageName);  16  }  17     
 18     /**
 19  * 經過aopFactory產生代理對象,將代理對象和原始對象封裝成bean添加到父類的map中  20      */
 21     private void addBean(Class<?> klass, Object object, String id, String className) {  22         // aopFactory是其父類AbstractApplicationContext的成員,原來產生代理對象
 23         Object proxy = aopFactory.creatCGLibProxy(klass, object);  24         // AnnoationBean是BeanElement接口的一個實現類
 25         AnnotationBean bean = new AnnotationBean(object, proxy);  26         // 父類AbstractApplicationContext的add方法
 27  add(id, className, bean);  28  }  29     
 30     protected void scanPackage(String packageName) {  31         new PackageScanner() {  32  @Override  33             public void dealClass(Class<?> klass) {  34                 if (!klass.isAnnotationPresent(Component.class)) return;  35                 Component component = klass.getAnnotation(Component.class);  36                 String className = klass.getName();  37                 String name = component.id();  38                 try {  39                     // 這裏簡單起見,不考慮構造方法的重載,默認執行無參構造 
 40                     Object object = klass.newInstance();  41                     // 產生BeanElement,加入到beanMap中
 42  addBean(klass, object, name, className);  43                     // 處理帶有@Bean註解的方法
 44  dealMethod(klass, object);  45                 } catch (Exception e) {  46                     // TODO 
 47  e.printStackTrace();  48  }  49  }  50  }.scanPackage(packageName);  51         if (methodList == null) return;  52         // 執行緩存的全部方法
 53         for (MethodBuffer methodBuffer : methodList) {  54             // 得到方法執行所須要的東西
 55             String id = methodBuffer.getId();  56             Class<?> returnClass = methodBuffer.getReturnClass();  57             Method method = methodBuffer.getMethod();  58             Object object = methodBuffer.getObject();  59             Parameter[] parameters = methodBuffer.getParameters();  60             
 61             try {  62  dealMultiParaMethod(returnClass, method, object, parameters, id);  63             } catch (Exception e) {  64                 // TODO 
 65  e.printStackTrace();  66  }  67  }  68  methodList.clear();  69  }  70     
 71     private void dealMultiParaMethod(Class<?> returnClass, Method method,  72  Object object, Parameter[] parameters, String id)  73                     throws BeansException, IllegalAccessException, IllegalArgumentException,  74  InvocationTargetException, ValueOnlyPrimitiveType {  75         int count = parameters.length;  76         Object[] result = new Object[count];  77         
 78         for (int index = 0; index < count; index++) {  79             Parameter para = parameters[index];  80             // 判斷參數是否帶有@Value註解
 81             if (para.isAnnotationPresent(Value.class)) {  82                 Class<?> type = para.getType();  83                 // 判斷@Value註解標識的參數是不是基本類型(八大類型和String)
 84                 if (!type.isPrimitive() && !type.equals(String.class)) {  85                     throw new ValueOnlyPrimitiveType("Value只能用基本類型!");  86  }  87                 // TypeConversion是我本身的一個工具類,用於將字符串轉換成基本類型!
 88                 result[index] = TypeConversion.getValue(para.getAnnotation(Value.class).value(),  89  para.getType().getSimpleName());  90             } else {  91                 // 若是不是基本類型,那麼就須要從beanMap中獲取符合要求的bean
 92                 result[index] = getBean(para.getType());  93  }  94  }  95         // 執行方法,得到方法返回值
 96         Object returnObject = method.invoke(object, result);  97         // 爲方法執行結果建立bean,添加到beanMap中
 98  addBean(returnClass, returnObject , id, returnClass.getName());  99  } 100     
101     /**
102  * 遍歷全部方法,處理帶有@Bean註解的方法 103      */
104     private void dealMethod(Class<?> klass, Object object) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 105         Method[] methods = klass.getDeclaredMethods(); 106         for (Method method : methods) { 107             if (!method.isAnnotationPresent(Bean.class)) continue; 108             
109             Class<?> returnType = method.getReturnType(); 110             if (returnType.equals(void.class)) { 111                 // TODO 若是沒有返回值,那麼根本沒有必要作
112                 return; 113  } 114             String id= method.getAnnotation(Bean.class).id(); 115             Parameter[] parameters = method.getParameters(); 116             // 判斷方法是否有參數,沒有參數,直接執行,添加Bean 117             // 有參數就先緩存起來,等全部都掃描完成後再執行
118             if (parameters.length <= 0) { 119                 Object returnObject = method.invoke(object); 120  addBean(returnType, returnObject, id, returnType.getName()); 121             } else { 122                 (methodList = methodList == null ? new ArrayList<>() : methodList) 123                     .add(new MethodBuffer(returnType, object, method, parameters, id)); 124  } 125  } 126  } 127     
128 }

這個類只負責掃描包,爲帶有@Component註解的類經過反射機制生成對象,再經過代理工廠將其加工成代理對象,而後封裝再AnnotationBean中做爲bean,將其添加到BeanMap中!
其次還處理了帶有@Bean註解的的方法,若是說是方法帶有參數,那麼就像將這個方法的執行延後,等全部東西都掃描完成後再執行;而對於無參的方法,則能夠直接執行,併爲執行結果產生的對象建立代理,生成AnnotationBean,添加進beanMap中。至於@Autowired註解這個類暫不處理,留給下一級處理!


AnnotationBean類的定義以下:

 1 /**
 2  * 註解分支中BeanElement的具體實現類  3  */
 4 public class AnnotationBean implements BeanElement {  5     private boolean DI; // 判斷是否完成注入
 6     private Object object; // 原始對象
 7     private Object proxy; // 代理對象
 8     
 9  AnnotationBean() { 10         this(false, null, null); 11  } 12     
13  AnnotationBean(Object object, Object proxy) { 14         this(false, object, proxy); 15  } 16     
17     AnnotationBean(boolean dI, Object object, Object proxy) { 18         DI = dI; 19  setObject(object); 20  setProxy(proxy); 21  } 22     
23  @Override 24     public Object getObject() { 25         return object; 26  } 27     
28  AnnotationBean setObject(Object object) { 29         this.object = object; 30         return this; 31  } 32 
33  AnnotationBean setProxy(Object proxy) { 34         this.proxy = proxy; 35          return this; 36  } 37 
38     void setDI(boolean DI) { 39         this.DI = DI; 40  } 41     
42  @Override 43     public boolean isDI() { 44         return DI; 45  } 46 
47  @Override 48     @SuppressWarnings("unchecked") 49     public <E> E getProxy() { 50         return (E) proxy; 51  } 52     
53 }

MethodBuffer 類以下:

 1 /**
 2  * 帶有@Bean註解的方法封裝類  3  */
 4 public class MethodBuffer {  5     private String id; // bean的id
 6     private Class<?> returnType; // 方法返回值類型
 7     private Object object; // 方法執行所需對象
 8     private Method method; // 方法自己
 9     private Parameter[] parameters; // 方法所需參數
10     
11  MethodBuffer() { 12  } 13 
14     MethodBuffer(Class<?> returnType, Object object, Method method, Parameter[] parameters, String id) { 15         this.returnType = returnType; 16         this.object = object; 17         this.method = method; 18         this.parameters = parameters; 19         this.id = id; 20  } 21 
22  String getId() { 23         return id; 24  } 25     
26  Object getObject() { 27         return object; 28  } 29 
30     Class<?> getReturnType() { 31         return returnType; 32  } 33 
34  Method getMethod() { 35         return method; 36  } 37 
38  Parameter[] getParameters() { 39         return parameters; 40  } 41     
42 }

*在處理@Bean註解時,就能發現我前面提出的問題,用CGLibProxy產生的代理爲何還須要原始對象?
咱們處理@Bean註解,是爲了獲得方法的返回值,若是直接對返回值所對應的類進行代理,產生代理對象,那麼,在方法執行時,若是原始對象的成員被賦值了,那麼代理對象是不會知道的,那麼產生的代理是不完整的!使用methodProxy.invokeSuper(proxyObjet, args)方法是不可行的了!因此必定要保存原始對象,使用method.invoke(object, args)纔是合理的!

AnnotationConfigApplicationContext:
這是註解這一支最高級的容器,是最終留給用戶使用的,用來完成最後@Autowired註解標識的成員的注入!
若是說是須要注入的bean都能找的到,且這些bean都完成了注入,那麼其注入過程會很是簡單,但如果,彼此之間的依賴關係比較複雜,你中有我,我中有你,會造成一個環形閉環,陷入死循環,這就是循環依賴!


循環依賴

 

這裏有四個類ClassA、ClassB、ClassC、ClassD,而且都沒有完成注入,若是如今想getBean獲得ClassA的Bean,那麼就須要先對ClassA的Bean完成注入,可是其注入又須要ClassB,那麼,就須要先將B注入,但B又要C,那就先注入C,但是C須要D,只能先注入D,可是D確須要A!繞了一圈又回來了,陷入了死循環,這就是咱們要解決的循環依賴!


解決方案:
1、前面我說過@Autowired註解能夠給setter方法添加,用來解決循環依賴!
若是說咱們使用這種方式給ClassA的setClassB方法添加@Autowired,而不是給其ClassB成員添加,那麼這個循環天然而然就不會出現!
2、假設自身以完成注入:
在ClassA注入以前,讓它的狀態變爲完成注入,而後繼續找B,發現B沒注入,找C,C沒注入,找D,D沒注入,找A,此時A的狀態是完成注入,天然也就不會產生閉環!

因此AnnotationConfigApplicationContext就是爲了最後的注入:

 1 public class AnnotationConfigApplicationContext extends AnnotationContext {  2     public AnnotationConfigApplicationContext() {  3  }  4     // 調用父類的構造
 5     public AnnotationConfigApplicationContext(String packageName) {  6         super(packageName);  7  }  8     // Advice是代理的攔截處理,內部使用默認的一種方式,用戶也能夠注入一種方式
 9     public AnnotationConfigApplicationContext setAdvice(Advice advice) { 10  aopFactory.setAdvice(advice); 11         return this; 12  } 13     
14     public AnnotationConfigApplicationContext parsePackage(String packageName) { 15  scanPackage(packageName); 16         return this; 17  } 18     
19  @Override 20     public Object getBean(String name) throws BeansException { 21         String className = beanNameMap.get(name); 22         return className == null ? get(name) : get(className); 23  } 24 
25     private <T> T get(String className, Class<?> klass) throws BeansException { 26         BeanElement bean = beanMap.get(className); 27         if (bean == null) { 28             throw new BeansException("Bean :" + klass + "不存在!"); 29  } 30         if (!bean.isDI() && bean instanceof AnnotationBean) { 31  autowired(klass, (AnnotationBean)bean); 32  } 33         return bean.getProxy(); 34  } 35     
36  @Override 37     public <T> T getBean(Class<T> klass) throws BeansException { 38         return get(klass.getName()); 39  } 40     
41     private void autowired(AnnotationBean bean) throws BeansException { 42         // 一開始令自身完成注入
43         bean.setDI(true); 44         Object object = bean.getObject(); 45         Class<?> klass = object.getClass(); 46         Field[] fields = klass.getDeclaredFields(); 47         Object arg = null; 48         for (Field field : fields) { 49             if (field.isAnnotationPresent(Value.class)) { 50                 try { 51                     // 注入基本類型的值
52                     arg = injectValue(field); 53                 } catch (ValueOnlyPrimitiveType e) { 54  e.printStackTrace(); 55  } 56             } else if (field.isAnnotationPresent(Autowired.class)) { 57                 // 注入bean中的Bean
58                 arg = injectBean(field); 59             } else { 60                 continue; 61  } 62             try { 63                 // 成員注入
64                 field.setAccessible(true); 65  field.set(object, arg); 66             } catch (Exception e) { 67                 throw new BeansException(klass + "依賴關係不正確!"); 68  } 69  } 70  } 71     
72     private Object injectValue(Field field) throws ValueOnlyPrimitiveType { 73         Class<?> type = field.getType(); 74         if (!type.isPrimitive() && !type.equals(String.class)) { 75             throw new ValueOnlyPrimitiveType("Value只能用於八大基本類型!"); 76  } 77         Value value = field.getAnnotation(Value.class); 78         return TypeConversion.getValue(value.value(), type.getSimpleName()); 79  } 80     
81     private Object injectBean(Field field) throws BeansException { 82         Class<?> fieldType = field.getType(); 83         BeanElement fieldBean = beanMap.get(fieldType.getName()); 84         if (fieldBean == null) { return null;} 85         if (!fieldBean.isDI() && fieldBean instanceof AnnotationBean) { 86  autowired((AnnotationBean)fieldBean); 87  } 88         return fieldBean.getProxy(); 89  } 90 }

這裏解決循環依賴就使用了我上面給出的第二種方案,利用遞歸來實現!

註解部分的簡單實現已基本完成,雖然有些地方沒有處理或是處理的比較簡陋,可是SpringIOC的核心思想就是如此,只不過Spring實現的更爲精緻、細膩!
來看看它的使用:
先給出幾個須要注入的類:

 1 @Component(id="studentA")  2 public class StudentA {  3     @Value(value="我是A")  4  String name;  5  @Autowired  6     private StudentB B;  7     
 8     public StudentA() {  9  } 10     
11  @Override 12     public String toString() { 13         return "A:" + name + "->" + B; 14  } 15     
16 } 17 
18 @Component 19 public class StudentB { 20     @Value(value="我是B") 21     private String name; 22  @Autowired 23     private StudentC C; 24     
25     public StudentB() { 26  } 27 
28  @Override 29     public String toString() { 30         return "B:" + name + "->" + C; 31  } 32     
33 } 34 
35 @Component 36 public class StudentC { 37     @Value(value="我是C") 38     private String name; 39     
40  @Autowired 41     private StudentA A; 42     
43     public StudentC() { 44  } 45 
46  @Override 47     public String toString() { 48         return "C:" + name; 49  } 50     
51 } 52 
53 public class StudentD { 54     private String name; 55  @Autowired 56     private StudentA A; 57     
58     public StudentD(String name) { 59         this.name = name; 60  } 61 
62  @Override 63     public String toString() { 64         return "D:" + name + "->" + A; 65  } 66     
67 } 68 
69 @Component 70 public class MethodAction { 71     public MethodAction() { 72  } 73 
74     @Bean(id="studentD") 75     public StudentD getStudentD(@Value(value="我是D")String name) { 76         return new StudentD(name); 77  } 78 }

主函數:

1 public static void main(String[] args) throws BeansException { 2     ApplicationContext applicationContext = 
3                 new AnnotationConfigApplicationContext("com.zc.ioc.demo"); 4         StudentD studentD = applicationContext.getBean(StudentD.class); 5  System.out.println(studentD); 6 
7 }

結果是:

或者這樣使用:

1 public static void main(String[] args) throws BeansException { 2     BeanFactory beanFactory = new AnnotationConfigApplicationContext("com.zc.ioc.demo"); 3         StudentD studentD = (StudentD) beanFactory.getBean("studentD"); 4  System.out.println(studentD); 5 }

結果:

有關XML方式以及AOP的實現我會在下一篇給出。

相關文章
相關標籤/搜索