Java高級主題(六)上——Ioc+AOP原理

有了前面關(guān)于反射和動態(tài)代理的基礎(chǔ),理解IOC和AOP就相對簡單了逗威。

一惨缆、概述

  • IOC:控制反轉(zhuǎn)(Inversion of Control,縮寫為IoC)剂桥,是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則忠烛,可以用來減低計(jì)算機(jī)代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection权逗,簡稱DI)美尸,還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉(zhuǎn)斟薇,對象在被創(chuàng)建的時(shí)候师坎,由一個(gè)調(diào)控系統(tǒng)內(nèi)所有對象的外界實(shí)體,將其所依賴的對象的引用傳遞給它堪滨。也可以說胯陋,依賴被注入到對象中。
    我們平時(shí)在寫Java程序時(shí)袱箱,總要通過new的方式來產(chǎn)生一個(gè)對象遏乔,對象的生死存亡是與我們直接掛鉤的,我們需要它時(shí)发笔,就new一個(gè)盟萨,不需要他時(shí)就通過GC幫我們回收;控制反轉(zhuǎn)的意思就是將對象的生死權(quán)交給第三方了讨,由第三方來生成和銷毀對象捻激,而我們只在需要時(shí)從第三方手中取獲取制轰,其余不管,這樣胞谭,對象的控制權(quán)就在第三方手里垃杖,我們只有使用權(quán)!這就是所謂的控制反轉(zhuǎn)丈屹!
    在Spring中缩滨,提供了XML文件配置和注解的方式來向第三方表明我們需要第三方幫我們創(chuàng)建什么對象,Spring就是這個(gè)第三方泉瞻!它負(fù)責(zé)通過XML文件的解析或者包掃描的方式脉漏,找到我們給出的映射關(guān)系,利用反射機(jī)制袖牙,在其內(nèi)部幫我們“new”出對象侧巨,再存儲起來,供我們使用鞭达!
  • AOP:Aspect Oriented Programming的縮寫司忱,意為:面向切面編程,通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)畴蹭。
    就是動態(tài)代理的體現(xiàn)坦仍,在Spring中就是利用JDKProxy或者CGLibProxy技術(shù),對方法進(jìn)行攔截叨襟!
    比如說有一個(gè)叫做fun()的方法繁扎,我們在其執(zhí)行前后對其攔截:


    image.png

    就像這樣,fun看成是縱向的線糊闽,那么就相當(dāng)于用平面把這條線截?cái)嗔耍?br> 有了上面的鋪墊梳玫,我們可以大概知道,Sping的IOC和AOP可以幫我們創(chuàng)建并管理對象右犹,可以對對象的方法進(jìn)行攔截提澎,那么這兩個(gè)技術(shù)合在一起,就可以達(dá)到自動幫我們創(chuàng)建念链、管理盼忌、并對對象進(jìn)行攔截!

二掂墓、Ioc實(shí)現(xiàn)

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

image.png

這是簡化后的IOC框圖谦纱,實(shí)際上的SpringIOC是非常龐大的,里面包含了許多接口梆暮,以及繼承關(guān)系服协,它把要處理的事務(wù)區(qū)分的非常細(xì)膩,將問題由大化小啦粹,層層遞減偿荷,把面向接口,高內(nèi)聚低耦合體現(xiàn)的淋漓盡致唠椭。
Spring提供了注解和Xml方式的注入跳纳,所以后面會有兩個(gè)分支,分別處理注解和XML文件的配置贪嫂!

BeanFactory

在別的地方說法是一個(gè)最底層容器寺庄,其實(shí)不要被其“誤導(dǎo)”,在我這它僅僅只是一個(gè)接口力崇,只提供了最基礎(chǔ)的一些方法斗塘,而方法的具體實(shí)現(xiàn)就需要真正的高級容器了!代碼如下:

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);
}

(這里我直接挪用了Spring的源碼亮靴,由于是模擬實(shí)現(xiàn)馍盟,所以后面只實(shí)現(xiàn)了其getBean的方法)

ApplicationContext

在別的地方的說法是一個(gè)高級容器,其實(shí)茧吊,它還是一個(gè)接口贞岭,只不過在源碼中其繼承了許多接口(核心還是BeanFactory),是一個(gè)集大成者搓侄,提供了遠(yuǎn)比BeanFactory更多的功能瞄桨。但目前所要實(shí)現(xiàn)的核心暫時(shí)用不上它,所以暫時(shí)留一個(gè)空接口吧...

public interface ApplicationContext extends BeanFactory {
     // TODO
}

說到這里讶踪,就不能往下繼續(xù)了芯侥,因?yàn)樵谏厦嫖覀兛吹降模^的“容器”乳讥,僅僅是定義了接口筹麸,完全不能裝東西啊,還有雏婶,所謂的容器里又要裝什么物赶?這里就要引入Bean!

Bean:

其實(shí)就是IOC容器里存放的東西留晚!前面我說過酵紫,Spring會根據(jù)我們給出的映射關(guān)系,幫我們創(chuàng)建對象并存儲起來错维,那么是否這個(gè)對象就是Bean奖地?是!但也不是赋焕!如果說Spring僅僅只是幫我們管理對象参歹,那么它的功能也太單一了,那么隆判,現(xiàn)在不得不再次提到前面說過的AOP犬庇!
前面說到Spring中的AOP使用了JDKProxy和CGLibProxy這兩種代理技術(shù)僧界,這兩種技術(shù)的目的都是產(chǎn)生代理對象,對方法進(jìn)行攔截臭挽。那么捂襟,是否這個(gè)代理對象就是我們的Bean?不完全是欢峰!Bean其實(shí)是原對象+代理對象葬荷!先不急,看到后面就會明白纽帖!

下面介紹兩種動態(tài)代理技術(shù):
JDKProxy:
使用JDK代理宠漩,其所代理的類,必須要實(shí)現(xiàn)某一個(gè)接口:

@SuppressWarnings("unchecked")
public <E> E getJDKProxy(Class<?> klass, Object tagObject) {
    return (E) Proxy.newProxyInstance(klass.getClassLoader(), 
            klass.getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) 
                        throws Throwable {
                    // TODO 置前攔截懊直,可對參數(shù)args進(jìn)行判斷
                    Object result = null;
                    try {
                        result = method.invoke(tagObject, args);
                    } catch (Exception e) {
                        // TODO 對異常進(jìn)行攔截
                        throw e;
                    }
                    // TODO 置后攔截扒吁,可對方法返回值進(jìn)行修改
                    return result;
                }
            });
}

使用JDK代理的話就不得不傳入一個(gè)原始對象,所以如果不考慮CGLib代理吹截,那么Bean就是原始對象+代理對象瘦陈!

CGLibProxy
使用CGLib代理,是讓被代理的類作為代理對象的父類波俄,故原類不能被final修飾晨逝,也不能對final修飾的方法攔截!

以下是網(wǎng)上絕大多數(shù)人給出的用法:

@SuppressWarnings("unchecked")
public <E> E getCGLibProxy(Class<?> klass) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(klass); // 從這里可以明顯看到懦铺,讓被代理的類作為了代理對象的父類
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy) 
                throws Throwable {
            // TODO 置前攔截捉貌,可對參數(shù)args進(jìn)行判斷
            Object result = null;
            try {
                result = methodProxy.invokeSuper(proxyObject, args);
            } catch (Exception e) {
                // TODO 對異常進(jìn)行攔截
                throw e;
            }
            // TODO 置后攔截,可對方法返回值進(jìn)行修改
            return result;
        }
    });
    return (E) enhancer.create();
}

這種方式是沒錯(cuò)冬念,但是不適用于后面要做的趁窃,至于原因,后面分析到了就會明白急前!
所以使用如下方式:

@SuppressWarnings("unchecked")
public <E> E getCGLibProxy(Class<?> klass, Object tagObject) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(klass);
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object proxyObject, Method method, Object[] args, MethodProxy methodProxy) 
                throws Throwable {
            // TODO 置前攔截醒陆,可對參數(shù)args進(jìn)行判斷
            Object result = null;
            try {
                result = method.invoke(tagObject, args);
            } catch (Exception e) {
                // TODO 對異常進(jìn)行攔截
                throw e;
            }
            // TODO 置后攔截,可對方法返回值進(jìn)行修改
            return result;
        }
    });
    return (E) enhancer.create();
}

由于是模擬實(shí)現(xiàn)裆针,后面就全部采用CGLib代理刨摩!
可以看到以上面這種方式進(jìn)行CGLib代理就需要原始對象,那么前面說到的Bean就必須是原對象+代理對象世吨!
當(dāng)然我知道以invokeSuper那種方式是不需要原始對象澡刹,但是原因不是因?yàn)锽ean,還在后面耘婚!
綜上罢浇,Bean的定義如下:

public interface BeanElement {
     <E> E getProxy();
     Object getObject();
     boolean isDI(); // 用于以后判斷是否完成注入
}

在這里我把BeanElement定義為了一個(gè)接口,后面會產(chǎn)生兩個(gè)分支,會產(chǎn)生兩種不同處理方式的Bean嚷闭,用接口更靈活攒岛,耦合也低!

現(xiàn)在知道了Bean到底是什么凌受,我們就可以往下繼續(xù)進(jìn)行:

AbstractApplicationContext

ApplicationContext的具體實(shí)現(xiàn)類阵子,但它是一個(gè)抽象類思杯,只能實(shí)現(xiàn)部分功能胜蛉,往后在它的基礎(chǔ)上還要分化成兩支,那么色乾,把所有的Bean存在這里面再合適不過了誊册!

public abstract class AbstractApplicationContext implements ApplicationContext {
    protected static final Map<String, String> beanNameMap;  // key : id      value : className
    protected static final Map<String, BeanElement> beanMap; // key : className value : Bean
    protected AopFactory aopFactory; // Aop工廠,生產(chǎn)代理對象
    
    static {
        beanNameMap = new HashMap<>();
        beanMap = new HashMap<>();
    }
    
    protected AbstractApplicationContext() {
        aopFactory = new AopFactory();
        // 設(shè)置切面
        aopFactory.setAdvice(
                (new AdviceHander())
                .setIntercepterFactory(new IntercepterLoader()));
    }
    
    protected void add(String id, String className, BeanElement bean) throws BeansException {
        if (beanMap.containsKey(className)) {
            // TODO 可以拋異常!
            return;
        }
        beanMap.put(className, bean);
        if (id.length() <= 0) return;
        if (beanNameMap.containsKey(id)) {
            throw new BeansException("bean:" + id + "已定義!");
        }
        beanNameMap.put(id, className);
    }
}

其中的aopFactory是代理工廠暖璧,負(fù)責(zé)生產(chǎn)代理案怯,會在后面給出,先不急澎办。
可以看到嘲碱,AbstractApplicationContext這個(gè)類持有了兩張靜態(tài)的map,第一組是用來存取Bean的別名(id)局蚀,第二組用來存放真正的Bean麦锯,這就是我們真正意義上的容器,由于其map都是static修飾的琅绅,在類加載的時(shí)候就存在了扶欣,所以往后有別的類繼承它時(shí),這兩個(gè)map是共享的千扶!只增加了一個(gè)add方法料祠,只要是繼承自它的子類,都會通過這種方式添加Bean澎羞!并且這個(gè)類是protected的髓绽,不對外提供使用!

我們先進(jìn)行左邊的分支妆绞,用注解的方式完成IOC
這里說的注解都是自定義注解顺呕,屬于RUNTIME,就必須通過反射機(jī)制來獲取摆碉,反射機(jī)制就要得到類或者類名稱塘匣,那么就先得到符合要求的類,這里就要用到包掃描巷帝,我在前面的博客中有介紹過:【Java】包忌卤、jar包的掃描
首先是對類的注解:

@Component

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

其中的id相當(dāng)于類名稱的別名,具有唯一性楞泼,如果不設(shè)置則不處理驰徊!通過這個(gè)注解我們就可以判斷哪個(gè)類是需要進(jìn)行操作的笤闯,就應(yīng)該自動地為這個(gè)類生成一個(gè)對象和代理對象,將其添加到beanMap中棍厂,就是bean的標(biāo)志颗味!
如果說用過Spring的XML配置,這其實(shí)就相當(dāng)于一個(gè)bean標(biāo)簽:

<bean id="XXX" class="XXX">
     ......
</bean>

注解中的id就相當(dāng)于id屬性牺弹,我們是通過反射機(jī)制得到注解的浦马,當(dāng)然能得到類名稱,那就相當(dāng)于有了class屬性张漂!

但是僅僅有這個(gè)注解是完全不夠的晶默,我們只能通過反射機(jī)制產(chǎn)生一個(gè)對象,但它的成員都沒賦值航攒,僅僅是一具軀殼磺陡,所以就需要對成員添加注解,將我們需要的值注入進(jìn)來漠畜;當(dāng)然也可以給方法添加注解币他,通過setter方法給成員賦值,Spring就是使用的這種方式憔狞!
這是對成員的注解:
@Autowired

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

這里直接使用的spring源碼蝴悉,在源碼中,這個(gè)注解可以成員躯喇、方法以及構(gòu)造方法添加辫封。其中的requisite是是否必須注入,這是Spring提供的更為細(xì)膩的操作廉丽,我的實(shí)現(xiàn)暫時(shí)不考慮它倦微。

如果說這個(gè)注解是給成員添加的,那么標(biāo)志著它需要賦值正压!使用這個(gè)注解的前提是它本身是一個(gè)復(fù)雜類型欣福,不是基本類型,它的賦值焦履,是將我們beanMap中的符合要求的Bean注入進(jìn)去拓劝!至于基本類型后面有別的解決辦法。
用Component 和Autowired注解其實(shí)就相當(dāng)于如下XML的配置:

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

我們同樣是通過反射機(jī)制得到的Autowired注解嘉裤,那么一定可以得到成員名稱郑临,和成員類型,成員名稱就相當(dāng)于name屬性屑宠,通過成員類型就可以得到類型名稱厢洞,就相當(dāng)于ref屬性!

如果說是給構(gòu)造方法添加,那么就規(guī)定了我們在反射機(jī)制執(zhí)行時(shí)需要調(diào)用哪個(gè)構(gòu)造方法躺翻,相當(dāng)于如下:

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

對于構(gòu)造方法的處理我覺得使用注解的方式比XML配置要更好丧叽,注解可以直接定位到某一個(gè)構(gòu)造方法,但是XML文件的方式就需要遍歷比較公你,找出符合要求的踊淳,而且關(guān)于這個(gè)符合要求這一說還有更為復(fù)雜的問題,我會在后面用XML的方式詳細(xì)介紹陕靠!

還有一種就是對方法添加迂尝,實(shí)際上就是提供給setter方法使用的,通過執(zhí)行setter方法給成員賦值懦傍,可能看到這里會覺得這樣做是不是多此一舉雹舀,其實(shí)不是芦劣,因?yàn)檫@樣做會避免我后面會談到的循環(huán)依賴的問題粗俱!所以給方法添加,和對成員添加等效:

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

上面我說過使用Autowired 是不處理基本類型的虚吟,它是將bean注入的寸认,基本類型完全沒有必要作為bean,那么串慰,我們就可以給出一個(gè)注解偏塞,直接賦值:

@Value

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

其中value就是我們要注入的值,但是它是String類型的邦鲫,可能需要強(qiáng)制類型轉(zhuǎn)換
演示如下:

@Component
public class TestA {
    @Autowired
    private TestB testB;
    @Value(value="直接賦值")
    private String member;
    
    public TestA() {
    }

}

@Component(id = "testB")
public class TestB {
    private int a;
    private TestA testA;
    
    @Autowired
    public TestB(@Value(value="10")int a, TestA testA) {
        this.a = a;
        this.testA = testA;
    }
    
}

就相當(dāng)于:

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

為了簡單處理灸叼,Autowired注解我在后面就只處理成員的。

有了上面的兩個(gè)注解是否夠呢庆捺?當(dāng)然不夠古今。仔細(xì)想一想,如果說我們需要Spring幫我們創(chuàng)建的對象滔以,其對應(yīng)的類又被打成了jar包捉腥,那么我們完全沒有辦法對已經(jīng)形成jar包的代碼添加注解;又或者說是我們需要創(chuàng)建的對象不是通過反射機(jī)制就能產(chǎn)生的你画,它是一個(gè)工廠對象抵碟,需要走工廠模式那一套來創(chuàng)建,上面的兩個(gè)注解就遠(yuǎn)遠(yuǎn)不能滿足我們的要求了坏匪!因此拟逮,我們還需要一個(gè)作為補(bǔ)充的注解:

@Bean

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

可以看出這是對方法的注解,因?yàn)槲覀兛梢酝ㄟ^反射機(jī)制執(zhí)行方法适滓,將方法的返回值作為Bean的原始對象敦迄,再產(chǎn)生一個(gè)代理對象,這樣就能解決上面的所說的問題!

@Component
public class Action {
    
    @Bean(id="getDocumentBuilderFactory")
    public DocumentBuilderFactory getDocumnet() throws Exception {
        return DocumentBuilderFactory.newInstance();
    }
    
    @Bean
    public DocumentBuilder getDocumentBuilder(DocumentBuilderFactory factory) throws Exception {
        return factory.newDocumentBuilder();
    }
    
}

就相當(dāng)于:

<bean class="xxx.xxx.Action "></bean>
<bean class="javax.xml.parsers.DocumentBuilderFactory" id="getDocumentBuilderFactory" 
    factory-method="newInstance"></bean>
<bean class="javax.xml.parsers.DocumentBuilderFactory" 
    factory-bean="getDocumentBuilderFactory" factory-method="newDocumentBuilder">
</bean>

有了這些注解颅崩,我們就能對包進(jìn)行掃描几于,可以繼續(xù)向下進(jìn)行!

AnnotationContext

這個(gè)類繼承自AbstractApplicationContext沿后,它是protected的沿彭,不對外提供,我用它來進(jìn)行有關(guān)注解的掃描和解析尖滚,但它的功能有限喉刘,不能完成全部的注入,這會涉及到注入的順序,以及注入之間的依賴關(guān)系:

/**
 * 執(zhí)行包掃描
 * 將符合要求的結(jié)果添加到父類AbstractApplicationContext的beanMap中
 * 只處理@Component和@Bean注解
 * @Autowired注解留給下一級處理
 */
public class AnnotationContext extends AbstractApplicationContext {
    // method緩沖區(qū)恩溅,保存暫時(shí)不能執(zhí)行的方法披坏,其中的MethodBuffer用來封裝method,以及invoke時(shí)所需要的內(nèi)容
    private List<MethodBuffer> methodList; 

    protected AnnotationContext() {
    }
    
    protected AnnotationContext(String packageName) {
        scanPackage(packageName);
    }
    
    /**
     * 通過aopFactory產(chǎn)生代理對象廉邑,將代理對象和原始對象封裝成bean添加到父類的map中
     */
    private void addBean(Class<?> klass, Object object, String id, String className) {
        // aopFactory是其父類AbstractApplicationContext的成員,原來產(chǎn)生代理對象
        Object proxy = aopFactory.creatCGLibProxy(klass, object);
        // AnnoationBean是BeanElement接口的一個(gè)實(shí)現(xiàn)類
        AnnotationBean bean = new AnnotationBean(object, proxy);
        // 父類AbstractApplicationContext的add方法
        add(id, className, bean);
    }
    
    protected void scanPackage(String packageName) {
        new PackageScanner() {
            @Override
            public void dealClass(Class<?> klass) {
                if (!klass.isAnnotationPresent(Component.class)) return;
                Component component = klass.getAnnotation(Component.class);
                String className = klass.getName();
                String name = component.id();
                try {
                    // 這里簡單起見倒谷,不考慮構(gòu)造方法的重載蛛蒙,默認(rèn)執(zhí)行無參構(gòu)造 
                    Object object = klass.newInstance();
                    // 產(chǎn)生BeanElement,加入到beanMap中
                    addBean(klass, object, name, className);
                    // 處理帶有@Bean注解的方法
                    dealMethod(klass, object);
                } catch (Exception e) {
                    // TODO 
                    e.printStackTrace();
                }
            }
        }.scanPackage(packageName);
        if (methodList == null) return;
        // 執(zhí)行緩存的所有方法
        for (MethodBuffer methodBuffer : methodList) {
            // 獲得方法執(zhí)行所需要的東西
            String id = methodBuffer.getId();
            Class<?> returnClass = methodBuffer.getReturnClass();
            Method method = methodBuffer.getMethod();
            Object object = methodBuffer.getObject();
            Parameter[] parameters = methodBuffer.getParameters();
            
            try {
                dealMultiParaMethod(returnClass, method, object, parameters, id);
            } catch (Exception e) {
                // TODO 
                e.printStackTrace();
            } 
        }
        methodList.clear();
    }
    
    private void dealMultiParaMethod(Class<?> returnClass, Method method, 
            Object object, Parameter[] parameters, String id) 
                    throws BeansException, IllegalAccessException, IllegalArgumentException, 
                    InvocationTargetException, ValueOnlyPrimitiveType {
        int count = parameters.length;
        Object[] result = new Object[count];
        
        for (int index = 0; index < count; index++) {
            Parameter para = parameters[index];
            // 判斷參數(shù)是否帶有@Value注解
            if (para.isAnnotationPresent(Value.class)) { 
                Class<?> type = para.getType();
                // 判斷@Value注解標(biāo)識的參數(shù)是否是基本類型(八大類型和String)
                if (!type.isPrimitive() && !type.equals(String.class)) {
                    throw new ValueOnlyPrimitiveType("Value只能用基本類型渤愁!");
                }
                // TypeConversion是我自己的一個(gè)工具類牵祟,用于將字符串轉(zhuǎn)換成基本類型!
                result[index] = TypeConversion.getValue(para.getAnnotation(Value.class).value(), 
                                            para.getType().getSimpleName());
            } else {
                // 如果不是基本類型抖格,那么就需要從beanMap中獲取符合要求的bean
                result[index] = getBean(para.getType());
            }
        }
        // 執(zhí)行方法诺苹,獲得方法返回值
        Object returnObject = method.invoke(object, result);
        // 為方法執(zhí)行結(jié)果創(chuàng)建bean,添加到beanMap中
        addBean(returnClass, returnObject , id, returnClass.getName());
    }
    
    /**
     * 遍歷所有方法雹拄,處理帶有@Bean注解的方法
     */
    private void dealMethod(Class<?> klass, Object object) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method[] methods = klass.getDeclaredMethods();
        for (Method method : methods) {
            if (!method.isAnnotationPresent(Bean.class)) continue; 
            
            Class<?> returnType = method.getReturnType();
            if (returnType.equals(void.class)) {
                // TODO 如果沒有返回值收奔,那么根本沒有必要做
                return;
            }
            String id= method.getAnnotation(Bean.class).id();
            Parameter[] parameters = method.getParameters();
            // 判斷方法是否有參數(shù),沒有參數(shù)办桨,直接執(zhí)行筹淫,添加Bean
            // 有參數(shù)就先緩存起來,等所有都掃描完成后再執(zhí)行
            if (parameters.length <= 0) {
                Object returnObject = method.invoke(object);
                addBean(returnType, returnObject, id, returnType.getName());
            } else {
                (methodList = methodList == null ? new ArrayList<>() : methodList)
                    .add(new MethodBuffer(returnType, object, method, parameters, id));
            }
        }
    }
    
}

這個(gè)類只負(fù)責(zé)掃描包呢撞,為帶有@Component注解的類通過反射機(jī)制生成對象损姜,再通過代理工廠將其加工成代理對象,然后封裝再AnnotationBean中作為bean殊霞,將其添加到BeanMap中摧阅!
其次還處理了帶有@Bean注解的的方法,如果說是方法帶有參數(shù)绷蹲,那么就像將這個(gè)方法的執(zhí)行延后棒卷,等所有東西都掃描完成后再執(zhí)行顾孽;而對于無參的方法,則可以直接執(zhí)行比规,并為執(zhí)行結(jié)果產(chǎn)生的對象創(chuàng)建代理若厚,生成AnnotationBean,添加進(jìn)beanMap中蜒什。至于@Autowired注解這個(gè)類暫不處理测秸,留給下一級處理!

AnnotationBean類的定義如下:

/**
 * 注解分支中BeanElement的具體實(shí)現(xiàn)類
 */
public class AnnotationBean implements BeanElement {
    private boolean DI; // 判斷是否完成注入
    private Object object; // 原始對象
    private Object proxy; // 代理對象
    
    AnnotationBean() {
        this(false, null, null);
    }
    
    AnnotationBean(Object object, Object proxy) {
        this(false, object, proxy);
    }
    
    AnnotationBean(boolean dI, Object object, Object proxy) {
        DI = dI;
        setObject(object);
        setProxy(proxy);
    }
    
    @Override
    public Object getObject() {
        return object;
    }
    
    AnnotationBean setObject(Object object) {
        this.object = object;
        return this;
    }

    AnnotationBean setProxy(Object proxy) {
        this.proxy = proxy;
         return this;
    }

    void setDI(boolean DI) {
        this.DI = DI;
    }
    
    @Override
    public boolean isDI() {
        return DI;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <E> E getProxy() {
        return (E) proxy;
    }
    
}

MethodBuffer 類如下:

/**
 *    帶有@Bean注解的方法封裝類
 */
public class MethodBuffer {
    private String id; // bean的id
    private Class<?> returnType; // 方法返回值類型
    private Object object; // 方法執(zhí)行所需對象
    private Method method; // 方法本身
    private Parameter[] parameters; // 方法所需參數(shù)
    
    MethodBuffer() {
    }

    MethodBuffer(Class<?> returnType, Object object, Method method, Parameter[] parameters, String id) {
        this.returnType = returnType;
        this.object = object;
        this.method = method;
        this.parameters = parameters;
        this.id = id;
    }

    String getId() {
        return id;
    }
    
    Object getObject() {
        return object;
    }

    Class<?> getReturnType() {
        return returnType;
    }

    Method getMethod() {
        return method;
    }

    Parameter[] getParameters() {
        return parameters;
    }
    
}

*在處理@Bean注解時(shí)灾常,就能發(fā)現(xiàn)我前面提出的問題霎冯,用CGLibProxy產(chǎn)生的代理為什么還需要原始對象?
我們處理@Bean注解钞瀑,是為了得到方法的返回值沈撞,如果直接對返回值所對應(yīng)的類進(jìn)行代理,產(chǎn)生代理對象雕什,那么缠俺,在方法執(zhí)行時(shí),如果原始對象的成員被賦值了监徘,那么代理對象是不會知道的晋修,那么產(chǎn)生的代理是不完整的!使用methodProxy.invokeSuper(proxyObjet, args)方法是不可行的了凰盔!所以一定要保存原始對象,使用method.invoke(object, args)才是合理的倦春!

AnnotationConfigApplicationContext

這是注解這一支最高級的容器户敬,是最終留給用戶使用的,用來完成最后@Autowired注解標(biāo)識的成員的注入睁本!
如果說是需要注入的bean都能找的到尿庐,且這些bean都完成了注入,那么其注入過程會非常簡單呢堰,但若是抄瑟,彼此之間的依賴關(guān)系比較復(fù)雜,你中有我枉疼,我中有你皮假,會形成一個(gè)環(huán)形閉環(huán),陷入死循環(huán)骂维,這就是循環(huán)依賴惹资!
循環(huán)依賴


image.png

這里有四個(gè)類ClassA、ClassB航闺、ClassC褪测、ClassD猴誊,并且都沒有完成注入,如果現(xiàn)在想getBean得到ClassA的Bean侮措,那么就需要先對ClassA的Bean完成注入懈叹,但是其注入又需要ClassB,那么分扎,就需要先將B注入项阴,但B又要C,那就先注入C笆包,可是C需要D环揽,只能先注入D,但是D確需要A庵佣!繞了一圈又回來了歉胶,陷入了死循環(huán),這就是我們要解決的循環(huán)依賴巴粪!

解決方案:
一通今、前面我說過@Autowired注解可以給setter方法添加,用來解決循環(huán)依賴肛根!
如果說我們使用這種方式給ClassA的setClassB方法添加@Autowired辫塌,而不是給其ClassB成員添加,那么這個(gè)循環(huán)自然而然就不會出現(xiàn)派哲!
二臼氨、假設(shè)自身以完成注入:
在ClassA注入之前,讓它的狀態(tài)變?yōu)橥瓿勺⑷氚沤欤缓罄^續(xù)找B储矩,發(fā)現(xiàn)B沒注入,找C褂乍,C沒注入持隧,找D,D沒注入逃片,找A屡拨,此時(shí)A的狀態(tài)是完成注入,自然也就不會產(chǎn)生閉環(huán)褥实!


image.png

所以AnnotationConfigApplicationContext就是為了最后的注入:

public class AnnotationConfigApplicationContext extends AnnotationContext {
    public AnnotationConfigApplicationContext() {
    }
    // 調(diào)用父類的構(gòu)造
    public AnnotationConfigApplicationContext(String packageName) {
        super(packageName);
    }
    // Advice是代理的攔截處理呀狼,內(nèi)部使用默認(rèn)的一種方式,用戶也可以注入一種方式
    public AnnotationConfigApplicationContext setAdvice(Advice advice) {
        aopFactory.setAdvice(advice);
        return this;
    }
    
    public AnnotationConfigApplicationContext parsePackage(String packageName) {
        scanPackage(packageName);
        return this;
    }
    
    @Override
    public Object getBean(String name) throws BeansException {
        String className = beanNameMap.get(name);
        return className == null ? get(name) : get(className);
    }

    private <T> T get(String className, Class<?> klass) throws BeansException {
        BeanElement bean =  beanMap.get(className);
        if (bean == null) {
            throw new BeansException("Bean :" + klass + "不存在性锭!");
        }
        if (!bean.isDI() && bean instanceof AnnotationBean) {
            autowired(klass, (AnnotationBean)bean);
        }
        return bean.getProxy();
    }
    
    @Override
    public <T> T getBean(Class<T> klass) throws BeansException {
        return get(klass.getName());
    }
    
    private void autowired(AnnotationBean bean) throws BeansException {
        // 一開始令自身完成注入
        bean.setDI(true);
        Object object = bean.getObject();
        Class<?> klass = object.getClass();    
        Field[] fields = klass.getDeclaredFields();
        Object arg = null;
        for (Field field : fields) {
            if (field.isAnnotationPresent(Value.class)) {
                try {
                    // 注入基本類型的值
                    arg = injectValue(field);
                } catch (ValueOnlyPrimitiveType e) {
                    e.printStackTrace();
                }
            } else if (field.isAnnotationPresent(Autowired.class)) {
                // 注入bean中的Bean
                arg = injectBean(field);
            } else {
                continue;
            }
            try {
                // 成員注入
                field.setAccessible(true);
                field.set(object, arg);
            } catch (Exception e) {
                throw new BeansException(klass + "依賴關(guān)系不正確赠潦!");
            }
        }
    }
    
    private Object injectValue(Field field) throws ValueOnlyPrimitiveType {
        Class<?> type = field.getType();
        if (!type.isPrimitive() && !type.equals(String.class)) {
            throw new ValueOnlyPrimitiveType("Value只能用于八大基本類型!");
        }
        Value value = field.getAnnotation(Value.class);
        return TypeConversion.getValue(value.value(), type.getSimpleName());
    }
    
    private Object injectBean(Field field) throws BeansException {
        Class<?> fieldType = field.getType();
        BeanElement fieldBean =  beanMap.get(fieldType.getName());
        if (fieldBean == null) { return null;}
        if (!fieldBean.isDI() && fieldBean instanceof AnnotationBean) {
            autowired((AnnotationBean)fieldBean);
        }
        return fieldBean.getProxy();
    }
}

這里解決循環(huán)依賴就使用了我上面給出的第二種方案草冈,利用遞歸來實(shí)現(xiàn)她奥!

注解部分的簡單實(shí)現(xiàn)已基本完成瓮增,雖然有些地方?jīng)]有處理或是處理的比較簡陋,但是SpringIOC的核心思想就是如此哩俭,只不過Spring實(shí)現(xiàn)的更為精致绷跑、細(xì)膩!
來看看它的使用:
先給出幾個(gè)需要注入的類:

@Component(id="studentA")
public class StudentA {
    @Value(value="我是A")
    String name;
    @Autowired
    private StudentB B;
    
    public StudentA() {
    }
    
    @Override
    public String toString() {
        return "A:" + name + "->" +  B;
    }
    
}

@Component
public class StudentB {
    @Value(value="我是B")
    private String name;
    @Autowired
    private StudentC C;
    
    public StudentB() {
    }

    @Override
    public String toString() {
        return "B:" + name + "->" + C;
    }
    
}

@Component
public class StudentC {
    @Value(value="我是C")
    private String name;
    
    @Autowired
    private StudentA A;
    
    public StudentC() {
    }

    @Override
    public String toString() {
        return "C:" + name;
    }
    
}

public class StudentD {
    private String name;
    @Autowired
    private StudentA A;
    
    public StudentD(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "D:" + name + "->" + A;
    }
    
}

@Component
public class MethodAction {
    public MethodAction() {
    }

    @Bean(id="studentD")
    public StudentD getStudentD(@Value(value="我是D")String name) {
        return new StudentD(name);
    }
}

主函數(shù):

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

}

結(jié)果是:


image.png

或者這樣使用:

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

結(jié)果是:


image.png

注解的方式實(shí)現(xiàn)了左邊的分支凡资,那么就剩下右邊的XML分支:
XmlContext:
這個(gè)類是也是AbstractApplicationContext的子類砸捏,和AnnotationContext相似,只不過這里是要解析XML文件而不是注解:
(關(guān)于XML文件的解析之前給過一篇博客:【Java】XML文件的解析
對于XML文件的處理隙赁,是不太容易的垦藏,會產(chǎn)生很多問題,后面只是實(shí)現(xiàn)核心步驟伞访,很多屬性就不考慮了掂骏!
首先給出XmlBean,和AnnotationBean一樣厚掷,都是繼承自BeanElement

public class XmlBean implements BeanElement {
    private boolean DI;
    private Object object;
    private Object proxy;
    private Map<Field, String> wiredMap; 
    // key:object的為注入成員  value:依賴的className
    // 將不能注入的成員先保存起來
    
    protected XmlBean() {
        this(true, null, null);
    }

    protected XmlBean(Object object, Object proxy) {
        this(true, object, proxy);
    }

    protected XmlBean(boolean dI, Object object, Object proxy) {
        DI = dI;
        this.object = object;
        this.proxy = proxy;
    }

    protected void addWiredElement(Field field, String ref) throws RepeatProperty {
        if (wiredMap == null) {
            wiredMap = new HashMap<>();
        }
        if (wiredMap.containsKey(field)) {
            throw new RepeatProperty(object.getClass() + "成員:" + field.getName() + "已定義弟灼!");
        }
        wiredMap.put(field, ref);
    }
    
    protected void setDI(boolean DI) {
        this.DI = DI;
    }

    protected Map<Field, String> getWiredMap() {
        return wiredMap;
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public <E> E getProxy() {
        return (E) proxy;
    }

    @Override
    public Object getObject() {
        return object;
    }

    @Override
    public boolean isDI() {
        return DI;
    }

}

XmlContext:

public class XmlContext extends AbstractApplicationContext {
    protected XmlContext() {
    }
    
    protected XmlContext(String xmlPath) {
        innerParseXml(xmlPath);
    }
    
    // 和注解方式中的做法一樣,只不過產(chǎn)生的是XML方式的BeanElement
    private XmlBean addXmlBean(Class<?> klass, Object object, String classId, String className) throws BeansException {
        Object proxy = aopFactory.creatCGLibProxy(klass, object);
        XmlBean bean = new XmlBean(object, proxy);
        add(classId, className, bean);
        return bean;
    }

    protected void innerParseXml(String xmlPath) {
        // 找到根標(biāo)簽 
        new XMLReader() {
            @Override
            public void dealElment(Element element, int index) {
                // 處理bean標(biāo)簽
                new XMLReader() {
                    @Override
                    public void dealElment(Element element, int index) {
                        // 得到id屬性和class屬性的值
                        String classId = element.getAttribute("id");
                        String className = element.getAttribute("class");
                        try {
                            // 由class得到類
                            Class<?> klass = Class.forName(className);
                            // 處理constructor標(biāo)簽
                            new XMLReader() {
                                @Override
                                public void dealElment(Element element, int index) {
                                // TODO 處理有參數(shù)的構(gòu)造方法冒黑,這里就會遇到許多問題田绑,在這里我就不處理了,后面會給出解決思路
                                }
                            }.parse(element, "constructor-arg");
                            // 由于上面沒有處理帶參數(shù)的構(gòu)造方法抡爹,這里直接通過反射機(jī)制調(diào)用無參構(gòu)造產(chǎn)生對象
                            // 并且利用產(chǎn)生的對象生成代理對象掩驱,最后得到Bean放入beanMap中
                            Object object = klass.newInstance();
                            XmlBean bean = addXmlBean(klass, object, classId, className);
                            
                            // 處理property標(biāo)簽
                            new XMLReader() {
                                @Override
                                public void dealElment(Element element, int index) {
                                    try {
                                        dealProperty(element, klass, bean);
                                    } catch (XmlPropertyMustNeedNameException e) {
                                        e.printStackTrace();
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }.parse(element, "property");
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    }
                }.parse(element, "bean");
            }
        }.parse(XMLReader.openDocument(xmlPath), "SimpleSpring");
    }
    
    private void dealProperty(Element element, Class<?> klass, XmlBean bean) 
            throws XmlPropertyMustNeedNameException, Exception {
        // 得到property標(biāo)簽name屬性的值
        String fieldName = element.getAttribute("name");
        if (fieldName.length() <= 0) {
            throw new XmlPropertyMustNeedNameException("Bean" + klass.getName() + "的Property標(biāo)簽必須聲明name屬性!");
        }
        // 通過反射機(jī)制得到成員
        Field field = klass.getDeclaredField(fieldName);
        // 得到該成員的類型
        Class<?> fieldType = field.getType();
        // 得到value屬性
        String value = element.getAttribute("value");
        // 得到ref屬性
        String ref = element.getAttribute("ref");
        
        // 判斷ref和value是否同時(shí)存在 
        if (value.length() > 0 && ref.length() > 0) {
            throw new CanNotJudgeParameterException("value:" + value + " ref:" + ref + "只能存在一個(gè)豁延!");
        }
        Object arg = null;
        // value存在昙篙,則直接通過類型轉(zhuǎn)換給成員賦值
        if (value.length() > 0) {
            if (!fieldType.isPrimitive() && !fieldType.equals(String.class)) {
                throw new ValueOnlyPrimitiveType("Value只能用于八大基本類型!");
            }
            // TypeConversion是我自己寫的诱咏,將字符串轉(zhuǎn)換為基本類型的工具
            arg = TypeConversion.getValue(value, fieldType.getSimpleName());
            field.setAccessible(true);
            field.set(bean.getObject(), arg);
        }
        if (ref.length() > 0) {
            // ref屬性存在,由于存在相互依賴關(guān)系缴挖,所以現(xiàn)在不做處理袋狞,只是將其保存起來
            // 設(shè)置該bean的狀態(tài)為尚未注入
            bean.setDI(false);
            bean.addWiredElement(field, ref);
        }
    }

}

XmlContext能做的工作也十分有限,只能完成簡單的注入映屋,剩下的注入工作留給下一級處理苟鸯!

在這里之所以沒有處理constructor標(biāo)簽,是因?yàn)閷εc構(gòu)造方法的處理存在許多因素:
比如:

public class Test {
    public Test(String one, int two) {
        ......
    }
    public Test(int two, String one) {
        ......
    }
}

通過XML文件讀取出來的都是字符串棚点,如何區(qū)分它是字符串“123”早处,而不是int類型123?這兩個(gè)構(gòu)造方法到底執(zhí)行哪個(gè)瘫析?
再比如說:

public Test(int one, int two, Student student) {
        ......
    }
    
    public Test(String one, int two, Student student) {
        ......
    }

    public Test(int two, String one, Student student) {
        ......
    }

通過反射機(jī)制砌梆,我們就需要得到構(gòu)造方法的集合getConstructors()默责;然后篩選出參數(shù)個(gè)數(shù)符合要求的子集,再遍歷這個(gè)子集的每一個(gè)構(gòu)造方法咸包,然后遍歷當(dāng)前構(gòu)造方法的所有參數(shù)桃序,一個(gè)一個(gè)比對參數(shù)類型是否符合要求,直到找到符合要求的那一個(gè)為止烂瘫,但是媒熊,如果說我們是想執(zhí)行第三個(gè)構(gòu)造方法,它卻找到的是第一個(gè)坟比,完全就出問題了芦鳍!
所以Spring的解決辦法是給出一個(gè)type屬性

<bean id="xxx" class="xxx.xxx.Test">
    <constructor-arg idnex="0" value="1" type="int.class">
    <constructor-arg idnex="1" value="2" type="java.lang.String">
    <constructor-arg idnex="2" ref="student">
</bean>

只有這樣做才能真真區(qū)分,所以以后在使用Spring的constructor標(biāo)簽時(shí)葛账,當(dāng)構(gòu)造方法有歧義時(shí)柠衅,一定要給出type屬性,避免出錯(cuò)注竿,也減少了查找時(shí)的遍歷茄茁!

接下來就是最后一個(gè)類,xml分支的最高容器:

ClassPathXmlApplicationContext

上面的XmlContext只是完成了基本的注入問題巩割,還有后續(xù)有關(guān)于注入之間的依賴關(guān)系裙顽,甚至是依賴循環(huán)(關(guān)于依賴循環(huán)在我的上一篇中有專門介紹,這里就不再介紹了)

public class ClassPathXmlApplicationContext extends XmlContext {
    public ClassPathXmlApplicationContext() {
    }
    
    public ClassPathXmlApplicationContext(String xmlPath) {
        super(xmlPath);
    }
    
    public ClassPathXmlApplicationContext parseXml(String xmlPath) {
        innerParseXml(xmlPath);
        return this;
    }
    
    @Override
    public <T> T getBean(Class<T> klass) throws BeansException {
        String className = klass.getName();
        BeanElement bean =  beanMap.get(className);
        
        if (bean == null) {
            throw new BeansException("Bean :" + klass + "不存在宣谈!");
        }
        // 在這里還是只考慮XmlBean的注入愈犹,不考慮AnnotationBlean注解的完成情況
        if (!bean.isDI() && bean instanceof XmlBean) {
            autowired(className, (XmlBean)bean);
        }
        
        return bean.getProxy();
    }
    
    private void autowired(String klassName, XmlBean bean) throws BeansException {
        // 和AnnotationBean的解決思路一樣,先設(shè)置狀態(tài)為已注入闻丑,防止循環(huán)依賴的無限遞歸
        bean.setDI(true);
        // 得到尚未注入的成員map
        Map<Field, String> wiredMap = bean.getWiredMap();
        if (wiredMap == null || wiredMap.isEmpty()) return;
        // 遍歷map
        for (Field field : wiredMap.keySet()) {
            String ref = wiredMap.get(field);
            String tagClassName = beanNameMap.get(ref);
            // ref如果是id則在beanNameMap中找漩怎,如果是className就在beanMap中找
            BeanElement wiredBean = tagClassName == null ? beanMap.get(ref) : beanMap.get(tagClassName);
            if (bean == null) {
                return;
            }
            if (!wiredBean.isDI() && wiredBean instanceof XmlBean) {
                autowired(ref, (XmlBean)wiredBean);
            }
            field.setAccessible(true);
            try {
                field.set(bean.getObject(), wiredBean.getObject());
            } catch (Exception e) {
                throw new BeansException(klassName + "依賴關(guān)系不正確!");
            }
        }
        wiredMap.clear();
    }
    
}

看過注解方式的話再看XML就會發(fā)現(xiàn)兩者其實(shí)是一回事嗦嗡,都是通過兩者提供的映射關(guān)系勋锤,利用反射機(jī)制完成注入!
只不過兩者提供的映射關(guān)系在解析起來時(shí)各有各的特點(diǎn)侥祭!

Xml方式的實(shí)現(xiàn)這里就簡單實(shí)現(xiàn)了叁执,來看看使用情況:

public class StudentA {
    String name;
    private StudentB B;
    
    public StudentA() {
    }
    
    @Override
    public String toString() {
        return "A:" + name + "->" +  B;
    }
    
}

@Component
public class StudentB {
    private String name;
    private StudentC C;
    
    public StudentB() {
    }

    @Override
    public String toString() {
        return "B:" + name + "->" + C;
    }
    
}

@Component
public class StudentC {
    private String name;
    private StudentA A;
    
    public StudentC() {
    }

    @Override
    public String toString() {
        return "C:" + name;
    }
    
}

xml的配置:

<SimpleSpring>
    <bean id="haha" class="com.zc.ioc.demo.StudentA">
        <property name="name" value="我是A"></property>
        <property name="B" ref="com.zc.ioc.demo.StudentB"></property>
    </bean>
    <bean class="com.zc.ioc.demo.StudentB">
        <property name="name" value="我是B"></property>
        <property name="C" ref="com.zc.ioc.demo.StudentC"></property>
    </bean>
    <bean class="com.zc.ioc.demo.StudentC">
        <property name="name" value="我是C"></property>
        <property name="A" ref="haha"></property>
    </bean>
</SimpleSpring>

主函數(shù):

public static void main(String[] args) throws BeansException {
        // 或者是使用BeanFactory beanFactory = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
        StudentA bean = applicationContext.getBean(StudentA.class);
        System.out.println(bean);
}

輸出:


image.png

那么試一試注解和Xml方式的混合使用:

@Component
public class StudentA {
    @Value(value="我是A")
    String name;
    @Autowired
    private StudentB B;
    
    public StudentA() {
    }
    
    @Override
    public String toString() {
        return "A:" + name + "->" +  B;
    }
    
}

@Component
public class StudentB {
    @Value(value="我是B")
    private String name;
    @Autowired
    private StudentC C;
    
    public StudentB() {
    }

    @Override
    public String toString() {
        return "B:" + name + "->" + C;
    }
    
}
@Component
public class StudentC {
    @Value(value="我是C")
    private String name;
    @Autowired
    private StudentD D;
    
    @Autowired
    private StudentA A;
    
    public StudentC() {
    }

    @Override
    public String toString() {
        return "C:" + name + "->" + D;
    }
    
}

public class StudentD {
    private String name;
    
    public StudentD() {
    }
    
    @Override
    public String toString() {
        return "D:" + name;
    }
    
}

Xml配置:

<SimpleSpring>
    <bean class="com.zc.ioc.demo.StudentD">
        <property name="name" value="我是D"></property>
    </bean>
</SimpleSpring>

主函數(shù):

public static void main(String[] args) throws BeansException {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/test_simple_spring.xml");
        StudentD studentD = applicationContext.getBean(StudentD.class);
        System.out.println(studentD);
        
        applicationContext= new AnnotationConfigApplicationContext("com.zc.moedl");
        StudentA studentA = applicationContext.getBean(StudentA.class);
        System.out.println(studentA);
}

輸出結(jié)果:


image.png

看起來是沒有問題了矮冬,但是如果Xml和注解之間的出現(xiàn)順序不同谈宛,結(jié)果也會不一樣,還得仔細(xì)考慮胎署,而且我做的這個(gè)是延遲注入吆录,只有在getBean的時(shí)候才會完成最后的注入,并且若是注解中需要一個(gè)Xml的bean注入琼牧,而xml的這個(gè)bean又依賴于注解中的一個(gè)bean恢筝,那么這套方法是不可行的哀卫!

參考:
模擬Sping,實(shí)現(xiàn)其IOC和AOP核心(一)
用 JDK 動態(tài)代理 API 寫的簡單的 AOP 程序

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滋恬,一起剝皮案震驚了整個(gè)濱河市聊训,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恢氯,老刑警劉巖带斑,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勋拟,居然都是意外死亡勋磕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門敢靡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挂滓,“玉大人,你說我怎么就攤上這事啸胧「险荆” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵纺念,是天一觀的道長贝椿。 經(jīng)常有香客問我,道長陷谱,這世上最難降的妖魔是什么烙博? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮烟逊,結(jié)果婚禮上渣窜,老公的妹妹穿的比我還像新娘。我一直安慰自己宪躯,他們只是感情好乔宿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著访雪,像睡著了一般予颤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冬阳,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音党饮,去河邊找鬼肝陪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刑顺,可吹牛的內(nèi)容都是我干的氯窍。 我是一名探鬼主播饲常,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼狼讨!你這毒婦竟也來了贝淤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤政供,失蹤者是張志新(化名)和其女友劉穎播聪,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體布隔,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡离陶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了衅檀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片招刨。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哀军,靈堂內(nèi)的尸體忽然破棺而出沉眶,到底是詐尸還是另有隱情,我是刑警寧澤杉适,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布谎倔,位于F島的核電站,受9級特大地震影響淘衙,放射性物質(zhì)發(fā)生泄漏传藏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一彤守、第九天 我趴在偏房一處隱蔽的房頂上張望毯侦。 院中可真熱鬧,春花似錦具垫、人聲如沸侈离。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卦碾。三九已至,卻和暖如春起宽,著一層夾襖步出監(jiān)牢的瞬間洲胖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工坯沪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绿映,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像叉弦,于是被迫代替她去往敵國和親丐一。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容