上一节我们在Spring系列-Spring Scope详解讲了Spring Scope怎么使用以及Scope代理是怎么实现的,这一节我们将如何自定义Spring Scope。

自定义Spring Scope只需要三步:

  • 定义Scope实现类
  • 注册Scope实现类
  • 使用自定义Scope

定义Scope实现类

定义Scope实现类需要实现org.springframework.beans.factory.config.Scope接口。例如我们定义一个Socpe,对于该Scope标识的Bean只有一个小时的有效期,超过一小时再次获取就要新建一个Bean。

public class OneHourScope implements Scope {

  private static final long ONE_HOUR = 1 * 60 * 60 * 1000;
  // 保存每一个beanName最后一次创建时间
  private Map<String, Long> times = new ConcurrentHashMap<>();
  // 保存每一个beanName对应的锁对象
  private Map<String, Object> locks = new ConcurrentHashMap<>();
  // 缓存每一个beanName对应的Bean对象
  private Map<String, Object> cache = new ConcurrentHashMap<>();

  // 该方法用户获取对应的Bean对象
  @Override
  public Object get(String name, ObjectFactory<?> objectFactory) {
    long now = System.currentTimeMillis();
    long createTime = times.getOrDefault(name, 0L);
    if (createTime == 0) {
      Object lock = locks.get(name);
      if (lock == null) {
        lock = locks.putIfAbsent(name, new Object());
      }
      synchronized (lock) {
        Object object = cache.get(name);
        if (object == null) {
          // 第一次获取该Bean走创建逻辑,然后放入缓存
          object = objectFactory.getObject();
          cache.put(name, object);
          times.put(name, now);
        }
        return object;
      }
    }
    if (now - createTime < ONE_HOUR) {
      return cache.get(name);
    }
    Object lock = locks.get(name);
    synchronized (lock) {
      createTime = times.get(name);
      if (now - createTime > ONE_HOUR) {
        // 如果该Bean已经创建超过一个小时,则重新创建
        Object object = objectFactory.getObject();
        cache.put(name, object);
        times.put(name, now);
        return object;
      }
      return cache.get(name);
    }
  }

  // 该方法用于在销毁bean时进行一些处理
  @Override
  public Object remove(String name) {
    times.remove(name);
    locks.remove(name);
    return cache.remove(name);
  }

  // 该方法用于注册销毁Bean时的回调方法
  @Override
  public void registerDestructionCallback(String name, Runnable callback) {
  }

  // 该方法可用于针对该Scope进行域内变量解析
  @Override
  public Object resolveContextualObject(String key) {
    return null;
  }

  // 获取一个标识ID
  @Override
  public String getConversationId() {
    return null;
  }
}

注册Scope

通过CustomScopeConfigurer进行注册Scope,因为该类实现了BeanFactoryPostProcessor,会在postProcessBeanFactory()方法中将我们添加的Scope通过beanFactory.registerScope()进行注册。

@Configuration
public class ScopeConfig {

  @Bean
  public CustomScopeConfigurer customScopeConfigurer() {
    CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
    customScopeConfigurer.addScope("oneHour", new OneHourScope());
    return customScopeConfigurer;
  }

}

使用Scope

使用@Scope注解标识该类,并设置Scope名为oneHour,并使用Scope代理。

@Scope(value = "oneHour", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class ScopeTest {

  public void test() {
    System.out.println(this);
  }

}

将标识了@Scope注解的类注入到其它类。

@Component
public class Demo {
  // 这里注入的是一个代理对象,如果不断调用scopeTest.test()方法,每隔一小时就会换一个新的ScopeTest对象
  @Autowired
  private ScopeTest scopeTest;

}

Scope的基本使用很简单,其中Scope接口中有一个方法我们没说,就是resolveContextualObject(), 该方法可以用于解析Scope域内的变量,以后再说吧,O(∩_∩)O哈哈~。