美文网首页
7.Bean Scope

7.Bean Scope

作者: wyh001 | 来源:发表于2023-09-29 22:40 被阅读0次

IoC容器中的bean都有其作用域,如下图(来自官网):

Scope 描述
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

作用域不同的bean协作

考虑A对象依赖B对象,前者是单例,后者是原型。由于依赖注入B对象是在A对象初始化时进行的,一旦注入后,该依赖不会改变。运行时一直使用不变地(和单例效果相同)B对象,违背了B作用域设计为原型的初衷。

如下图所示,依赖查找范围为当前容器及其所有父容器

scope.png

单例依赖原型

示例:

// 原型
@Slf4j
public class Stest1Service {
}

// 单例
@Slf4j
public class Stest2Service {

    private Stest1Service stest1Service;

    // 依赖原型bean
    public void setStest1Service(Stest1Service stest1Service) {
        this.stest1Service = stest1Service;
    }

    public void test() {
        log.info("test=[{}]", stest1Service);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解配置 -->
    <context:annotation-config/>

    <!-- 原型bean-->
    <bean name="stest1Service" class="org.example.s7.service.Stest1Service" scope="prototype"/>

    <!-- 单例bean -->
    <bean name="stest2Service" class="org.example.s7.service.Stest2Service">
        <!-- 依赖原型bean -->
        <property name="stest1Service" ref="stest1Service"/>
    </bean>
</beans>
private static void s7_1() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
    context.setConfigLocation("s7/scope-demo.xml");
    context.refresh();

    final Stest2Service stest2Service = context.getBean(Stest2Service.class);
    // 每次打印相同的stest1Service引用
    stest2Service.test();
    stest2Service.test();
    context.close();
}

正确使用方式

对于不同作用域的bean协作,如:A依赖B,解决问题的一般思路是,在运行时每一次使用B对象,都从IoC容器中进行依赖查找,依赖查找bean会遵循Scope的定义。

方式1

每次都从IoC容器依赖查找

@Slf4j
public class Stest2Service {

    @Autowired
    private ApplicationContext applicationContext;

    public void test() {
        log.info("test=[{}]", getStest1Service());
    }

    // 每次使用stest1Service都从IoC容器中查找
    public Stest1Service getStest1Service() {
        return applicationContext.getBean(Stest1Service.class);
    }
}

方式2

使用lookup方法,运行时依赖查找

@Slf4j
public abstract class Stest3Service {

    public void test() {
        log.info("test=[{}]", createStest1Service());
    }

    // look-up方法
    // @Lookup
    public abstract Stest1Service createStest1Service();
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 原型bean-->
    <bean name="stest1Service" class="org.example.s7.service.Stest1Service" scope="prototype"/>

    <!-- 单例bean -->
    <bean name="stest3Service" class="org.example.s7.service.Stest3Service">
        <!-- 依赖原型bean -->
        <lookup-method name="createStest1Service" bean="stest1Service"/>
    </bean>
</beans>

方式3

使用ObjectFactory或者ObjectProvider

@Slf4j
public class Stest4Service {

    @Autowired
    private ObjectFactory<Stest1Service> stest1ServiceObjectFactory;

    @Autowired
    private ObjectProvider<Stest1Service> stest1ServiceObjectProvider;

    public void test() {
        log.info("test=[{}]", stest1ServiceObjectFactory.getObject());
        log.info("test=[{}]", stest1ServiceObjectProvider.getObject());
    }
}

方式4

使用ScopedProxyFactoryBean

使用注解

@Scope(scopeName = BeanDefinition.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Slf4j
public class Stest5Service {
}

或者xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 开启注解配置 -->
    <context:annotation-config/>

    <!-- 原型bean-->
    <bean name="stest5Service" class="org.example.s7.service.Stest5Service" scope="prototype">
        <!-- 使用scope代理 -->
        <aop:scoped-proxy/>
    </bean>

    <!-- 单例bean -->
    <bean name="stest6Service" class="org.example.s7.service.Stest6Service">
        <!-- 依赖原型bean -->
        <property name="stest5Service" ref="stest5Service"/>
    </bean>
</beans>

自定义Scope

  1. 实现org.springframework.beans.factory.config.Scope接口

  2. 自定义XxxScope注解,使用@Scope注解标注,设置proxyMode=ScopedProxyMode.TARGET_CLASS(可选操作)

  3. 注册步骤1的实例到IoC容器中:ConfigurableBeanFactory#registerScope

    • 使用容器生命周期接口BeanFactoryPostProcessor获取IoC容器并注册Scope

    • 使用CustomScopeConfigurer

Spring Cloud中RefreshScope的实现

  1. 实现Scope接口

    RefreshScope

  2. 自定义@RefreshScope注解

    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Scope("refresh")
    @Documented
    public @interface RefreshScope {
    
     /**
      * @see Scope#proxyMode()
      * @return proxy mode
      */
     ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
    
    }
    
  3. 实现BeanFactoryPostProcessor注册Scope

    RefreshScope类实现了该接口来注册自己

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
        throws BeansException {
        this.beanFactory = beanFactory;
        // 注册scope
        beanFactory.registerScope(this.name, this);
        setSerializationId(beanFactory);
    }
    

    同时作为普通bean注册到容器

    由于该类是一个BeanFactoryPostProcessor,所以需要注册到容器中,在java配置类中定义时使用static方法

    @Bean
    @ConditionalOnMissingBean(RefreshScope.class)
    public static RefreshScope refreshScope() {
        return new RefreshScope();
    }
    

相关文章

网友评论

      本文标题:7.Bean Scope

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