2017-05-01 3 views
2

Ich muss Zuordnungen für Klassen erstellen (wörtlich ein Map<Class<?>, String>), die zur Laufzeit nicht variieren, und die Dinge entkoppelt zu halten ist eine Priorität. Da ich in einer Frühlings-Anwendung bin, dachte ich mir eine Anmerkung verwenden würde und ClassPathScanningCandidateComponentProvider mehr oder weniger wie so:Spring Klassenpfad Komponente Scannen

@Inherited 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
public @interface Mapping { 
    String value(); 
} 

Und:

public class MappingLookUp { 
    private static final Map<Class<?>, String> MAPPING_LOOK_UP; 
    static { 
     Map<Class<?>, String> lookUp = new HashMap<>(); 
     ClassPathScanningCandidateComponentProvider scanningCandidateComponentProvider = new ClassPathScanningCandidateComponentProvider(false); 
     scanningCandidateComponentProvider.addIncludeFilter(new AnnotationTypeFilter(Mapping.class)); 
     for (BeanDefinition beanDefinition : scanningCandidateComponentProvider.findCandidateComponents("blah")) { 
      Class<?> clazz; 
      try { 
       clazz = Class.forName(beanDefinition.getBeanClassName()); 
      } catch (ClassNotFoundException e) { 
       throw new RuntimeException(e); 
      } 
      Mapping mapping = AnnotationUtils.getAnnotation(clazz, Mapping.class); 
      if (mapping == null) { 
       throw new IllegalStateException("This should never be null"); 
      } 
      lookUp.put(clazz, mapping.value()); 
     } 
     MAPPING_LOOK_UP = Collections.unmodifiableMap(lookUp); 
    } 

    public static String getMapping(Class<?> clazz) { 
     ... 
    } 
} 

Obwohl ich das glaube funktionieren wird, das fühlt sich an wie:

  1. viel in einer statischen Initialisierung
  2. hacky Verwendung der Scan-Komponente Anbieter zu setzen, auch wenn es häufig ist reco für diesen Zweck empfohlen; BeanDefinition klingt so, als ob es eher Springbohnen als allgemeine Klassendefinitionen finden soll.

klar zu sein, sind die kommentierten Werte Klassen Daten - nicht feder Managed Beans - so ein BeanPostProcessor Muster passt nicht, und in der Tat, deshalb fühlt es sich umständlich die Scan-Komponente-Anbieter zu verwenden, die, für mich, scheint für die Entdeckung von Spring Managed Beans gedacht.

Ist dies der richtige Weg, dieses Muster zu implementieren? Ist es eine ordnungsgemäße Anwendung des Anbieters? Gibt es eine praktikable Alternative, ohne andere Klassenpfad-Scan-Implementierungen einzubeziehen?

+0

Eine Zuordnung von Klasse String, ziemlich seltsam scheint. Sie haben keine Einschränkung für die Klasse, wie eine Schnittstelle, die implementiert werden muss, und Strings sollten nur angezeigt werden. Könnten Sie den Anwendungsfall näher erläutern, könnte ich einen Fall von String zu Class sehen, aber nicht umgekehrt. - Und meine Augen schmerzen, wenn Sie statische Initialisierer verwenden, besonders wenn Sie mit Spring arbeiten ;-) –

+0

Dies sind verschiedene Objekttypen, die effektiv für ein cross-cutting Audit-System markiert werden müssen. Ich könnte eine Schnittstelle mit einer Methode erzwingen, die das Tag zurückgibt, aber da dieser Wert zur Laufzeit nie variiert und für keine der implementierenden Klassen wichtig ist, halte ich eine Annotation für besser geeignet. Es sind sehr viele Metadaten. – jwilner

+0

Sie haben also eine Instanz der Klasse und rufen dann MappingLookUp auf. getMapping (instance.getClass()) ', um die Prüfzeichenfolge zu erhalten? –

Antwort

3

Ich schlage vor, das sieht nicht so aus, als ob es in einer sehr Frühlings-Weise gemacht wird. Wenn ich dies tun würde, würde ich Spring's BeanPostProcessor oder BeanFactoryPostProcessor verwenden. Beides ermöglicht eine Introspektion auf alle Bean's in Spring's BeanFactory und ermöglicht es Ihnen, sich von der Statik Ihres aktuellen Setups zu entfernen, da die PostProcessors nur Spring Bean's sind.

class MappingLookup implements BeanPostProcessor { 
    private final Map<Class<?>, String> lookup = new HashMap<>(); 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) { 
    // check bean's class for annotation... 
    // add to lookup map as necessary... 
    // make sure to return bean (javadoc explains why) 
    return bean; 
    } 

    public String getMapping(Class<?> clazz) { 
    // ... 
    } 

    // omitted other methods... 
} 
+0

Ihre Antwort ist sinnvoll, passt aber nicht ganz zu meinem Anwendungsfall (das ist meine Schuld, weil ich nicht klar gemacht habe). Ich notiere keine von Spring verwalteten Beans, sondern Datenobjekte. – jwilner

0

fragte ich eine sehr ähnliche Frage How to get list of Interfaces from @ComponentScan packages vor kurzem und umgesetzt schließlich die ersten Ansätze vorgeschlagen.

können Sie den Code sehen https://github.com/StanislavLapitsky/SpringSOAProxy sehen https://github.com/StanislavLapitsky/SpringSOAProxy/blob/master/core/src/main/java/org/proxysoa/spring/service/ProxyableScanRegistrar.java und natürlich Initialisierung Anmerkung https://github.com/StanislavLapitsky/SpringSOAProxy/blob/master/core/src/main/java/org/proxysoa/spring/annotation/ProxyableScan.java der Schlüssel Sache @Import({ProxyableScanRegistrar.class})

Der Schlüssel Code hinzufügen ist

public class ProxyableScanRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { 
    private Environment environment; 

    @Override 
    public void setEnvironment(Environment environment) { 
     this.environment = environment; 
    } 

    @Override 
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { 
     // Get the ProxyableScan annotation attributes 
     Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ProxyableScan.class.getCanonicalName()); 

     if (annotationAttributes != null) { 
      String[] basePackages = (String[]) annotationAttributes.get("value"); 

      if (basePackages.length == 0) { 
       // If value attribute is not set, fallback to the package of the annotated class 
       basePackages = new String[]{((StandardAnnotationMetadata) metadata).getIntrospectedClass().getPackage().getName()}; 
      } 
Verwandte Themen