美文网首页
DefaultListableBeanFactory和conte

DefaultListableBeanFactory和conte

作者: 非典型_程序员 | 来源:发表于2019-03-24 22:47 被阅读0次

今天想来继续学习一下spring的部分源码,上次学习spring源码我是使用的spring boot,这次继续使用spring boot来跟踪和学习。以前就是大概走看了spring boot大概的启动过程以及内置tomcat的创建和启动。今天就想深入的去学习一点知识,就准备看下 beanfactory这个类,即DefaultListableBeanFactory的源码,但是因为这个类的体系比较庞大,所以自己只能先简单的了解一下。

一、继承体系

首先明白一点,beanfactory是spring的一个很基础性接口,或者说根接口。spring boot的context容器中注入beanfactory实际就是DefaultListableBeanFactory实例,我们通过idea自带的工具先来看看,它体系结构是怎么样的,看下面的图:


图-1.png

我感觉还是非常复杂的,其中涉及到的注入通用接口主要有三个,BeanFactory、SingletonBeanRegistry和AliasRegistry。我们先看下这几个接口的方法都有哪些吧!
先看AliasRegistry,这个就是一个通用接口,主要管理bean的别名的,看下方法有那些:

public interface AliasRegistry {

    // 使用指定别名替换具体bean
    void registerAlias(String name, String alias);

    // 从注册容器中移除某个别名bean
    void removeAlias(String alias);

    // 给定的名称是否在注册容器中时以别名存在
    boolean isAlias(String name);

    // 返回给定名称的所有别名
    String[] getAliases(String name);

}

这几方法感觉还是很好理解,关于它的实现这里就先不看了,继续看下个接口SingletonBeanRegistry。


SingletonBeanRegistry,看名字就知道这个是一个单列bean的注册容器,它主要是为单列bean提供通用的注册接口,可以通过BeanFactory实现来实现,以便以统一的方式公开其单例管理工具。我们还是看下代码

public interface SingletonBeanRegistry {

    // 使用给定的名称注册单列bean对象,这个bean会被认为是已经完全初始化过的
    void registerSingleton(String beanName, Object singletonObject);

    // 根据名称返回某个单例bean,只会检查已经实例化的bean
    @Nullable
    Object getSingleton(String beanName);

    // 检查容器中是否已经注册了某个单例bean,其名称与给定名称一致
    boolean containsSingleton(String beanName);

    // 返回注册容器中所有单例bean的名称
    String[] getSingletonNames();

    // 返回注册容器中单例bean的数量
    int getSingletonCount();

    // 返回注册容器中使用单例互斥锁
    Object getSingletonMutex();
}

方法不多,也比较好理解,其实看到这里我觉得大家应该也猜到了,存放单例bean的容器肯定是一个map来实现的。


接着就是非常重要的BeanFactory接口,它是用于访问Spring bean容器的根接口。这是bean容器的基本客户端视图,像其他的ListableBeanFactory和ConfigurableBeanFactory接口都是为了实现特定的目的。BeanFactory是应用程序组件的中央注册容器,并集中应用程序组件的配置。需要注意的是,最好通过DI(依赖注入)通过set或构造方法来配置应用程序对象,而不是使用像BeanFactory查找一样“拉”的形式配置。 Spring的DI功能是使用BeanFactory接口及其子接口实现的。下面看代码:

public interface BeanFactory {

    // 工厂bean的前缀,用来区分FactoryBean创建的bean和FactoryBean。比如,如果bean名称为myJndiObject,这是一个FactoryBean,而&myJndiObject将返回工厂,而不是工厂返回的实例。
    String FACTORY_BEAN_PREFIX = "&";

     // 返回给定名称bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
    Object getBean(String name) throws BeansException;

    // 返回指定名称和类型bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

    // 返回指定名称或者其他参数bean的实例,这个实例可以是共享(单例)的,也可以是独立的。
    // 这个方法可以指定显式构造参数或工厂方法参数,覆盖bean定义中指定的默认参数(如果有的话)。
    Object getBean(String name, Object... args) throws BeansException;

    // 返回唯一匹配给定对象类型的bean实例
    <T> T getBean(Class<T> requiredType) throws BeansException;

     // 返回指定类型或者其他参数bean的实例,这个实例可以是共享(单例)的,也可以是独立的。 
     // 这个方法可以指定显式构造参数或工厂方法参数,覆盖bean定义中指定的默认参数。
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

     // 此bean工厂是否包含具有给定名称的bean定义或外部注册的单例实例
    boolean containsBean(String name);

    // 根据名称查找的这个bean是否为共享单例。也就是说,getBean方法总是否会返回相同的实例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    // 根据名称查找的这个bean是否为prototype。也就是说,getBean方法总是否会返回不同的实例
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // 判断具有给定名字的bean是否与指定的类型一致。
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    // 判断具有给定名字的bean是否与指定的类型一致。
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    // 返回给定名称的bean的类型。
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    // 返回给定名称的bean别名
    String[] getAliases(String name);

}

这方法还是挺多的,但是也都还好理解,现在有个大概了解就好了,主要还是看具体的实现。关于接口方法就先到这里,这里主要是了解具体的体系以及一些基本的方法。

2、DefaultListableBeanFactory创建

自己在看源码的时候一直没有发现context中的beanFactory是什么时候创建的,一直跟源码才发现创建context时就会创建beanFactory,下面通过debug方法来跟踪看一下。下面是SpringApplication的run方法,整个应用的启动基本上在这里完成的,以前有专门说过这部分,这里就不再细述,我们主要看 context创建这里。

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();// 创建context
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

下面是createApplicationContext方法的代码

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

这里主要做了两件事,一是确定创建的context类型,给定了三种,分别是SERVLET、REACTIVE、default三种类型,他们对应的context容器也不相同

SERVLET:这个我们比较熟悉,但是在spring boot里面创建的context为:AnnotationConfigServletWebServerApplicationContext实例,名字比较长,但是名字也说明了很多东西,注解、可配置、servletWebServer。
REACTIVE:作为spring5以后新增的MVC应用,主要是基于事件驱动的WebFlux,这个自己也还没有去了解过,这种应用创建的context为:AnnotationConfigReactiveWebServerApplicationContext,和上面的类似,也是注解、可配置、但是是响应式的WebServer。应该也是基于Netty实现,应该和vert.x相似,应该也会使用lambda表达式来开发。
default:创建的context:AnnotationConfigApplicationContext,这个主要是应用于非web环境,但是使用spring一般是Web环境,非Web环境我暂时还没遇到过。

二就是实例化context容器,我们看下BeanUtils.instantiateClass方法的代码

public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
        Assert.notNull(clazz, "Class must not be null");
        if (clazz.isInterface()) {
            throw new BeanInstantiationException(clazz, "Specified class is an interface");
        }
        try {
            Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
                    KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
            return instantiateClass(ctor);
        }
        catch (NoSuchMethodException ex) {
            throw new BeanInstantiationException(clazz, "No default constructor found", ex);
        }
        catch (LinkageError err) {
            throw new BeanInstantiationException(clazz, "Unresolvable class definition", err);
        }
    }

这个方法有进一步作了判断,判断应用是否为使用kotlin开发,如果是的话返回这个类的一级构造函数(kotlin和Java稍微有点区别,自己也是很久之前看了点kotlin的基础),否则返回这个类的默认无参构造函数,接着调用instantiateClass方法对其进行实例化,我们接着往下看instantiateClass方法

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
        Assert.notNull(ctor, "Constructor must not be null");
        try {
            ReflectionUtils.makeAccessible(ctor);
            return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
                    KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
        }
        catch (InstantiationException ex) {
            throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
        }
        catch (IllegalAccessException ex) {
            throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
        }
        catch (IllegalArgumentException ex) {
            throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
        }
        catch (InvocationTargetException ex) {
            throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
        }
    }

这个方法,先通过反射工具类,设置该构造函数访问权限为可访问,接着依然需要判断是否为kotlin,是的话调用kotlin相关方法,否则调用Constructor的newInstance方法创建进行实例化。我debug模式下看到调用的为AnnotationConfigServletWebServerApplicationContext类默认的构造函数。

public AnnotationConfigServletWebServerApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

看到这里是否觉感觉有些奇怪,为什么没看到beanFactory实例化呢?肯定不止我一个人这么想,大家似乎忘了一件事,子类构造函数如果没有显性调用父类构造函数,那么它会隐性调用父类的无参构造函数,即调用那么都会默认调用super()。我们看下AnnotationConfigServletWebServerApplicationContext的继承体系


图-2.png

这个好像更复杂了,我们先不管这个。先找它的直接父类ServletWebServerApplicationContext,看默认构造函数

public ServletWebServerApplicationContext() {
}

没有找到,那么继续向上找,看GenericApplicationContext的默认构造,如下:

public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}

这里可以看到直接new了一个DefaultListableBeanFactory对象,并其赋值给成员变量beanFactory。也就是说必须要先完成DefaultListableBeanFactory的创建,然后才能完成整个应用context的实例化。今天暂时先到这里,关于DefaultListableBeanFactory的相关内容以后再继续吧(感觉至少要3篇),因为这个内容太多了,而且感觉也比较难,但是这个确实非常非常重要。

相关文章

网友评论

      本文标题:DefaultListableBeanFactory和conte

      本文链接:https://www.haomeiwen.com/subject/kitevqtx.html