Spring official website reading (a) and examples of the vessel

Starting today, we together go over Spring's official website, the paper mainly related to the festival's official website in the 1.2, 1.3.

Spring container

What containers are?

We look at the official website of the sentence:

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.

Translation is probably down:

  1. Spring IOC is a container org.springframework.context.ApplicationContextof objects instantiated
  2. It is responsible for instantiating the container, configuration, and fitted with a bean

So we can say:

From the code-level view: Spring container that implements a ApplicationContextobject interface ,

From a functional point of view: Spring Spring Framework is the core of the container, it is used to manage the object. The container object is created to connect them together, configure them, and manage their entire lifecycle from creation to destruction.

How to container work?

Tell me what we direct online a picture, as follows:

Spring container system may be used to produce a fully configurable by the configuration metadata pojo classes and we have submitted

Here that the configuration metadata, in fact, we are providing our XML configuration file, or some configuration information provided by way of comment

Spring Bean

How to instantiate a Bean?

From the official website of view, there are three main methods

  1. Construction method
  2. By static factory method
  3. By way of example factory method

These three examples, the official website has specific presentation, there is not a paste, through their own access to portions of our source code, to validate the conclusions we get in the official website, debug and then verified by other means.

We then from the perspective of the code wave analysis, where we directly targeted to org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstancethis approach, no specific demonstration of the positioning step, shaped as we can by the following code:

ClassPathXmlApplicationContext cc =
    // 这里我们通过xml配置实例化一个容器
    new ClassPathXmlApplicationContext("classpath:application.xml");
MyServiceImpl luBan = (MyServiceImpl) cc.getBean("myServiceImpl");

Main direct method of operation, and then org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstancehit a breakpoint entry of this method, as shown:

Next, we analyze this method, the code is as follows:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        // 1.获取这个bean的class属性,确保beanDefinition中beanClass属性已经完成解析
        // 我们通过xml从<bean>标签中解析出来的class属性在刚刚开始的时候必定是个字符串
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        // 省略异常判断代码.....
        
        // 2.通过beanDefinition中的supplier实例化这个bean
        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }
        
        // 3.通过FactoryMethod实例化这个bean
        if (mbd.getFactoryMethodName() != null) {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }

        // 4.下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种                是通过推断出来的构造函数
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
    
    
   
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                return instantiateBean(beanName, mbd);
            }
        }

        // Candidate constructors for autowiring?
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // Preferred constructors for default construction?
        ctors = mbd.getPreferredConstructors();
        if (ctors != null) {
            return autowireConstructor(beanName, mbd, ctors, null);
        }

        // No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }

我们主要关注进行实例化的几个方法:

  1. 通过BeanDefinition中的instanceSupplier直接获取一个实例化的对象。这个instanceSupplier属性我本身不是特别理解,在xml中的标签以及注解的方式都没有找到方式配置这个属性。后来在org.springframework.context.support.GenericApplicationContext这个类中找到了以下两个方法

supplier.png

经过断点测试,发现这种情况下,在实例化对象时会进入上面的supplier方法。下面是测试代码:

public static void main(String[] args) {
    // AnnotationConfigApplicationContext是GenericApplicationContext的一个子类
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        ac.registerBean("service", Service.class,Service::new);
        ac.refresh();
        System.out.println(ac.getBean("service"));
    }

可以发现进入了这个方法进行实例化

debug001.png

这个方法一般不常用,平常我们也使用不到,就不做过多探究,笔者认为,这应该是Spring提供的一种方便外部扩展的手段,让开发者能够更加灵活的实例化一个bean。

  1. 接下来我们通过不同的创建bean的手段,来分别验证对象的实例化方法

通过@compent,@Service等注解的方式

测试代码:

public class Main {
    public static void main(String[] args) {
        // 通过配置类扫描
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
        System.out.println(ac.getBean(Service.class));
    }
}

@Component
public class Service {

}

观察debug:

debug112901.jpg

可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊的处理的时候,默认会使用无参构造函数进行对象的实例化

  • 通过普通XML的方式(同@compent注解,这里就不赘诉了)
  • 通过@Configuration注解的方式

测试代码:

public class Main {
    public static void main(String[] args) {
        // 通过配置类扫描
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
        // 这里将测试对象换为config即可,同时记得将条件断点更改为beanName.equlas("config")
        System.out.println(ac.getBean(config.class));
    }
}

debug112902.jpg

同样,断点也进入最后一行

通过@Bean的方式

测试代码:

@Configuration
@ComponentScan("com.dmz.official")
public class Config {
    @Bean
    public Service service(){
        return new Service();
    }
}

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(Config.class);
        System.out.println(ac.getBean("service"));
    }
}

断点结果:

debug112903.jpg

可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod的方法进行实例化对象的。Spring会在我们需要实例化的这个对象对应的BeanDefinition中记录factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同时会记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。

这里我们需要注意几个概念:

  1. 这里所说的通过静态工厂方式通过factoryBeanName获取一个Bean,注意,这个Bean,不是一个FactoryBean。也就是说不是一个实现了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我们在后面的文章会认真分析
  2. 提到了一个概念BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。不懂可以暂且跳过,后面有文章会讲到。

通过静态工厂方法的方式

测试代码:

public static void main(String[] args) {
    ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application.xml");
    System.out.println(cc.getBean("service"));
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    <bean id="myServiceImpl" class="com.dmz.official.service.Service"/>-->

    <!-- the factory bean, which contains a method called get() -->
    <bean id="myFactoryBean" class="com.dmz.official.service.MyFactoryBean">
        <!-- inject any dependencies required by this locator bean -->
    </bean>

    <!-- 测试实例工厂方法创建对象-->
    <bean id="clientService"
          factory-bean="myFactoryBean"
          factory-method="get"/>

    <!--测试静态工厂方法创建对象-->
    <bean id="service"
          class="com.dmz.official.service.MyFactoryBean"
          factory-method="staticGet"/>
</beans>

断点如下:

debug112904.jpg

可以发现,这种情况也进入了instantiateUsingFactoryMethod方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:

  1. 通过<bean>标签中的class属性得到一个Class对象
  2. 通过Class对象获取到对应的方法名称的Method对象
  3. 最后反射调用Method.invoke(null,args)

因为是静态方法,方法在执行时,不需要一个对象。

通过实例工厂方法的方式

测试代码(配置文件不变):

public static void main(String[] args) {
    ClassPathXmlApplicationContext cc =
        new ClassPathXmlApplicationContext("application.xml");
    System.out.println(cc.getBean("clientService"));
}

断点如下:

debug112905.jpg

还是执行的这个方法。这个方法的执行过程我断点跟踪了以后,发现跟@Bean方式执行的流程是一样的。这里也不再赘述了。

到这里,这段代码我们算结合官网大致过了一遍。其实还遗留了以下几个问题:

  1. Spring是如何推断构造函数的?我们在上面验证的都是无参的构造函数,并且只提供了一个构造函数
  2. Spring是如何推断方法的?不管是静态工厂方法,还是实例工厂方法的方式,我们都只在类中提供了一个跟配置匹配的方法名,假设我们对方法进行了重载呢?

要说清楚这两个问题需要比较深入的研究代码,同时进行测试。我们在官网学习过程中,暂时不去强求这类问题。这里提出来是为了在源码学习过程中,我们可以带一定目的性去阅读。

实例化总结

  1. 对象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期,这里需要大家注意
  2. Spring官网上指明了,在Spring中实例化一个对象有三种方式:
  • 构造函数
  • 实例工厂方法
  • 静态工厂方法
  1. 我自己总结如下结论:

Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个Beanfinition对象。通过这个对象可以实例化出一个java bean对象。主要流程如图:

debug112906.jpg

这篇文章到这里就结束了,主要学习了Spring官网中的1.2,1.3两小节。下篇文章,我们开始学习1.4中的知识。主要涉及到依赖注入的一些内容,也是我们Spring中非常重要的一块内容哦!下篇文章再见!

如果想第一时间看到更多更新的技术文章,请关注微信公众号:1点25
image.png

Guess you like

Origin www.cnblogs.com/nicerblog/p/12179851.html