Spring中RequestScope作用域Bean原理

一、前言

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>

测试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 使用装饰模式对Bean定义进行修改

先上时序图:

screenshot.png

可知上面时序图完成了对RequestScope对象定义的修改创建了代理bean,具体修改内容是修改了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定义创建代理bean定义,
        // 保存原来的bean定义到代理bean里面为后面创建代理类做准备.
        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);

        // 注册代理前的bean到容器,在创建代理bean时候使用.targetBeanName=scopedTarget.lavaPvgInfo
        registry.registerBeanDefinition(targetBeanName, targetDefinition);

        // 返回代理bean定义作为原来的bean定义
        return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }

2.2 创建代理Bean

先上时序图

screenshot.png

主要代码如下:

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

        this.scopedTargetSource.setBeanFactory(beanFactory);

               //创建代理工厂
        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 {//获取目标类,也就是被代理的
            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);
                }
            }
                       //设置被代理类为超类,这样解释了为啥代理后的类能够赋值给被代理类不会发生错误
            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 调用时序图

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是关键方法,看下:

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


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

所以最后是从IOC获取目标类bean.下面看下getBean代码:

//获取RequestScope对象
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
    throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
   //调用RequestScope对象对象的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) {
               //获取当前线程属性集合
        RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
        Object scopedObject = attributes.getAttribute(name, getScope());
        if (scopedObject == null) {//不在属性集则调用createBean创建,然后放入集合
            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");
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏曾大稳的博客

Android自定义GLSurfaceView

当我们需要把同一个场景渲染到不同的Surface上时,此时系统GLSurfaceView 就不能满足需求了,所以我们需要自己创建EGL环境来实现渲染操作。 注意...

41420
来自专栏微信公众号:Java团长

一个经典例子让你彻彻底底理解Java回调机制

以前不理解什么叫回调,天天听人家说加一个回调方法啥的,心里想我草,什么叫回调方法啊?然后自己就在网上找啊找啊找,找了很多也不是很明白,现在知道了,所谓回调:就是...

15340
来自专栏Android相关

CoordiantorLayout与Behavior

CoordinatorLayout继承自FrameLayout,并且实现了NestedScrollingParent2接口用于接收嵌套滑动的事件。并且内部定义了...

10320
来自专栏码匠的流水账

聊聊spring cloud gateway的GlobalFilter

本文主要研究一下spring cloud gateway的GlobalFilter

23810
来自专栏JavaQ

深入理解Spring系列之三:BeanFactory解析

《深入理解Spring系列之一:开篇》中提到在Spring容器启动的过程中,会将类解析成Spring内部的BeanDefinition结构,并将BeanDefi...

38840
来自专栏菩提树下的杨过

spring:如何用代码动态向容器中添加或移除Bean ?

先来看一张类图: ? 有一个业务接口IFoo,提供了二个实现类:FooA及FooB,默认情况下,FooA使用@Component由Spring自动装配,如果出于...

832100
来自专栏Java与Android技术栈

使用Kotlin高效地开发Android App(三)

Kotlin基于Java的空指针提出了一个空安全的概念,即每个属性默认不可为null。

11210
来自专栏刘望舒

ButterKnife原理解析看这篇文章就够了

ButterKnife 算是一款知名老牌 Android 开发框架了,通过注解绑定视图,避免了 findViewById() 的操作,广受好评!由于它是在编译时...

19310
来自专栏xingoo, 一个梦想做发明家的程序员

基于重叠IO模型的 回显TCP服务器设计

---------------------1 套接字对象---------------------- 为每个套接字创建一个SOCKET_OBJ对象,记录与之相关...

225100
来自专栏技术小黑屋

浅析WeakHashMap

在Java或者是Android编程中,我们一般都会使用到Map,比如HashMap这样的具体实现。更高级一点,我们可能会使用WeakHashMap。

43120

扫码关注云+社区

领取腾讯云代金券