自己動(dòng)手寫Spring

眾說周知当宴,Spring是一個(gè)具有強(qiáng)大的依賴注入功能的Java框架。本篇文章將介紹筆者自己動(dòng)手寫的一個(gè)輕量級(jí)依賴注入框架冰沙,實(shí)現(xiàn)jsr330并兼容jsr330注解哑芹。

完整代碼托管在github中,可以點(diǎn)擊https://github.com/bdqfork/spring-toy 查看完整項(xiàng)目垫释。

另筆者開發(fā)經(jīng)驗(yàn)不足丝格,歡迎大家指正批評(píng)。

需求簡介

  1. 可以使用注解標(biāo)記類為組件棵譬,并自動(dòng)掃描包路徑显蝌,識(shí)別組件類。
  2. 獲取注解信息订咸,將組件類注冊(cè)到容器中曼尊,供以后的訪問使用。
  3. 解析組件之間的依賴關(guān)系脏嚷,初始化組件類骆撇,并注入相關(guān)依賴。
  4. 從容器中獲取組件類的實(shí)例父叙,并正常調(diào)用相應(yīng)的方法神郊。

更多更具體的細(xì)節(jié)需求,可以查看jsr330趾唱。jsr330對(duì)依賴注入作了一個(gè)詳細(xì)的說明涌乳,但并未給出實(shí)現(xiàn),筆者嘗試根據(jù)jsr330的定義甜癞,作出了自己的實(shí)現(xiàn)夕晓。

項(xiàng)目框架

整個(gè)項(xiàng)目大致分為以下幾個(gè)包:

image

其中,

  • annotation包中定義了一些容器所需要的注解带欢,比如Component运授,Service等注解烤惊。
  • container包是容器的主要實(shí)現(xiàn),負(fù)責(zé)處理容器的相關(guān)功能吁朦,如依賴注入等柒室。
  • context包定義了上下文環(huán)境,負(fù)責(zé)掃描組件逗宜,以及依賴解析等過程雄右。
  • exception包定義了項(xiàng)目所需的異常。
  • proxy包定義了兩種動(dòng)態(tài)代理的方式纺讲,一種是Jdk的動(dòng)態(tài)代理實(shí)現(xiàn)擂仍,另一種是CGlib方式。
  • utils包定義了一些工具類熬甚。

功能實(shí)現(xiàn)

注解定義

在進(jìn)行核心功能實(shí)現(xiàn)之前逢渔,首先定義相關(guān)的注解,筆者參考了Spring的注解定義乡括,作出了如下注解肃廓。

定義 功能 參數(shù)定義
@Component 用于標(biāo)記組件類,被標(biāo)記的類將會(huì)被添加到容器中管理诲泌。 String value()盲赊,用于指定類名,默認(rèn)為""敷扫。
@Repositorty 用于標(biāo)記組件類哀蘑,被標(biāo)記的類將會(huì)被添加到容器中管理,這是一個(gè)領(lǐng)域定義葵第,其功能和Component一致绘迁。 String value(),用于指定類名羹幸,默認(rèn)為""脊髓。
@Service 用于標(biāo)記組件類,被標(biāo)記的類將會(huì)被添加到容器中管理栅受,這是一個(gè)領(lǐng)域定義,其功能和Component一致恭朗。 String value()屏镊,用于指定類名,默認(rèn)為""痰腮。
@Controller 用于標(biāo)記組件類而芥,被標(biāo)記的類將會(huì)被添加到容器中管理,這是一個(gè)領(lǐng)域定義膀值,其功能和Component一致棍丐。 String value()误辑,用于指定類名,默認(rèn)為""歌逢。
@Scope 用于標(biāo)記組件類巾钉,用于表示,注冊(cè)到容器中的類秘案,其實(shí)例是否為單例砰苍。 String value(),用于表示是否單例阱高,其值在ScopeType類中定義赚导。
@AutoWired 標(biāo)記待注入的字段,構(gòu)造函數(shù)赤惊,setter方法等吼旧。 boolean required(),用于表示是否一定需要注入未舟。
@Qualifier 限定器浇揩,可以與AutoWired一起使用,標(biāo)記待注入依賴名牍蜂。 String value()陷虎,待注入依賴名。
BeanFactory 用于替換@AutoWired的一個(gè)接口魂角,可以實(shí)現(xiàn)相同的功能昵济。 T,待注入依賴的類型野揪。

由于同時(shí)筆者兼容了jsr330访忿,jsr330官方給出了一些注解,功能上與上文定義的注解可以進(jìn)行相互替換斯稳,在此也進(jìn)行描述海铆。

注解 功能 備注
@Named 可以作為@Component等組件標(biāo)記注解的替換,標(biāo)記一個(gè)組件類挣惰。 也可以作為@Qualifier的替換卧斟,標(biāo)記待注入依賴名。
@Singleton 可以替換@Scope(“singleton”)憎茂,被標(biāo)記的類的實(shí)例將會(huì)是單例珍语。
@Inject 可以和@AutoWired替換,標(biāo)記待注入的字段竖幔,構(gòu)造函數(shù)板乙,setter方法等。 不支持required拳氢,其標(biāo)記的依賴必須注入募逞,否則拋出異常蛋铆。
Provider 與BeanFactory等價(jià),可以互相替換放接。
注解掃描

在相關(guān)注解的定義完成之后刺啦,需要進(jìn)行掃描,將標(biāo)記有@Component等注解的類掃描出來透乾,以進(jìn)行下一步的處理洪燥。

整個(gè)掃描的過程實(shí)際上是對(duì)類進(jìn)行掃描,可以通過Java的ClassLoader來掃描類路徑乳乌,將類加載進(jìn)一個(gè)集合中捧韵。這個(gè)過程的部分代碼如下,完整代碼可以在utils包下的ReflectUtil中查看汉操。

    private static final String FILE_PROTOCOL = "file";
    private static final String JAR_PROTOCOL = "jar";
    private static final String SUFFIX = ".class";

    /**
     * 根據(jù)包名獲取獲取Class
     *
     * @param packageName
     * @return
     */
    public static Set<Class<?>> getClasses(String packageName) {
        if (packageName == null || "".equals(packageName)) {
            return Collections.emptySet();
        }
        //將包名改為相對(duì)路徑
        String packagePath = packageName.replace(".", "/");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set<Class<?>> classes = new HashSet<>();
        try {
            //掃描包路徑再来,返回資源的枚舉
            Enumeration<URL> dirs = classLoader.getResources(packagePath);
            while (dirs.hasMoreElements()) {
                URL fileUrl = dirs.nextElement();
                String filePath = fileUrl.getPath();
                //判斷資源類型
                if (FILE_PROTOCOL.equals(fileUrl.getProtocol())) {
                    //處理文件類型的Class
                    classes.addAll(getClassesByFilePath(filePath, packagePath));
                } else if (JAR_PROTOCOL.equals(fileUrl.getProtocol())) {
                    //處理Jar包中的Class
                    JarURLConnection jarURLConnection = (JarURLConnection) fileUrl.openConnection();
                    JarFile jarFile = jarURLConnection.getJarFile();
                    classes.addAll(getClassesByJar(jarFile));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

通過ClassLoader加載指定包路徑下的所有資源,然后對(duì)Class進(jìn)行加載即可磷瘤,需要注意的是jar包里面的Class加載的方式有些許不同芒篷。

容器的實(shí)現(xiàn)

容器這個(gè)功能可以說是依賴注入的核心之一了,容器是對(duì)所有組件的管理采缚,基本上所有的功能都圍繞著容器來開展针炉。

最簡單的容器可能就是一個(gè)Map<String,Object>了,網(wǎng)上很多的文章都是基于這個(gè)類型實(shí)現(xiàn)的簡單的依賴注入扳抽。然而篡帕,這種方式有很多的缺陷。例如贸呢,使用這種方式實(shí)現(xiàn)的容器镰烧,其存儲(chǔ)的大都是待注入對(duì)象的直接實(shí)例,也就是說獲取的對(duì)象實(shí)例大都是單例的形式楞陷,這就導(dǎo)致了一個(gè)問題怔鳖,當(dāng)需要返回的實(shí)例是一個(gè)新的實(shí)例的時(shí)候,這種實(shí)現(xiàn)方式就無法滿足了固蛾。一方面是因?yàn)榻嶂矗琈ap里只保存了一個(gè)實(shí)例,另一方面是因?yàn)榉祷匦碌膶?shí)例艾凯,需要重新將依賴注入到新的實(shí)例中昌犹。

因此,要使用更高級(jí)的方式進(jìn)行實(shí)現(xiàn)览芳。看過Spring源碼的同學(xué)鸿竖,應(yīng)該了解到BeanDefinition沧竟。BeanDefinition是對(duì)Bean的一個(gè)描述铸敏,我們可以定義一個(gè)BeanDefinition。它描述了一個(gè)Bean的類型悟泵,名稱杈笔,是否需要單例等信息。使用Map<String,BeanDefinition>等方式來作為容器糕非,這樣上文描述的問題就迎刃而解了蒙具。

筆者定義的BeanDefinition如下:

public class BeanDefinition {
    /**
     * Class類
     */
    private Class<?> clazz;
    /**
     * Bean的名稱
     */
    private String name;
    /**
     * 單實(shí)例
     */
    private Object instance;
    /**
     * 是否單例
     */
    private boolean isSingleton;
     /**
     * 依賴信息提供者
     */
    private InjectorProvider injectorProvider;
}

獲取對(duì)象實(shí)例的方法如下:

    /**
     * 獲取對(duì)象實(shí)例,如果bean是單例的朽肥,則每次都返回同一個(gè)實(shí)例禁筏,如果不是,則每次都創(chuàng)建一個(gè)新的實(shí)例衡招。
     *
     * @return Object
     */
    public Object getInstance() throws InjectedException {
        if (isSingleton) {
            return getSingleInstance();
        }
        return newBean();
    }

    private Object getSingleInstance() throws InjectedException {
        if (instance == null) {
            synchronized (Object.class) {
                if (instance == null) {
                    instance = newBean();
                }
            }
        }
        return instance;
    }

    private Object newBean() throws InjectedException {
        Object instance = injectorProvider.doInject(this);
        Class<?>[] classes = clazz.getInterfaces();
        if (classes.length != 0) {
            JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler();
            return jdkInvocationHandler.newProxyInstance(instance);
        } else {
            CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
            return cglibMethodInterceptor.newProxyInstance(instance);
        }
    }

這樣一來篱昔,容器的實(shí)現(xiàn)就簡單多了。BeanDefinition的完整代碼始腾,可以查閱container包下的BeanDefinition類州刽。

下面貼出容器的代碼實(shí)現(xiàn),只貼出注冊(cè)部分浪箭,完整代碼請(qǐng)查看container包下的BeanContainer類穗椅。

public class BeanContainer {
    private Map<String, BeanDefinition> beans = new HashMap<>();

    public void register(String beanName, BeanDefinition beanDefinition) throws ConflictedBeanException {
        if (beans.containsKey(beanName)) {
            throw new ConflictedBeanException(String.format("the entity named: %s has conflicted ! ", beanName));
        }
        beans.put(beanName, beanDefinition);
    }
}

通過調(diào)用register方法,即可將Bean注冊(cè)到容器中奶栖。

注冊(cè)組件

組件的注冊(cè)過程很簡單匹表,掃描包路徑,獲取所有組件的Class驼抹。然后根據(jù)jsr330的要求桑孩,檢測(cè)組件是否可注冊(cè),將可注冊(cè)的組件注冊(cè)到容器中即可框冀。代碼如下:

    private void scan() throws SpringToyException {
        Set<Class<?>> candidates = new HashSet<>();
        for (String scanPath : scanPaths) {
            candidates.addAll(ReflectUtil.getClasses(scanPath));
        }
        for (Class<?> candidate : candidates) {
            if (candidate.isAnnotation() || candidate.isInterface() || Modifier.isAbstract(candidate.getModifiers())) {
                continue;
            }
            String name = getComponentName(candidate);
            if (name != null) {
                boolean isSingleton = false;

                Scope scope = candidate.getAnnotation(Scope.class);
                if (scope != null) {
                    if (ScopeType.SINGLETON.equals(scope.value())) {
                        isSingleton = true;
                    } else if (!ScopeType.PROTOTYPE.equals(scope.value())) {
                        throw new SpringToyException("the value of scope is error !");
                    }
                } else if (candidate.getAnnotation(Singleton.class) != null) {
                    isSingleton = true;
                }

                if ("".equals(name)) {
                    name = this.beanNameGenerator.generateBeanName(candidate);
                }

                BeanDefinition beanDefinition = new BeanDefinition(candidate, isSingleton, name);
                beanDefinition.setInjectorProvider(new InjectorProvider(candidate, this.beanNameGenerator));

                beanContainer.register(beanDefinition.getName(), beanDefinition);
            }
        }

        Map<String, BeanDefinition> beanDefinationMap = beanContainer.getBeanDefinations();
        Resolver resolver = new Resolver(beanContainer);
        for (Map.Entry<String, BeanDefinition> entry : beanDefinationMap.entrySet()) {
            resolver.resolve(entry.getValue());
        }

    }

完整的代碼在context包下的AnnotationApplicationContext類中可以查看流椒。

依賴信息的管理

通過上文的介紹,我們使用BeanDefinition描述了一個(gè)組件Bean的基本信息明也,但是我們還有一樣重要的信息沒有描述——組件依賴信息宣虾。組件類之間是有著依賴的關(guān)系的,BeanDefinition并沒有描述組件類的依賴信息温数,為了要完整的描述組件類的信息绣硝,引入InjectorData來描述依賴注入信息。

InjectorData是一個(gè)接口撑刺,可以有多種實(shí)現(xiàn)鹉胖,其定義如下:

public interface InjectorData {

    /**
     * 設(shè)置注入的bean
     *
     * @param bean
     */
    void setBean(BeanDefinition bean);

    /**
     * 返回依賴的bean
     *
     * @return
     */
    BeanDefinition getBean();

    /**
     * 設(shè)置依賴的默認(rèn)名稱
     *
     * @param defaultName
     */
    void setDefaultName(String defaultName);

    /**
     * 獲取依賴的默認(rèn)名稱
     *
     * @return
     */
    String getDefaultName();

    /**
     * 獲取指定的依賴的名稱
     *
     * @return
     */
    String getRefName();

    /**
     * 獲取依賴的類型
     *
     * @return
     */
    Class<?> getType();

    /**
     * 判斷依賴是否匹配
     *
     * @param beanDefinition
     * @return
     */
    boolean isMatch(BeanDefinition beanDefinition);

    /**
     * 是否必須
     *
     * @return
     */
    boolean isRequired();

    /**
     * 設(shè)置是否是注入器
     *
     * @param provider
     */
    void setProvider(boolean provider);

    /**
     * 是否是注入器
     *
     * @return
     */
    boolean isProvider();

    /**
     * 設(shè)置注入器類型
     *
     * @param providedType
     */
    void setProvidedType(Class<?> providedType);

}

根據(jù)具體實(shí)現(xiàn)類的不同,可以用來描述不同的依賴注入信息,包括字段依賴注入信息甫菠,參數(shù)注入信息挠铲。繼承關(guān)系圖如下:

image

其中,抽象父類中實(shí)現(xiàn)了一些通用的方法寂诱,部分代碼如下拂苹,省略一些get,set方法:

public abstract class AbstractInjectorData implements InjectorData {
    /**
     * 默認(rèn)依賴名稱
     */
    private String defalultName;
    /**
     * 指定依賴名稱
     */
    private String refName;
    /**
     * 依賴的BeanDefination實(shí)例
     */
    private BeanDefinition bean;
    /**
     * 是否必須
     */
    private boolean isRequired;
    /**
     * 是否是Provider或者BeanFactory依賴
     */
    private boolean isProvider;
    /**
     * Provider或者BeanFactory提供的依賴類
     */
    private Class<?> providedType;

    public AbstractInjectorData(String defalultName, String refName, boolean isRequired) {
        this.defalultName = defalultName;
        this.refName = refName;
        this.isRequired = isRequired;
    }

    @Override
    public void setDefaultName(String defaultName) {
        this.defalultName = defaultName;
    }

    @Override
    public String getDefaultName() {
        return defalultName;
    }

    @Override
    public String getRefName() {
        return refName;
    }

    @Override
    public void setBean(BeanDefinition bean) {
        this.bean = bean;
    }

    @Override
    public BeanDefinition getBean() {
        return this.bean;
    }

    @Override
    public boolean isRequired() {
        return isRequired;
    }

    @Override
    public boolean isMatch(BeanDefinition beanDefinition) {
        if (refName != null && refName.equals(beanDefinition.getName())) {
            return true;
        } else if (defalultName.equals(beanDefinition.getName())) {
            return true;
        } else {
            Class<?> type = getType();
            return beanDefinition.isType(type);
        }
    }

    @Override
    public void setProvider(boolean provider) {
        isProvider = provider;
    }

    @Override
    public boolean isProvider() {
        return isProvider;
    }

    @Override
    public void setProvidedType(Class<?> providedType) {
        this.providedType = providedType;
    }

    protected Class<?> getProvidedType() {
        return providedType;
    }
}

其子類實(shí)現(xiàn)也是很簡單痰洒,將標(biāo)記的依賴字段或者參數(shù)瓢棒,傳入相應(yīng)的依賴描述里面保存下來即可:

/**
 * bean的依賴信息
 *
 * @author bdq
 * @date 2019-02-12
 */
public class FieldInjectorData extends AbstractInjectorData {
    private Field field;

    public FieldInjectorData(String defalultName, String refName, boolean required, Field field) {
        super(defalultName, refName, required);
        this.field = field;
    }

    @Override
    public Class<?> getType() {
        if (isProvider()) {
            return getProvidedType();
        }
        return field.getType();
    }

    public Field getField() {
        return field;
    }

}

/**
 * @author bdq
 * @date 2019-02-13
 */
public class ParameterInjectorData extends AbstractInjectorData {
    private Parameter parameter;

    public ParameterInjectorData(String defalultName, String refName, boolean required, Parameter parameter) {
        super(defalultName, refName, required);
        this.parameter = parameter;
    }

    @Override
    public Class<?> getType() {
        if (isProvider()) {
            return getProvidedType();
        }
        return parameter.getType();
    }

}
依賴注入器

什么是依賴注入器,依賴注入器是筆者自己定義的一個(gè)接口Injector丘喻,它的功能是負(fù)責(zé)管理依賴信息脯宿,進(jìn)行依賴注入。之所以定義這個(gè)接口仓犬,是因?yàn)橐蕾囎⑷胗兄N場(chǎng)景:字段注入嗅绰,構(gòu)造器注入,方法注入搀继。不同的注入方式有不同的實(shí)現(xiàn)方式窘面,于是引入Injector,分別實(shí)現(xiàn)對(duì)應(yīng)三種場(chǎng)景的注入器叽躯,Injector的實(shí)現(xiàn)應(yīng)該持有對(duì)應(yīng)的注入信息财边。

Injector接口的定義如下:

/**
 * @author bdq
 * @date 2019-02-14
 */
public interface Injector {
    /**
     * 判斷當(dāng)前bean是否依賴beanDefination,如果是点骑,返回true酣难,否則返回false
     *
     * @param beanDefinition
     * @return boolean
     */
    boolean hasDependence(BeanDefinition beanDefinition);

    /**
     * 注入依賴
     *
     * @param instance
     * @param beanDefinition
     * @return
     * @throws InjectedException
     */
    Object inject(Object instance, BeanDefinition beanDefinition) throws InjectedException;
}

繼承關(guān)系圖如下:

image

AbstractInjector是Injector的抽象實(shí)現(xiàn)類,實(shí)現(xiàn)了一些通用的方法黑滴,代碼如下:

/**
 * @author bdq
 * @date 2019-02-14
 */
public abstract class AbstractInjector implements Injector {
    protected List<InjectorData> injectorDatas;

    public AbstractInjector(List<InjectorData> injectorDatas) {
        this.injectorDatas = injectorDatas;
    }

    @Override
    public boolean hasDependence(BeanDefinition beanDefinition) {
        for (InjectorData injectorData : injectorDatas) {
            if (injectorData.isMatch(beanDefinition)) {
                return true;
            }
        }
        return false;
    }

}

ConstructorInjector的功能是進(jìn)行構(gòu)造函數(shù)的注入憨募,產(chǎn)生對(duì)象實(shí)例,主要代碼如下:

    /**
     * 構(gòu)造器注入
     *
     * @param beanDefinition
     * @return
     * @throws ConstructorInjectedException
     */
    public Object inject(BeanDefinition beanDefinition) throws ConstructorInjectedException {
        return inject(null, beanDefinition);
    }

    @Override
    public Object inject(Object instance, BeanDefinition beanDefinition) throws ConstructorInjectedException {
        if (constructor != null) {
            if (injectorDatas != null && injectorDatas.size() > 0) {
                List<Object> args = new LinkedList<>();
                //遍歷構(gòu)造函數(shù)的參數(shù)依賴信息
                for (InjectorData injectorData : injectorDatas) {
                    BeanDefinition bean = injectorData.getBean();
                    try {
                        if (bean != null) {
                            //判斷是否是Provider
                            if (injectorData.isProvider()) {
                                //添加實(shí)例到Provider參數(shù)
                                args.add(new ObjectFactory<>(bean.getInstance()));
                            } else {
                                //添加實(shí)例作為參數(shù)
                                args.add(bean.getInstance());
                            }
                        }
                    } catch (InjectedException e) {
                        throw new ConstructorInjectedException(String.format("failed to inject entity: %s by constructor!", beanDefinition.getName()), e);
                    }
                }
                try {
                    if (args.size() > 0) {
                        //反射調(diào)用構(gòu)造器袁辈,構(gòu)造對(duì)象實(shí)例
                        instance = constructor.newInstance(args.toArray());
                    }
                } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                    throw new ConstructorInjectedException(String.format("failed to inject entity: %s by constructor!", beanDefinition.getName()), e);
                }
            }
        }
        return instance;
    }

另外兩個(gè)注入器的實(shí)現(xiàn)原理與之類似菜谣,只不過反射調(diào)用的方法不同罷了,就不再貼出代碼了晚缩。

依賴解析

依賴注入器實(shí)現(xiàn)了依賴注入的過程尾膊,而依賴解析的過程并沒有體現(xiàn)。依賴解析的步驟荞彼,筆者認(rèn)為不屬于注入器功能上的定義冈敛,依賴注入器應(yīng)該只關(guān)注于進(jìn)行依賴注入,所以筆者將這部分代碼放在了Resolver中鸣皂。

在Resolver中抓谴,會(huì)對(duì)依賴進(jìn)行解析暮蹂,查詢依賴的Bean,設(shè)置依賴信息齐邦。其主要代碼如下:

    public void resolve(BeanDefinition beanDefinition) throws SpringToyException {
        //如果已經(jīng)解析過了椎侠,則返回
        if (beanDefinition.isResolved()) {
            return;
        }
        //優(yōu)先解析父類
        Class<?> superClass = beanDefinition.getClazz().getSuperclass();
        if (superClass != null && superClass != Object.class) {

            for (BeanDefinition bean : beanContainer.getBeans(superClass).values()) {
                if (bean != beanDefinition) {
                    //遞歸解析父類
                    resolve(bean);
                }
            }
        }

        InjectorProvider injectorProvider = beanDefinition.getInjectorProvider();
        if (injectorProvider != null) {

            //如果有構(gòu)造器注入,則先解析構(gòu)造器注入依賴
            if (injectorProvider.getConstructorParameterDatas() != null) {
                for (InjectorData parameterInjectorData : injectorProvider.getConstructorParameterDatas()) {
                    doResolve(beanDefinition, injectorProvider, parameterInjectorData, parameterInjectorData.isRequired());
                }
            }

            //如果有字段注入措拇,則解析字段注入依賴
            if (injectorProvider.getFieldInjectorDatas() != null) {
                for (InjectorData fieldInjectorData : injectorProvider.getFieldInjectorDatas()) {
                    doResolve(beanDefinition, injectorProvider, fieldInjectorData, fieldInjectorData.isRequired());
                }
            }

            //如果有方法注入,則解析方法注入依賴
            if (injectorProvider.getMethodInjectorAttributes() != null) {
                for (MethodInjectorAttribute methodInjectorAttribute : injectorProvider.getMethodInjectorAttributes()) {
                    if (methodInjectorAttribute.getParameterInjectorDatas() != null) {
                        for (InjectorData parameterInjectorData : methodInjectorAttribute.getParameterInjectorDatas()) {
                            doResolve(beanDefinition, injectorProvider, parameterInjectorData, methodInjectorAttribute.isRequired());
                        }
                    }
                }
            }

        }

        beanDefinition.setResolved(true);

    }

    private void doResolve(BeanDefinition beanDefinition, InjectorProvider injectorProvider, InjectorData injectorData, boolean isRequired) throws UnsatisfiedBeanException {
        BeanDefinition ref = null;

        Map<String, BeanDefinition> beanDefinationMap = beanContainer.getBeanDefinations();
        //判斷依賴組件是否存在慎宾,先查找指定名稱的依賴丐吓,如果不存在,則按找默認(rèn)名稱去查找趟据,仍然不存在券犁,則再按類型匹配
        if (injectorData.getRefName() != null && beanDefinationMap.containsKey(injectorData.getRefName())) {
            ref = beanDefinationMap.get(injectorData.getRefName());
        } else if (beanDefinationMap.containsKey(injectorData.getDefaultName())) {
            ref = beanDefinationMap.get(injectorData.getDefaultName());
        } else {
            for (BeanDefinition bean : beanDefinationMap.values()) {
                if (bean.isType(injectorData.getType())) {
                    ref = bean;
                    break;
                } else if (bean.isSubType(injectorData.getType())) {
                    ref = bean;
                    break;
                }
            }
        }

        //判斷依賴是否存在,如果不存在汹碱,則拋出異常粘衬。如果依賴存在,但有相互引用的情況咳促,也拋出異常
        if (ref == null) {
            if (isRequired) {
                throw new UnsatisfiedBeanException("unsatisfied entity , the entity named " + injectorData.getType() + " don't exists");
            }
        } else if (beanDefinition == ref || injectorProvider.hasDependence(beanDefinition)) {
            throw new UnsatisfiedBeanException("unsatisfied entity , there two entity ref each other !");
        } else {
            //設(shè)置依賴信息
            injectorData.setBean(ref);
        }
    }

至此稚新,一個(gè)簡單的依賴注入框架完成了。這個(gè)框架還有很多需要完善的地方跪腹,比如效率的優(yōu)化褂删,更多安全性的檢查等等。

如果對(duì)筆者的框架感興趣的冲茸,可以點(diǎn)擊:https://github.com/bdqfork/spring-toy 查看完整代碼屯阀,運(yùn)行example進(jìn)行測(cè)試,希望大家批評(píng)指正轴术。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末难衰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子逗栽,更是在濱河造成了極大的恐慌盖袭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祭陷,死亡現(xiàn)場(chǎng)離奇詭異苍凛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)兵志,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門醇蝴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人想罕,你說我怎么就攤上這事悠栓∶拐牵” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵惭适,是天一觀的道長笙瑟。 經(jīng)常有香客問我,道長癞志,這世上最難降的妖魔是什么往枷? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮凄杯,結(jié)果婚禮上错洁,老公的妹妹穿的比我還像新娘。我一直安慰自己戒突,他們只是感情好屯碴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膊存,像睡著了一般导而。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上隔崎,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天今艺,我揣著相機(jī)與錄音,去河邊找鬼仍稀。 笑死洼滚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的技潘。 我是一名探鬼主播遥巴,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼享幽!你這毒婦竟也來了铲掐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤值桩,失蹤者是張志新(化名)和其女友劉穎摆霉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奔坟,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡携栋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咳秉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婉支。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖澜建,靈堂內(nèi)的尸體忽然破棺而出向挖,到底是詐尸還是另有隱情蝌以,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布何之,位于F島的核電站跟畅,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏溶推。R本人自食惡果不足惜徊件,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悼潭。 院中可真熱鬧庇忌,春花似錦、人聲如沸舰褪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽占拍。三九已至,卻和暖如春捎迫,著一層夾襖步出監(jiān)牢的瞬間晃酒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工窄绒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贝次,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓彰导,卻偏偏與公主長得像蛔翅,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子位谋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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