Hasor 在管理 Bean 的时候支持作用域,一个典型的作用域应用场景就是“单例”。单例作用域的表现是整个应用程序中只保存一份。另外一个作用域的例子是用户登录网站之后 web 应用程序通过 Session 保持会话。

单例模式(Singleton)

声明 Bean 的单例一般通过下面这种注解方式:

  1. @Singleton()public class AopBean { }

如果您使用的 ApiBinder 方式进行代码形式声明单例,那么需要这样:

  1. public class MyModule implements Module {
  2. public void loadModule(ApiBinder apiBinder) {
  3. apiBinder.bindType(PojoInfo.class).asEagerSingleton();
  4. }
  5. }

原型模式(Prototype)

原型模式 和单例模式是正反的一对关系。Hasor 默认使用的是原型模式,因此开发者不需要做任何配置。

  1. @Prototype()public class AopBean { }

或者您可以通过 ApiBinder 方式进行代码形式声明:

  1. public class MyModule implements Module {
  2. public void loadModule(ApiBinder apiBinder) {
  3. apiBinder.bindType(PojoInfo.class).asEagerPrototype();
  4. }
  5. }

设置默认为单例模式

Hasor 是不直接支持默认单例的。不过可以借助 SPI 实现这个功能。首先创建SPI监听器:

  1. public class MyCollectScopeListener implements CollectScopeListener {
  2. public Supplier<Scope>[] collectScope(BindInfo<?> bindInfo, AppContext appContext,
  3. Supplier<Scope>[] suppliers) {
  4. // 注册的 Bean 无论是否已经单例,都追加一个单例。
  5. return ArrayUtils.add(suppliers, appContext.findScope(Singleton.class));
  6. }
  7.  
  8. public Supplier<Scope>[] collectScope(Class<?> targetType, AppContext appContext,
  9. Supplier<Scope>[] suppliers) {
  10. // 非注册的 Bean 无论是否已经单例,都追加一个单例。
  11. return ArrayUtils.add(suppliers, appContext.findScope(Singleton.class));
  12. }
  13. }

然后创建容器并且设置 SPI:

  1. AppContext appContext = Hasor.create().build(apiBinder -> {
  2. // 设置默认单例SPI
  3. apiBinder.bindSpiListener(CollectScopeListener.class, new MyCollectScopeListener());
  4. });

最后测试两次创建的 Bean 就是一样的了:

  1. PojoBean pojoBean1 = appContext.getInstance(PojoBean.class);
  2. PojoBean pojoBean2 = appContext.getInstance(PojoBean.class);
  3. assert pojoBean1 == pojoBean2;

自定义作用域

以 HttpSession 为例,实现一个 HttpSession 作用域。

  1. public class SessionScope implements Scope {
  2. public static final ThreadLocal<HttpSession> session
  3. = new ThreadLocal<HttpSession>();
  4.  
  5. public <T> Provider<T> scope(Object key, Provider<T> provider) {
  6. HttpSession httpSession = session.get();
  7. if (httpSession == null) {
  8. return provider;
  9. }
  10. // 为了避免保存到 Session 中的 Bean 和本身 Session 中的数据 key
  11. // 出现冲突,增加一个前缀用于区分
  12. String keyStr = "session_scope_" + key.toString();
  13. Object attribute = httpSession.getAttribute(keyStr);
  14. Provider<T> finalProvider = provider;
  15. if (attribute == null) {
  16. httpSession.setAttribute(keyStr, provider);
  17. } else {
  18. finalProvider = (Provider<T>) httpSession.getAttribute(keyStr);
  19. }
  20. return finalProvider;
  21. }
  22. }

然后通过一个 Filter 每次 request 请求到来的时候把 Session 对象设置到 ThreadLocal 中。

  1. public class ConfigSession implements Filter {
  2. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  3. throws IOException, ServletException {
  4. try {
  5. if (SessionScope.session.get() != null) {
  6. SessionScope.session.remove();
  7. }
  8. SessionScope.session.set(((HttpServletRequest) request).getSession(true));
  9. chain.doFilter(request, response);
  10. } finally {
  11. if (SessionScope.session.get() != null) {
  12. SessionScope.session.remove();
  13. }
  14. }
  15. }
  16. }

最后我们在创建 Hasor 的时候把 Scope 配置上,这里由于要配置 Filter 因此使用 WebModule

  1. public class StartModule extends WebModule {
  2. public void loadModule(WebApiBinder apiBinder) throws Throwable {
  3. ...
  4. apiBinder.filter("/*").through(0, new ConfigSession());
  5. apiBinder.registerScope("session", new SessionScope());
  6. ...
  7. }
  8. }

接下来配置每次创建 UserInfo 对象时都是 Session 内唯一:

  1. apiBinder.bindType(UserInfo.class).toScope("session");

作用域链

(暂略)详细暂时请看:net.hasor.core.Scope 接口