一,背景

Scope注解主要用于配置bean在容器中的生命周期,除了可以配置为singleton和prototype,在Web环境还可以配置为request、session等 值,表示容器会为一次请求或一个会话分配一个bean的实例。如果对bean的生命周期有特殊需求,可以使用自定义的Scope。比如实现一个bean被使用3次后,就获取新的bean实例。

二,实现

下面以Spring boot 进行演示

pom.xml

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

MyBean.java

@Data
public class MyBean {

    public MyBean(String id) {
        this.id = id;
    }

    private String id;
}

MyScope.java

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lwc
 */
public class MyScope implements Scope {

    /**
     * 记录bean的使用次数
     */
    private static ConcurrentHashMap<String, Integer> beanCounts = new ConcurrentHashMap<>();

    /**
     * 保存实例
     */
    private ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<String, Object>();

    @Override
    public Object get(String s, ObjectFactory<?> objectFactory) {
        beanCounts.putIfAbsent(s, 0);
        //第一次使用,放到实例的Map中
        Integer beanCount = beanCounts.get(s);
        if (beanCount == 0) {
            Object newObject = objectFactory.getObject();
            beans.put(s, newObject);
        }
        Object bean = beans.get(s);
        //计数器加1
        int newBeanCount = beanCount + 1;
        if (newBeanCount >= 3) {
            newBeanCount = 0;
        }
        //设置新的次数
        beanCounts.put(s, newBeanCount);
        return bean;
    }

    @Override
    public Object remove(String s) {
        return null;
    }

    @Override
    public void registerDestructionCallback(String s, Runnable runnable) {

    }

    @Override
    public Object resolveContextualObject(String s) {
        return null;
    }

    @Override
    public String getConversationId() {
        return null;
    }
}

MyConfig.java

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import javax.annotation.PostConstruct;

/**
 * 配置
 *
 * @author lwc
 */
@Configuration
public class MyConfig {

    @Autowired
    BeanFactory factory;

    @PostConstruct
    public void customScopeConfigurer() {
        CustomScopeConfigurer config = new CustomScopeConfigurer();
        config.addScope("three", new MyScope());
        config.postProcessBeanFactory((ConfigurableListableBeanFactory) factory);
    }

    @Bean
    @Scope(scopeName = "three")
    public MyBean bean1() {
        return new MyBean("1");
    }
}

DemoController.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author lwc
 */
@RestController
public class DemoController {

    @Autowired
    ApplicationContext ctx;

    @RequestMapping(value = "/")
    public String index() {
        for (int i = 0; i < 5; i++) {
            System.out.println(ctx.getBean("bean1"));
        }
        return "";
    }
}

三,测试

com.g7go.springannotation.MyBean@7e05c996
com.g7go.springannotation.MyBean@7e05c996
com.g7go.springannotation.MyBean@7e05c996
com.g7go.springannotation.MyBean@2ef5859d
com.g7go.springannotation.MyBean@2ef5859d

和我们要的相符,当然这里仅仅是一个下例子,更多使用场景还需要大家慢慢发现。