30个类手写Spring核心原理之依赖注入功能(3)

混合现实
• 阅读 838
本文节选自《Spring 5核心原理》

在之前的源码分析中我们已经了解到,依赖注入(DI)的入口是getBean()方法,前面的IoC手写部分基本流程已通。先在GPApplicationContext中定义好IoC容器,然后将GPBeanWrapper对象保存到Map中。在GPApplicationContext中设计两个Map:factoryBeanObjectCache保存单例对象的缓存,factoryBeanInstanceCache保存GPBeanWrapper的缓存,变量命名也和原生Spring一致,这两个对象的设计其实就是注册式单例模式的经典应用。


public class GPApplicationContext extends GPDefaultListableBeanFactory implements GPBeanFactory {

    private String [] configLocations;

    private GPBeanDefinitionReader reader;

    //用来保证注册式单例的容器
    private Map<String,Object> factoryBeanObjectCache = new HashMap<String, Object>();

    //用来存储所有的被代理过的对象
    private Map<String,GPBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, GPBeanWrapper>();
        
    ...

}

1 从getBean()方法开始

下面我们从完善getBean()方法开始:


    @Override
    public Object getBean(String beanName) {

        GPBeanDefinition beanDefinition = super.beanDefinitionMap.get(beanName);

        try{

            //生成通知事件
            GPBeanPostProcessor beanPostProcessor = new GPBeanPostProcessor();

            Object instance = instantiateBean(beanDefinition);
            if(null == instance){ return  null;}

            //在实例初始化以前调用一次
            beanPostProcessor.postProcessBeforeInitialization(instance,beanName);

            GPBeanWrapper beanWrapper = new GPBeanWrapper(instance);

            this.factoryBeanInstanceCache.put(beanName,beanWrapper);

            //在实例初始化以后调用一次
            beanPostProcessor.postProcessAfterInitialization(instance,beanName);

            populateBean(beanName,instance);

            //通过这样调用,相当于给我们自己留有了可操作的空间
            return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
        }catch (Exception e){
//            e.printStackTrace();
            return null;
        }
    }

2 instantiateBean()方法反射创建实例


    //传一个BeanDefinition,就返回一个实例Bean
    private Object instantiateBean(GPBeanDefinition beanDefinition){
        Object instance = null;
        String className = beanDefinition.getBeanClassName();
        try{

            //因为根据Class才能确定一个类是否有实例
            if(this.factoryBeanObjectCache.containsKey(className)){
                instance = this.factoryBeanObjectCache.get(className);
            }else{
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();

                this.factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(),instance);
            }

            return instance;
        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }
        

3 populateBean()方法完成依赖注入


    private void populateBean(String beanName,Object instance){

        Class clazz = instance.getClass();

        if(!(clazz.isAnnotationPresent(GPController.class) ||
                clazz.isAnnotationPresent(GPService.class))){
            return;
        }

        Field [] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            if (!field.isAnnotationPresent(GPAutowired.class)){ continue; }

            GPAutowired autowired = field.getAnnotation(GPAutowired.class);

            String autowiredBeanName = autowired.value().trim();

            if("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getName();
            }

            field.setAccessible(true);

            try {

                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName). getWrappedInstance());

            } catch (IllegalAccessException e) {
//                e.printStackTrace();
            }

        }

    }
        

4 GPBeanPostProcessor后置处理器

原生Spring中的BeanPostProcessor是为对象初始化事件设置的一种回调机制。这个Mini版本中只做说明,不做具体实现,感兴趣的“小伙伴”可以继续深入研究Spring源码。


package com.tom.spring.formework.beans.config;

public class GPBeanPostProcessor {

    //为在Bean的初始化之前提供回调入口
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }

    //为在Bean的初始化之后提供回调入口
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }
}

至此,DI部分就手写完成了,也就是说完成了Spring的核心部分。“小伙伴们”是不是发现其实还是很简单的?

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
SpringIOC官方文档解读
IoC容器本章介绍了Spring的控制反转(IoC)容器。1.1。SpringIoC容器和Bean简介本章介绍了反转控制(IoC)原则的Spring框架实现。IoC也称为依赖注入(DI)。在此过程中,对象可以通过①构造函数参数(),②工厂方法的参数③或在构造或从工厂方法返回后在对象实例上设置的属性来定义其依
Easter79 Easter79
3年前
Spring系列(五) 容器初始化过程源码
IoC/DI的概念容器是Spring的核心之一(另一个核心是AOP).有了容器,IOC才可能实现.什么使IoC?IoC就是将类自身管理的与其由依赖关系的对象的创建/关联和管理交予容器实现,容器按照配置(比如xml文件)来组织应用对象的创建和关联.什么使DI?DI是IoC的实现方式,由容器
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
SpringIOC
XML方式:IOC:控制反转的底层原理就是:工厂模式反射配置文件DI:依赖注入就是通过配置文件设置属性值BeanFactory 是老版本的工厂类:调用getBean的时候,才会生成类的实例ApplicationContext 是新版本的工厂类:加载配置文件的时候,就会将Spring管理的类
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这