2016-06-13 15 views

Antwort

3

Nachdem ich Google Guice seit einigen Jahren verwende, bin ich an die Verfügbarkeit eines Just-In-Time-Binders gewöhnt, der die Injektion beliebiger Typen ermöglicht, ohne dass eine Vorkonfiguration erforderlich ist.

Auch ich fand die Idee, jeden Dienst explizit als schlechten Code-Geruch zu bezeichnen. Ich bin auch nicht verrückt nach der Notwendigkeit, einen speziellen Build-Schritt und den zusätzlichen Initialisierungscode für den Populator zu verwenden.

So kam ich mit der folgenden JustInTimeResolver Implementierung bis:

/** 
* 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; 
    } 
} 

Mit diesem in meinem Projekt, habe ich einfach folgend mein Bindemittel in meiner Jersey Anwendungskonfiguration:

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

und ich bekomme eine automatische Bindefunktion, wie ich es in Guice getan habe.

+0

@ jwells131313 - Ich bin sehr neugierig, warum eine solche Einrichtung nicht in HK2 enthalten war. –

+1

Es ist, Sie haben es gerade benutzt! Aber ich denke, du meinst etwas, was das generisch mit jeder Klasse tun würde, die zufällig auftaucht (beachte, dass deine Lösung nur Klassen behandelt, die mit com.fastmodel beginnen). Die Sache ist, wie Sie sehen können, ist es trivial hinzuzufügen. Es ist vielleicht keine schlechte Idee, eine Version von diesem zu haben, die einfach ANY-Klasse hinzufügt, die injiziert werden muss, aber sie müsste mit Vorsicht verwendet werden, da es einige Klassen geben kann, die NICHT auf diese Art hinzugefügt werden sollen. Trotzdem ist es keine schlechte Idee für eine "Extras" -Funktion. – jwells131313

1

Ich würde vorschlagen, zuerst hier zu suchen: Automatic Service Population.

Der grundlegende Prozess besteht darin, @Service-Anmerkungen für Ihre Klassen zu verwenden und den JSR-269 (APT) -Prozessor (Metadata Generator) zum Zeitpunkt der Erstellung zu verwenden. Dadurch fügen Sie Ihren JAR-Dateien einige Metadaten hinzu (normalerweise unter META-INF/hk2-locator/default).

Sie können dann sicherstellen, dass diese Dienste automatisch abgeholt werden, anstatt all diese lästigen Bindungen zu machen, indem Sie eine Populator verwenden, die Sie von Dynamic Configuration Service erhalten, die in jedem ServiceLocator verfügbar ist.

Der Pseudo-Code würde wie folgt sein:

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

In dem obigen Code die ClasspathDescriptorFileFinder verwendet werden durch den Classpath zu suchen, um die Metadaten zu finden. Andere Strategien könnten in Umgebungen wie OSGi verwendet werden.

IMO Dies ist eine viel bessere Möglichkeit, Dienste hinzuzufügen, anstatt alle Bindungen selbst zu erledigen.

0

Ich habe eine Suggestion, die mein Problem hier gelöst hat, habe ich vorgeschlagene Lösung versucht und nicht hier gearbeitet. In meiner Lösung muss jede Klasse mit @MyInjectable Annotation kommentiert werden.

1-Eine Annotation

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

2-erstellen AbstractBinder Implementierung

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-erstellen ResourceConfig

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

4-Configure richtig in 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>