@Scope注解 、@Lazy注解 -【Spring底层原理】

Spring框架底层原理 专栏收录该内容
17 篇文章 0 订阅

目录

一、注解用法

二、实例分析

三、源码追踪

四、总结


一、注解用法

【1】@Scope注解

@Scope注解是用来控制实例作用域的,单实例还是多实例,该注解可以作用在类和方法上面,通过属性来控制作用域,如下:

  • prototype:多实例,IOC容器启动的时候并不会创建对象放在容器中,每次获取的时候才会调用方法创建对象
  • singleton:单实例,IOC容器启动的时候就会调用方法创建对象放到容器中,以后每次获取都是从容器map中拿同一个对象
  • request:同一次请求创建一个实例
  • session:同一个session创建一个实例

【2】@Lazy注解

@Lazy注解,懒加载:即容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化。

一般的单实例Bean,默认在容器启动的时候创建对象,因此可以使用@Lazy注解改变这一特性,通过加上@Lazy注解,让容器启动不创建对象,第一次使用的时候再创建Bean对象。

二、实例分析

我们可以通过实例来分析证明上面注解的用法,这里有两个问题需要验证:

  1. 单例还是多例
  2. 何时创建的对象(容器启动的时候创建还是方法调用的时候创建)

同样以spring工程为例,咱们来进行验证

【1】单例还是多例

这里使用applicationContext来获取对象,按照id来获取,获取之后进行对比,看是否是同一个对象

// 启动类
public class MainTest {
    @Test
    public void TestMain(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        // 按照id获取bean
        Object user = applicationContext.getBean("user");
        Object user2 = applicationContext.getBean("user");
        System.out.println(user == user2);
    }
}

// 配置类
@Configuration
public class AppConfig {
    // 默认是单例的
    // @Scope("prototype"):多实例,IOC容器启动的时候并不会创建对象放在容器中,每次获取的时候才会调用方法创建对象
    // @Scope("singleton"):单实例,IOC容器启动的时候就会调用方法创建对象放到容器中,以后每次获取都是从容器map中拿同一个对象
    // @Scope("request"):同一次请求创建一个实例
    // @Scope("session"):同一个session创建一个实例
    @Scope("singleton")
    @Bean
    // @Lazy
    public User user(){
        return new User();
    }
}

// User的bean
public class User {
    public User() {
        System.out.println("User对象");
    }
}

运行启动类,可以看到user == suer2是true,也就是同一个对象,这是@Scope("singleton")注解的作用,当然,默认情况也是这样

image-20210131235826642

接下来,咱们改为@Scope("prototype")注解,也就是多实例模式,运行启动类,可以看到,返回值是false,也就是说两次创建的对象不是同一个,是多例的(其实这里也可以看到,两次打印,上面只创建了一个User对象,而这里创建了两个User对象,这里就已经能说明单例和多例模式下何时创建对象的了,下面还会进行发分析)

image-20210201000858114

【2】对象何时创建

上面我们提到,单例模式是下容器启动的时候创建的对象,而多例模式是在方法调用的时候创建的对象,所以第一次创建一个,第二次创建两个,这也证实了上面的结论。为了更直观的查看,我们对案例进行如下修改

@Scope注解

我们先将启动类的获取Bean方法注释掉,只留下容器启动的方法,然后运行,

单例模式下(@Scope("singleton")):可以看到,创建了User对象

image-20210201001654209

多例子模式下(@Scope("prototype")):可以看到,没有创建对象

image-20210201001826342

因为没有了方法调用,所以只有在容器启动的时候才能创建对象,所以单例模式是在容器启动的时候创建对象的。

@Lazy注解

单例模式下,是可以在容器启动的时候创建对象的,而使用了@Lazy懒加载注解后,可以改变这一特性,让对象在方法调用的时候再创建

加上@Lazy注解,打印结果如下,可以看到并没有创建对象:

image-20210201003837395

放开启动类的获取Bean方法注释,咱们再次运行,可以看到创建了一个对象:

image-20210201003956197

这也证明了:通过加上@Lazy注解,让容器启动不创建对象,第一次使用的时候再创建Bean对象。

三、源码追踪

这里对单例和多例实例化过程底层原理进行源码追踪

咱们直接来到BeanFactoryPostProcessor接口,找到和Scope注解相关的实现类,进入源码查看:

 

image-20210201003956197

在这里,调用registerScope方法,继续跟踪

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    if (this.scopes != null) {
        this.scopes.forEach((scopeKey, value) -> {
            if (value instanceof Scope) {
                beanFactory.registerScope(scopeKey, (Scope)value);
            } else {
                Class scopeClass;
                if (value instanceof Class) {
                    scopeClass = (Class)value;
                    Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
                    beanFactory.registerScope(scopeKey, (Scope)BeanUtils.instantiateClass(scopeClass));
                } else {
                    if (!(value instanceof String)) {
                        throw new IllegalArgumentException("Mapped value [" + value + "] for scope key [" + scopeKey + "] is not an instance of required type [" + Scope.class.getName() + "] or a corresponding Class or String value indicating a Scope implementation");
                    }

                    scopeClass = ClassUtils.resolveClassName((String)value, this.beanClassLoader);
                    Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
                    beanFactory.registerScope(scopeKey, (Scope)BeanUtils.instantiateClass(scopeClass));
                }
            }

        });
    }

}

这里是进行注册Scope,在该类中,有个doGetBean方法,所有的bean的创建都会去调用该方法,咱们继续跟踪源码,看Scope是如何创建的:

public void registerScope(String scopeName, Scope scope) {
    Assert.notNull(scopeName, "Scope identifier must not be null");
    Assert.notNull(scope, "Scope must not be null");
    if (!"singleton".equals(scopeName) && !"prototype".equals(scopeName)) {
        Scope previous = (Scope)this.scopes.put(scopeName, scope);
        if (previous != null && previous != scope) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
        }

    } else {
        throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
    }
}

AbstractBeanFactory类中doGetBean方法:

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    // 转化传入的Bean,如果是别名转真名,如果是&开头,去掉&。&代表取factoryBean
    String beanName = this.transformedBeanName(name);
    // 1、这里为了解决Singleton循环依赖,如果允许早期暴露,会在这里取到其早期暴露,后面会介绍
    // 2、如果是工厂模式,在获取bean的时候,在这里会先获取到factoryBean
    Object sharedInstance = this.getSingleton(beanName);
    Object bean;
    // 如果 sharedInstance 不为空,说明当前singleton是依赖者在注入属性时创建的。则打印相关日志
    if (sharedInstance != null && args == null) {
        if (this.logger.isTraceEnabled()) {
            if (this.isSingletonCurrentlyInCreation(beanName)) {
                this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
            } else {
                this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }

        // 这里会将name和beanName同时传进来, 因为一开始我们的name可能是带&符号的,代表这里需要获取factoryBean而不是bean
        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
    } else {
        if (this.isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // 判断是否有父容器,如果有父容器,优先用父容器调用其getBean
        BeanFactory parentBeanFactory = this.getParentBeanFactory();
        if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
            String nameToLookup = this.originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
            }

            if (args != null) {
                return parentBeanFactory.getBean(nameToLookup, args);
            }

            if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }

            return parentBeanFactory.getBean(nameToLookup);
        }

        if (!typeCheckOnly) {
            this.markBeanAsCreated(beanName);
        }

        StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate").tag("beanName", name);

        try {
            if (requiredType != null) {
                beanCreation.tag("beanType", requiredType::toString);
            }

            // 获取目标bean的definition
            RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
            this.checkMergedBeanDefinition(mbd, beanName, args);
            // 实例化依赖bean
            String[] dependsOn = mbd.getDependsOn();
            String[] var12;
            if (dependsOn != null) {
                var12 = dependsOn;
                int var13 = dependsOn.length;

                for(int var14 = 0; var14 < var13; ++var14) {
                    String dep = var12[var14];
                    if (this.isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }

                    this.registerDependentBean(dep, beanName);

                    try {
                        this.getBean(dep);
                    } catch (NoSuchBeanDefinitionException var33) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var33);
                    }
                }
            }

            // 这里就是和单例相关的,开始创建Singleton
            if (mbd.isSingleton()) {
                // 这里用了一个实现ObjectFactory接口 的 lambda表达式。getSingleton会有对这个createBean,有个前置操作和后置操作,中间调用lambda进行创建
                sharedInstance = this.getSingleton(beanName, () -> {
                    try {
                        return this.createBean(beanName, mbd, args);
                    } catch (BeansException var5) {
                        this.destroySingleton(beanName);
                        throw var5;
                    }
                });
                bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } 
            // 多例相关,创建Prototype
            else if (mbd.isPrototype()) {
                var12 = null;

                Object prototypeInstance;
                try {
                    this.beforePrototypeCreation(beanName);
                    prototypeInstance = this.createBean(beanName, mbd, args);
                } finally {
                    this.afterPrototypeCreation(beanName);
                }

                bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            } else {
                String scopeName = mbd.getScope();
                if (!StringUtils.hasLength(scopeName)) {
                    throw new IllegalStateException("No scope name defined for bean ��" + beanName + "'");
                }

                Scope scope = (Scope)this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }

                try {
                    // scope创建和single差不多, 也是调用get by beanName + lambda表达式, 这个scope会对scopeBean生命进行管理
                    Object scopedInstance = scope.get(beanName, () -> {
                        // 也有before, 只是和singleton有点不一样, 它的before和after封装在lambda中
                        this.beforePrototypeCreation(beanName);

                        Object var4;
                        try {
                            var4 = this.createBean(beanName, mbd, args);
                        } finally {
                            this.afterPrototypeCreation(beanName);
                        }

                        return var4;
                    });
                    bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                } catch (IllegalStateException var32) {
                    throw new ScopeNotActiveException(beanName, scopeName, var32);
                }
            }
        } catch (BeansException var35) {
            beanCreation.tag("exception", var35.getClass().toString());
            beanCreation.tag("message", String.valueOf(var35.getMessage()));
            this.cleanupAfterBeanCreationFailure(beanName);
            throw var35;
        } finally {
            beanCreation.end();
        }
    }

    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            } else {
                return convertedBean;
            }
        } catch (TypeMismatchException var34) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var34);
            }

            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    } else {
        return bean;
    }
}

这个方法比较长,内容比较多,咱们挑重要的分开来进行讲解:

【1】获取单例对象

if (sharedInstance != null && args == null) {
    if (this.logger.isTraceEnabled()) {
        if (this.isSingletonCurrentlyInCreation(beanName)) {
            this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
        } else {
            this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
        }
    }

    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
    // 创建对象
}

首先,会去spring的容器里面根据名字查找单例,如果获取到单例实例对象,就直接返回该实例,如果获取不到,则进入到else分支,就是创建单例,创建单例之前,首先会把目标bean里面的所有依赖bean(并非@Autowired注解的bean,而是xml用到depend-on标签,或者是参数值引用的bean)全都创建完才创建目标bean,部分代码如下:

【2】实例化依赖bean

// 获取目标bean的definition
RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
// 实例化依赖bean
String[] dependsOn = mbd.getDependsOn();
String[] var12;
if (dependsOn != null) {
    var12 = dependsOn;
    int var13 = dependsOn.length;

    for(int var14 = 0; var14 < var13; ++var14) {
        String dep = var12[var14];
        if (this.isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
        }

        this.registerDependentBean(dep, beanName);

        try {
            this.getBean(dep);
        } catch (NoSuchBeanDefinitionException var33) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var33);
        }
    }
}

从源码可以看出,依赖bean会在目标bean之前被创建出来。创建完依赖bean之后,就开始创建目标bean,首先他会先判断bean的定义是否是单例,如果是,会先去缓存查找是否已经创建了单例,如果创建了直接返回,否则创建单例并返回;如果不是则创建多例,创建单例的部分代码如下:

【3】创建单例对象

if (mbd.isSingleton()) 
{
    sharedInstance = this.getSingleton(beanName, () -> {
        try {
            return this.createBean(beanName, mbd, args);
        } catch (BeansException var5) {
            this.destroySingleton(beanName);
            throw var5;
        }
    });
    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

如果是多例则会进入到多例的创建流程,部分代码如下:

【4】创建多例对象

if (mbd.isPrototype()) {
    var12 = null;

    Object prototypeInstance;
    try {
        this.beforePrototypeCreation(beanName);
        prototypeInstance = this.createBean(beanName, mbd, args);
    } finally {
        this.afterPrototypeCreation(beanName);
    }
    bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

单例和多例创建的差异:

  • 单例创建完后spring会记录他的实例状态并保存到缓存里面,下次创建会优先取缓存里的实例
  • 多例的创建则是直接创建实例化对象,并不会记录他的实例

在上面的代码里面,bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);出现的比较频繁,我们知道,spring里面有一种叫做bean工厂的,就是实现了接口FactoryBean的类,他的实例并不是真正的bean,他是一特定类的创建的工厂类,所以工厂的bean实例化完后并不能直接返回,bean工厂的具体的实例化对象是通过接口FactoryBean里面的方法getObject来实现的,所以如果是工厂类则需要特殊处理,这也是正是这句代码的作用,我们来看下它的实现:

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        } else if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        } else {
            if (mbd != null) {
                mbd.isFactoryBean = true;
            }

            return beanInstance;
        }
    } else if (!(beanInstance instanceof FactoryBean)) {
        return beanInstance;
    } else {
        Object object = null;
        if (mbd != null) {
            mbd.isFactoryBean = true;
        } else {
            object = this.getCachedObjectForFactoryBean(beanName);
        }

        if (object == null) {
            FactoryBean<?> factory = (FactoryBean)beanInstance;
            if (mbd == null && this.containsBeanDefinition(beanName)) {
                mbd = this.getMergedLocalBeanDefinition(beanName);
            }

            boolean synthetic = mbd != null && mbd.isSynthetic();
            object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);
        }

        return object;
    }
}

从代码里我们可以看出,要是如果是个普通的bean则就会直接返回实例对象,如果是个工厂bean,则会去执行获取工厂里的实例化对象。

四、总结

主要的实现都是在doGetBean方法中进行的

  1. doGetBean方法中,通过getSingleton(beanName)先去缓存中根据名字获取单例对象,获取到则直接返回
  2. 如果获取不到就创建实例对象,创建之前会先创建依赖Bean,创建完依赖Bean后就开始创建目标Bean
  3. 创建的时候会判断bean的定义是否是单例
  4. 是单例==>去缓存查找是否已经创建了单例,如果创建了直接返回
  5. 不是单例==>则创建多例
  • 0
    点赞
  • 2
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值