Spring源码解析(二十)-Bean 的实例化策略InstantiationStrategy

怀念过去,不满当下,迷惑未来,那么,努力吧!总得找个方式让自己转移一下注意力。

Spring版本

5.2.5.RELEASE

参考

《芋道源码》

源码解读

1 InstantiationStrategy

《Spring源码解析(九)-创建bean实例》中,使用了instantiate方法来执行Spring Bean实例化策略:

// 如果候选方法只有一个,并且没有要求参数,且有无参构造方法,那么,满足创建bean的要求,进行实例化
            if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
                Method uniqueCandidate = candidates.get(0);
                // 如果唯一的候选方法参数个数为=0,满足mbd的无参构造方法
                if (uniqueCandidate.getParameterCount() == 0) {
                    mbd.factoryMethodToIntrospect = uniqueCandidate;
                    synchronized (mbd.constructorArgumentLock) {
                        mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
                        mbd.constructorArgumentsResolved = true;
                        mbd.resolvedConstructorArguments = EMPTY_ARGS;
                    }
                    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
                    return bw;
                }
            }

其中,instantiate方法实现如下:

private Object instantiate(String beanName, RootBeanDefinition mbd,
            @Nullable Object factoryBean, Method factoryMethod, Object[] args) {

        try {
            if (System.getSecurityManager() != null) {
                return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        this.beanFactory.getInstantiationStrategy().instantiate(
                                mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
                        this.beanFactory.getAccessControlContext());
            }
            else {
                // 通过反射调用对应方法进行实例化
                return this.beanFactory.getInstantiationStrategy().instantiate(
                        mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean instantiation via factory method failed", ex);
        }
    }

可以看到通过getInstantiationStrategy()方法调用了对应的实例化策略进行实例化,getInstantiationStrategy()返回一个InstantiationStrategy对象,查看源码可以发现:InstantiationStrategy接口定义了 Spring Bean 实例化的策略,有具体以下三种实现策略:

  • 无参构造
  • 有参构造
  • 工厂方法
public interface InstantiationStrategy {

    // 无参构造
    Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
            throws BeansException;

    // 有参构造
    Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
            Constructor<?> ctor, Object... args) throws BeansException;

    // 工厂方法
    Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
            @Nullable Object factoryBean, Method factoryMethod, Object... args)
            throws BeansException;

}

InstantiationStrategy有俩个实现类:

  • SimpleInstantiationStrategy
  • CglibSubclassingInstantiationStrategy

2 SimpleInstantiationStrategy

该类实现了InstantiationStrategy的三个方法

2.1 无参构造

    @Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        // 如果没有replace-method、lookup-method,以及使用@Lookup注解,那么使用反射进行实例化
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                // 获取构造方法
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                // 未能获取到构造方法,尝试通过clazz获取
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    // 接口不准实例化
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        // 再次获取构造方法
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                        }
                        else {
                            constructorToUse = clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            // 通过构造方法实例化对象
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            // 否则,创建cglib子类实例化
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

首先判断是否存在replace-methodlookup-method,以及使用@Lookup注解:

  • 不存在,获取构造方法,之后通过构造方法进行实例化
  • 存在,调用instantiateWithMethodInjection方法

查看instantiateWithMethodInjection方法,可以看到在SimpleInstantiationStrategy中是默认不实现的:

    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
    }

交由CglibSubclassingInstantiationStrategy进行实现

2.2 有参构造

    @Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
            final Constructor<?> ctor, Object... args) {

        // 如果没有replace-method、lookup-method,以及使用@Lookup注解,那么使用反射进行实例化
        if (!bd.hasMethodOverrides()) {
            if (System.getSecurityManager() != null) {
                // use own privileged to change accessibility (when security is on)
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    ReflectionUtils.makeAccessible(ctor);
                    return null;
                });
            }
            // 使用cror构造方法通过反射实例化
            return BeanUtils.instantiateClass(ctor, args);
        }
        else {
            // 否则,创建cglib子类实例化
            return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
        }
    }

同样,判断是否存在replace-methodlookup-method,以及使用@Lookup注解:

  • 不存在,通过传入的构造方法进行实例化
  • 存在,调用instantiateWithMethodInjection方法

2.3 工厂方法

    @Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
            @Nullable Object factoryBean, final Method factoryMethod, Object... args) {

        try {
            // 设置method方法可访问
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    ReflectionUtils.makeAccessible(factoryMethod);
                    return null;
                });
            }
            else {
                ReflectionUtils.makeAccessible(factoryMethod);
            }

            Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
            try {
                currentlyInvokedFactoryMethod.set(factoryMethod);
                // 通过反射调用工厂方法获取实例
                Object result = factoryMethod.invoke(factoryBean, args);
                if (result == null) {
                    result = new NullBean();
                }
                return result;
            }
            finally {
                if (priorInvokedFactoryMethod != null) {
                    currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
                }
                else {
                    currentlyInvokedFactoryMethod.remove();
                }
            }
        }
    // 省略
}

先通过ReflectionUtils.makeAccessible(factoryMethod)设置工厂方法可以访问,之后通过Object result = factoryMethod.invoke(factoryBean, args)反射调用工厂方法获取实例

3 CglibSubclassingInstantiationStrategy

    @Override
    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        return instantiateWithMethodInjection(bd, beanName, owner, null);
    }

    @Override
    protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
            @Nullable Constructor<?> ctor, Object... args) {

        // Must generate CGLIB subclass...
        return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
    }

在第二个重载方法中,可以看到构造了一个CglibSubclassCreator对象,之后通过instantiate进行了实例化:

        public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
            // 创建代理类
            Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
            Object instance;
            if (ctor == null) {
                // 如果构造器为空,则通过反射获取实例
                instance = BeanUtils.instantiateClass(subclass);
            }
            else {
                try {
                    // 获取代理类构造方法
                    Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
                    // 进行实例化
                    instance = enhancedSubclassConstructor.newInstance(args);
                }
                catch (Exception ex) {
                    throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
                            "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
                }
            }
            // SPR-10785: set callbacks directly on the instance instead of in the
            // enhanced class (via the Enhancer) in order to avoid memory leaks.
            Factory factory = (Factory) instance;
            factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
                    new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
                    new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
            return instance;
        }

3.1 createEnhancedSubclass

        private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanDefinition.getBeanClass());
            // 设置命名策略
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            if (this.owner instanceof ConfigurableBeanFactory) {
                ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
                enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
            }
            // 添加回调过滤器
            enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
            enhancer.setCallbackTypes(CALLBACK_TYPES);
            return enhancer.createClass();
        }

MethodOverrideCallbackFilter是用来定义 CGLIB 回调过滤方法的拦截器行为,它继承 CglibIdentitySupport实现CallbackFilter 接口:

    private static class MethodOverrideCallbackFilter extends CglibIdentitySupport implements CallbackFilter {

        private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);

        public MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
            super(beanDefinition);
        }

        @Override
        public int accept(Method method) {
            MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
            if (logger.isTraceEnabled()) {
                logger.trace("MethodOverride for " + method + ": " + methodOverride);
            }
            if (methodOverride == null) {
                return PASSTHROUGH;
            }
            else if (methodOverride instanceof LookupOverride) {
                return LOOKUP_OVERRIDE;
            }
            else if (methodOverride instanceof ReplaceOverride) {
                return METHOD_REPLACER;
            }
            throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
                    methodOverride.getClass().getName());
        }
    }

它实现了accept方法,返回的PASSTHROUGHLOOKUP_OVERRIDEMETHOD_REPLACER对应的是CALLBACK_TYPES数组的元素下标:

private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
                {NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};

可以看到这里定义了LookupOverrideMethodInterceptorReplaceOverrideMethodInterceptor,通过这俩个拦截器对lookup-methodreplace-method进行了处理。

3.2 LookupOverrideMethodInterceptor

    private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {

        private final BeanFactory owner;

        public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
            super(beanDefinition);
            this.owner = owner;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
            // Cast is safe, as CallbackFilter filters are used selectively.
            LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
            Assert.state(lo != null, "LookupOverride not found");
            Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
            // 通过getBean获取实例,然后返回该实例,以起到替换作用
            if (StringUtils.hasText(lo.getBeanName())) {
                return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                        this.owner.getBean(lo.getBeanName()));
            }
            else {
                return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
                        this.owner.getBean(method.getReturnType()));
            }
        }
    }

3.3 ReplaceOverrideMethodInterceptor

    private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {

        private final BeanFactory owner;

        public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
            super(beanDefinition);
            this.owner = owner;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
            ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
            Assert.state(ro != null, "ReplaceOverride not found");
            // TODO could cache if a singleton for minor performance optimization
            MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
            return mr.reimplement(obj, method, args);
        }
    }

总结

本文主要学习了spring实例化bean对几种策略,及其对lookup-methodreplace-method的代理处理,不过平时对于lookup-methodreplace-method的使用较少,个人认为稍微理解一下就行,不必过于纠结细节,否则,学习起来进度应该相当缓慢,进而影响坚持下去的动力。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335