【问题标题】:Jersey 2 + HK2 - automatic binding of classessJersey 2 + HK2 - 类的自动绑定
【发布时间】:2016-10-14 03:09:02
【问题描述】:

话题继续 Jersey 2 + HK2 - @ApplicationScoped not working.

我已经知道,如何绑定类以正确地@Inject 它们。

你有什么想法,如何自动化这个过程?在我的应用程序中,将每一项服务都放在 bind 语句中似乎很糟糕。

【问题讨论】:

    标签: java jersey-2.0 hk2


    【解决方案1】:

    在使用 Google 的 Guice 多年后,我习惯了 Just-In-Time binder 的可用性,允许注入任意类型而无需任何前期配置。

    我也发现必须显式绑定每个服务的想法是不好的代码气味。对于需要使用特殊的构建步骤和为填充器添加的初始化代码,我也不感到疯狂。

    所以我想出了以下JustInTimeResolver 实现:

    /**
     * Mimic GUICE's ability to satisfy injection points automatically,
     * without needing to explicitly bind every class, and without needing
     * to add an extra build step.
     */
    @Service
    public class JustInTimeServiceResolver implements JustInTimeInjectionResolver {
    
        @Inject
        private ServiceLocator serviceLocator;
    
        @Override
        public boolean justInTimeResolution( Injectee injectee ) {
        final Type requiredType = injectee.getRequiredType();
    
            if ( injectee.getRequiredQualifiers().isEmpty() && requiredType instanceof Class ) {
                final Class<?> requiredClass = (Class<?>) requiredType;
    
                // IMPORTANT: check the package name, so we don't accidentally preempt other framework JIT resolvers
                if ( requiredClass.getName().startsWith( "com.fastmodel" )) {
                    final List<ActiveDescriptor<?>> descriptors = ServiceLocatorUtilities.addClasses( serviceLocator, requiredClass );
    
                    if ( !descriptors.isEmpty() ) {
                        return true;
                    }
                }
            }
            return false;
        }
    } 
    

    在我的项目中,我简单地将以下内容添加到我的 Jersey 应用程序配置中的活页夹中:

    bind( JustInTimeServiceResolver.class ).to( JustInTimeInjectionResolver.class );
    

    我可以像在 Guice 中一样自动创建绑定。

    【讨论】:

    • @jwells131313 - 我很好奇为什么这样的设施没有包含在 HK2 中。
    • 是的,你刚刚用过!但我猜你的意思是对碰巧出现的任何类都可以做到这一点(注意你的解决方案只处理以 com.fastmodel 开头的类)。事情是,正如您所看到的,添加它是微不足道的。拥有一个简单地尝试添加任何需要注入的类的版本可能不是一个坏主意,但必须小心使用它,因为可能有些类您不希望以这种方式添加。不过,对于“附加”功能来说,这并不是一个坏主意。
    【解决方案2】:

    我建议先看这里:Automatic Service Population

    基本过程是在您的类上使用@Service 注解并在构建时使用 JSR-269 (APT) 处理器 (Metadata Generator)。这样做会在你的 jar 文件中添加一些元数据(通常在 META-INF/hk2-locator/default 下)。

    然后,您可以确保自动获取这些服务,而不必使用 Populator 来完成所有那些讨厌的绑定,Populator 可以从每个 ServiceLocator 中提供的Dynamic Configuration Service 获得。

    伪代码是这样的:

    public void populate(ServiceLocator locator) throws Exception {
        DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
        Populator populator = dcs.getPopulator();
        populator.populate(new ClasspathDescriptorFileFinder(getClass().getClassLoader()));
    }
    

    在上面的代码中,ClasspathDescriptorFileFinder 用于搜索类路径以查找元数据。其他策略也可以在 OSGi 等环境中使用。

    IMO 这是一种添加服务的更好方法,而不是自己完成所有绑定。

    【讨论】:

      【解决方案3】:

      我有一个在这里解决了我的问题的建议,我已经尝试过提出的解决方案,但在这里没有工作。在我的解决方案中,必须使用 @MyInjectable 注释来注释每个类。

      1-创建注释

      @Retention(RUNTIME)
      @Target(ElementType.TYPE)
      public @interface MyInjectable {
      }
      

      2-创建一个 AbstractBinder 实现

      public class MyApplicationBinder extends AbstractBinder {
          @Override
          protected void configure() {
              bindFactory(EMFFactory.class).to(EntityManagerFactory.class).in(Singleton.class);
              bindFactory(EMFactory.class).to(EntityManager.class).in(RequestScoped.class);
              bind(Environment.class).to(Environment.class);
              scanAndBind("com.yourpackage.here");
          }
      
          private void scanAndBind(String packageName) {
              try {
                  Class[] classes = getClasses(packageName);
                  for (Class<?> klazz:
                       classes) {
                      MyInjectable annotation = klazz.getAnnotation(MyInjectable.class);
                      if (annotation!= null) {
                          bind(klazz).to(klazz);
                      }
                  }
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      
          private static Class[] getClasses(String packageName)
                  throws ClassNotFoundException, IOException {
              ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
              assert classLoader != null;
              String path = packageName.replace('.', '/');
              Enumeration<URL> resources = classLoader.getResources(path);
              List<File> dirs = new ArrayList<>();
              while (resources.hasMoreElements()) {
                  URL resource = resources.nextElement();
                  dirs.add(new File(resource.getFile()));
              }
              ArrayList<Class> classes = new ArrayList<Class>();
              for (File directory : dirs) {
                  classes.addAll(findClasses(directory, packageName));
              }
              return classes.toArray(new Class[classes.size()]);
          }
      
          private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
              List<Class> classes = new ArrayList<Class>();
              if (!directory.exists()) {
                  return classes;
              }
              File[] files = directory.listFiles();
              for (File file : files) {
                  if (file.isDirectory()) {
                      assert !file.getName().contains(".");
                      classes.addAll(findClasses(file, packageName + "." + file.getName()));
                  } else if (file.getName().endsWith(".class")) {
                      classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
                  }
              }
              return classes;
          }
      
      }
      

      3-创建资源配置

      public class MyApplication extends ResourceConfig {
          @Inject
          public MyApplication(ServiceLocator locator) {
              ServiceLocatorUtilities.enableImmediateScope(locator);
              ....
              register(new MyApplicationBinder());
          }
      }
      

      4-在web.xml中正确配置

      <servlet>
          <servlet-name>Jersey Web Application</servlet-name>
          <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
          <init-param>
              <param-name>jersey.config.server.provider.packages</param-name>
              <param-value>br.com.solutiontrue.ws</param-value>
          </init-param>
          <init-param>
              <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
              <param-value>true</param-value>
          </init-param>
          <init-param>
              <param-name>javax.ws.rs.Application</param-name>
              <param-value>your.package.name.MyApplication</param-value>
          </init-param>
          <init-param>
              <param-name>jersey.config.server.resource.validation.disable</param-name>
              <param-value>true</param-value>
          </init-param>
      
          <load-on-startup>1</load-on-startup>
      </servlet>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-01
        • 1970-01-01
        • 2019-09-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多