Spring中RequestScope作用域Bean原理

一冬殃、前言

阿里巴巴長期招聘Java研發(fā)工程師p6,p7,p8等上不封頂級(jí)別滔迈,有意向的可以發(fā)簡(jiǎn)歷給我,注明想去的部門和工作地點(diǎn):1064454834@qq.com_

web.xml里面配置


<listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

component-bean.xml里面配置

 <bean id="lavaPvgInfo" class="com.alibaba.lava.privilege.PrivilegeInfo"
        scope="request">
        <property name="aesKey" value="666" />
        <aop:scoped-proxy />
    </bean>

測(cè)試Rpc

@WebResource("/testService")
public class TestRpc {

    @Autowired
    private PrivilegeInfo pvgInfo;

    @ResourceMapping("test")
    public ActionResult test(ErrorContext context) {
        ActionResult result = new ActionResult();

        String aseKey = pvgInfo.getAesKey();
        pvgInfo.setAesKey("888");
        System.out.println("aseKey---" + aseKey);

        return result;
    }
}

二、源碼分析

2.1 使用裝飾模式對(duì)Bean定義進(jìn)行修改

先上時(shí)序圖:


screenshot.png

可知上面時(shí)序圖完成了對(duì)RequestScope對(duì)象定義的修改創(chuàng)建了代理bean烂斋,具體修改內(nèi)容是修改了beanClass為ScopedProxyFactoryBean辽俗,并且保存了原來的bean定義originatingBeanDefinition。

下面看下主要代碼ScopedProxyUtils中的createScopedProxy

public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
            BeanDefinitionRegistry registry, boolean proxyTargetClass) {
        
        String originalBeanName = definition.getBeanName();
        BeanDefinition targetDefinition = definition.getBeanDefinition();

        // 保持原來的beanName不變拴魄,但是基于原來的bean定義創(chuàng)建代理bean定義,
        // 保存原來的bean定義到代理bean里面為后面創(chuàng)建代理類做準(zhǔn)備.
        RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setOriginatingBeanDefinition(definition.getBeanDefinition());
        proxyDefinition.setSource(definition.getSource());
        proxyDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

        String targetBeanName = getTargetBeanName(originalBeanName);
        proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);

        if (proxyTargetClass) {
            targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
        }
        else {
            proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
        }

        // Copy autowire settings from original bean definition.
        proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
        proxyDefinition.setPrimary(targetDefinition.isPrimary());
        if (targetDefinition instanceof AbstractBeanDefinition) {
            proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
        }

        // The target bean should be ignored in favor of the scoped proxy.
        targetDefinition.setAutowireCandidate(false);
        targetDefinition.setPrimary(false);

        // 注冊(cè)代理前的bean到容器冗茸,在創(chuàng)建代理bean時(shí)候使用.targetBeanName=scopedTarget.lavaPvgInfo
        registry.registerBeanDefinition(targetBeanName, targetDefinition);

        // 返回代理bean定義作為原來的bean定義
        return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }

2.2 創(chuàng)建代理Bean

先上時(shí)序圖


screenshot.png

主要代碼如下:

    public void setBeanFactory(BeanFactory beanFactory) {
        ...
        ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

        this.scopedTargetSource.setBeanFactory(beanFactory);

               //創(chuàng)建代理工廠
        ProxyFactory pf = new ProxyFactory();
        pf.copyFrom(this);
        pf.setTargetSource(this.scopedTargetSource);

        ...
        // Add an introduction that implements only the methods on ScopedObject.
        ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
        pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

        // Add the AopInfrastructureBean marker to indicate that the scoped proxy
        // itself is not subject to auto-proxying! Only its target bean is.
        pf.addInterface(AopInfrastructureBean.class);

        this.proxy = pf.getProxy(cbf.getBeanClassLoader());
    }
public Object getProxy(ClassLoader classLoader) {
            ....

        try {//獲取目標(biāo)類席镀,也就是被代理的
            Class rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class[] additionalInterfaces = rootClass.getInterfaces();
                for (Class additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }

            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass);

            // Configure CGLIB Enhancer...
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
                       //設(shè)置被代理類為超類,這樣解釋了為啥代理后的類能夠賦值給被代理類不會(huì)發(fā)生錯(cuò)誤
            enhancer.setSuperclass(proxySuperClass);
            enhancer.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setInterceptDuringConstruction(false);

                       //獲取攔截器夏漱,其中就有DynamicAdvisedInterceptor
            Callback[] callbacks = getCallbacks(rootClass);
            enhancer.setCallbacks(callbacks);
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));

            Class[] types = new Class[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            enhancer.setCallbackTypes(types);

            // Generate the proxy class and create a proxy instance.
            Object proxy;
            if (this.constructorArgs != null) {
                proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
            }
            else {
                proxy = enhancer.create();
            }

            return proxy;
        }

2.3 調(diào)用時(shí)序圖

screenshot.png

代碼:

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

        ...
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Class targetClass = null;
            Object target = null;
            try {
                if (this.advised.exposeProxy) {
                    // Make invocation available if necessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                // 獲取被代理類
                target = getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
                // Check whether we only have one InvokerInterceptor: that is,
                // no real advice, but just reflective invocation of the target.
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    
                    retVal = methodProxy.invoke(target, args);
                }
                else {
                    // We need to create a method invocation...
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = massageReturnTypeIfNecessary(proxy, target, method, retVal);
                return retVal;
            }
            finally {
                if (target != null) {
                    releaseTarget(target);
                }
                if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }
}

getTarget是關(guān)鍵方法豪诲,看下:

    protected Object getTarget() throws Exception {
        return this.advised.getTargetSource().getTarget();
    }


    public Object getTarget() throws Exception {
        return getBeanFactory().getBean(getTargetBeanName());
    }

所以最后是從IOC獲取目標(biāo)類bean.下面看下getBean代碼:

//獲取RequestScope對(duì)象
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
    throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
   //調(diào)用RequestScope對(duì)象對(duì)象的get方法
    Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
        public Object getObject() throws BeansException {
            beforePrototypeCreation(beanName);
            try {
                return createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
        }
    });
    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
    throw new BeanCreationException(beanName,
            "Scope '" + scopeName + "' is not active for the current thread; " +
            "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
            ex);
}

requestscope的get方法:

    public Object get(String name, ObjectFactory objectFactory) {
               //獲取當(dāng)前線程屬性集合
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        Object scopedObject = attributes.getAttribute(name, getScope());
        if (scopedObject == null) {//不在屬性集則調(diào)用createBean創(chuàng)建,然后放入集合
            scopedObject = objectFactory.getObject();
            attributes.setAttribute(name, scopedObject, getScope());
        }
        return scopedObject;
    }

可知requestAttributesHolder屬性是threadlocal

public abstract class RequestContextHolder  {
    
    private static final boolean jsfPresent =
            ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());

    private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
            new NamedThreadLocal<RequestAttributes>("Request attributes");

    private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
            new NamedInheritableThreadLocal<RequestAttributes>("Request context");
}

歡迎關(guān)注微信公眾號(hào):技術(shù)原始積累 獲取更多技術(shù)干貨_

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挂绰,一起剝皮案震驚了整個(gè)濱河市屎篱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葵蒂,老刑警劉巖交播,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異刹勃,居然都是意外死亡堪侯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門荔仁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伍宦,“玉大人,你說我怎么就攤上這事乏梁〈瓮荩” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵遇骑,是天一觀的道長卖毁。 經(jīng)常有香客問我,道長落萎,這世上最難降的妖魔是什么亥啦? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮练链,結(jié)果婚禮上翔脱,老公的妹妹穿的比我還像新娘。我一直安慰自己媒鼓,他們只是感情好届吁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绿鸣,像睡著了一般疚沐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上潮模,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天亮蛔,我揣著相機(jī)與錄音,去河邊找鬼再登。 笑死尔邓,一個(gè)胖子當(dāng)著我的面吹牛晾剖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播梯嗽,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼齿尽,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了灯节?” 一聲冷哼從身側(cè)響起循头,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎炎疆,沒想到半個(gè)月后卡骂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡形入,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年全跨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亿遂。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浓若,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛇数,到底是詐尸還是另有隱情挪钓,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布耳舅,位于F島的核電站碌上,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏浦徊。R本人自食惡果不足惜馏予,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盔性。 院中可真熱鬧吗蚌,春花似錦、人聲如沸纯出。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暂筝。三九已至,卻和暖如春硬贯,著一層夾襖步出監(jiān)牢的瞬間焕襟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工饭豹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸵赖,地道東北人务漩。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像它褪,于是被迫代替她去往敵國和親饵骨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 文章作者:Tyan博客:noahsnail.com 3.4 依賴 標(biāo)準(zhǔn)企業(yè)應(yīng)用不會(huì)由一個(gè)對(duì)象(或Spring用語中...
    SnailTyan閱讀 1,170評(píng)論 0 1
  • Spring容器高層視圖 Spring 啟動(dòng)時(shí)讀取應(yīng)用程序提供的Bean配置信息茫打,并在Spring容器中生成一份相...
    Theriseof閱讀 2,796評(píng)論 1 24
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理居触,服務(wù)發(fā)現(xiàn),斷路器老赤,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,128評(píng)論 2 7
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法轮洋,類相關(guān)的語法,內(nèi)部類的語法抬旺,繼承相關(guān)的語法弊予,異常的語法,線程的語...
    子非魚_t_閱讀 31,587評(píng)論 18 399