尚硅谷spring5day2

今天是2021-1-17

一。ioc的原理进阶

1.当某个Java实例需要另一个Java实例时,传统的方法是由调用者创建被调用者的实例(例如,使用new关键字获得被调用者实例),而使用Spring框架后,被调用者的实例不再由调用者创建,而是由Spring容器创建,这称为控制反转。Spring容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过Spring容器获得被调用者实例,这称为依赖注入。
2.ioc容器可以说就是基于工厂模式实现,在运行时通过解析提供的配置,利用反射创建好需要的对象,对于需要外部资源–其他对象或者常量值的对象,ioc在创建这个对象时会自动将需要的资源注入其中,使用时提供名字以拿到相应的对象。

二。ioc的实现过程简析

1.配置参数:在xml文件中配置好要创建的对象,或者是通过配置包扫描来告知spring要扫描的包。
2.解析配置:程序启动后,AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext解析你的配置,通过BeanConfigParser解析为统一的BeanDefinition格式,即将配置的bean标签或者标注@component系列注解的类解析为一个个独立的BeanDefinition实体类对象,包含bean的一些属性信息,比如这个bean是否懒加载、是否单例等,可以说BeanDefinition就是bean的抽象,bean在创建完成返回时其实是一个object类型。
3.创建bean:主要由BeanFactory工厂类实现,它负责根据解析得到的BeanDefinition来创建对象。主要结构有两个集合,存储单例bean的ConcurrentHashMap:singletonObjects,存储BeanDefinition的ConcurrentHashMap:beanDefinations。主要方法有,createbean()、getbean()、addBeanDefinitions()
3.1在解析完配置后,调用addBeanDefinitions()方法,为beanDefinations添加解析完成的BeanDefinition。添加完成后,遍历beanDefinations,如果有是单例且不是懒加载的beanDefinations,调用createbean()方法创建bean。
3.1.2createbean()方法创建bean,先得到bean对应类名的class类实例。如果这个bean对象的参数为空,直接通过反射创建对象;如果这个bean的参数不为空,那么遍历其参数集合
3.1.2.1每次遍历,如果这个参数不是引用的外部bean,那么得到其类名及实例;如果这个参数是引用的外部bean,那么去beanDefinations得到其beanDefinition,递归调用createbean方法,即先创建引用的外部bean,递归调用直到所有引用的外部bean都被创建。
3.1.2.2参数集合遍历完后,以所有参数的类名为参数去寻找bean的构造方法,并以所有参数的实例为构造方法参数创建bean
3.1.3bean创建好后,如果创建的bean非空且是单例的,那么就放入singletonObjects并返回。如果是空或者不是单例,那么就直接返回。
3.2所有bean创建好后,ioc容器也就准备好了。此时我们就会调用容器的getBean方法来获取bean,其实内部调用了BeanFactory的getbean方法
3.3在getbean方法中,根据传入的beanid去beanDefinations中获取beanDefination,然后调用createbean(beanDefination)并返回创建的bean。
3.3.1之前我们说过,在addBeanDefinitions()中只会为单例且不是懒加载的beanDefination创建bean,如果一个bean是多例或者懒加载,那么它在getbean时才会被真正创建。
3.3.2如果这个bean是单例的,那么再次调用createbean方法并不会重复创建。不要忘了,在ioc容器初始化时,调用createbean方法创建好单例bean后,放入了singletonObjects作为缓存。此时再次调用createbean方法,在开头处会判断:如果这个beanDefination是单例的且singletonObjects包含其id,那么直接返回singletonObjects中缓存的bean。
3.4getbean方法成功拿到bean就可以使用了。
3.5:其实有的地方没写好hh,在ioc容器初始化调用createbean方法时,递归创建引用的外部bean后,如果是单例的也会缓存进map,且开头也有单例判断,文字描述还是有点缺陷,还是直接看源码好的多,就贴一下吧:

public class BeansFactory {
    
    
    //缓存单例bean,key是beanId,value是对象
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    //缓存beanDefinition,key是beanDefinitionId,value是BeanDefinition对象
    private ConcurrentHashMap<String, BeanDefinition> beanDefinations = new ConcurrentHashMap<>();

    public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) {
    
    
        for (BeanDefinition beanDefinition: beanDefinitionList) {
    
    
            this.beanDefinations.putIfAbsent(beanDefinition.getId(), beanDefinition);
        }

        for (BeanDefinition beanDefinition : beanDefinitionList) {
    
    
            if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton()) {
    
    
                createBean(beanDefinition);
            }
        }
    }

    public Object getBean(String beanId) {
    
    
        BeanDefinition beanDefinition = beanDefinations.get(beanId);
        //找不到beanDefinition对象,直接抛异常
        if (beanDefinition == null) {
    
    
            throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId);
        }
        //根据beanDefinition来创建bean
        return createBean(beanDefinition);
    }

    @VisibleForTesting
    protected Object createBean(BeanDefinition beanDefinition) {
    
    
        if (beanDefinition.isSingleton() && singletonObjects.containsKey(beanDefinition.getId())) {
    
    
            return singletonObjects.get(beanDefinition.getId());
        }

        Object bean = null;
        try {
    
    
            // TODO: 此处Class的路径是如何计算的
            Class beanClass = Class.forName(beanDefinition.getClassName());
            List<BeanDefinition.ConstructorArg> args = beanDefinition.getConstructorArgs();
            if (args.isEmpty()) {
    
    
                //这里反射调用,创建实例
                bean = beanClass.newInstance();
            } else {
    
    
                Class[] argClasses = new Class[args.size()];
                Object[] argObjects = new Object[args.size()];
                for (int i = 0; i < args.size(); i++) {
    
    
                    BeanDefinition.ConstructorArg arg = args.get(i);
                    if (!arg.isRef()) {
    
    
                        argClasses[i] = arg.getType();
                        argObjects[i] = arg.getArg();
                    } else {
    
    
                        BeanDefinition refBeanDefinition = beanDefinations.get(arg.getArg());
                        if (refBeanDefinition == null) {
    
    
                            throw new NoSuchBeanDefinitionException("Bean is not defined: " + arg.getArg());
                        }
                        //递归调用
                        argObjects[i] = createBean(refBeanDefinition);
                        argClasses[i] = argObjects[i].getClass();
                    }
                }

                bean = beanClass.getConstructor(argClasses).newInstance(argObjects);
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
    
    
            e.printStackTrace();
        }

        if (bean != null && beanDefinition.isSingleton()) {
    
    
            singletonObjects.putIfAbsent(beanDefinition.getId(), bean);
            return singletonObjects.get(beanDefinition.getId());
        }

        return bean;
    }
}

总结:看完大神的文章后,第一次尝试看源码来总结ioc的流程原理,说实话还是有很多地方没有理解透,还是得多看,也只有看源码才能真正理解原理,也能体会大师设计代码的优雅与完美,感叹。

三。异步非阻塞

异步非阻塞,简单来说就是一个请求发出后,本次请求不用等待接口返回处理结果就可以进行下一步操作,而是接口处理完成后通知请求方。拆开来说,异步表示不需要主动确认请求的结果,而非阻塞表示等待期间可以进行其他操作。同步不等于阻塞,异步也不等于非阻塞,同步与异步的区别在于同步需要主动确认执行的结果,而异步不需要主动确认,等待通知即可;阻塞与非阻塞的区别在于阻塞期间本次请求直到得到返回的处理结果都不能做其他操作,而非阻塞可以继续进行其他操作。只有分开理解了各自的含义,同步非阻塞/异步非阻塞这样组合起来时才能完美理解。

猜你喜欢

转载自blog.csdn.net/qq_44727091/article/details/112750372