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


在這里我要實現的是Spring的IOC和AOP的核心,而且有關IOC的實現,注解+XML能混合使用!

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

IOC:我們平時在寫Java程序時,總要通過new的方式來產生一個對象,對象的生死存亡是與我們直接掛鈎的,我們需要它時,就new一個,不需要他時就通過GC幫我們回收;控制反轉的意思就是將對象的生死權交給第三方,由第三方來生成和銷毀對象,而我們只在需要時從第三方手中取獲取,其余不管,這樣,對象的控制權就在第三方手里,我們只有使用權!這就是所謂的控制反轉!
在Spring中,提供了XML文件配置和注解的方式來向第三方表明我們需要第三方幫我們創建什么對象,Spring就是這個第三方!它負責通過XML文件的解析或者包掃描的方式,找到我們給出的映射關系,利用反射機制,在其內部幫我們“new”出對象,再存儲起來,供我們使用!

AOP :就是動態代理的體現,在Spring中就是利用JDKProxy或者CGLibProxy技術,對方法進行攔截!
比如說有一個叫做fun()的方法,我們在其執行前后對其攔截:

 

就像這樣,fun看成是縱向的線,那么就相當於用平面把這條線截斷了!

 

有了上面的鋪墊,我們可以大概知道,Sping的IOC和AOP可以幫我們創建並管理對象,可以對對象的方法進行攔截,那么這兩個技術合在一起,就可以達到自動幫我們創建、管理、並對對象進行攔截!

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

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

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

 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的方法)

 

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!繞了一圈又回來了,陷入了死循環,這就是我們要解決的循環依賴!


解決方案:
一、前面我說過@Autowired注解可以給setter方法添加,用來解決循環依賴!
如果說我們使用這種方式給ClassA的setClassB方法添加@Autowired,而不是給其ClassB成員添加,那么這個循環自然而然就不會出現!
二、假設自身以完成注入:
在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的實現我會在下一篇給出。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM