web-dev-qa-db-de.com

Frühling @Autowired für eine neue Instanz der Klasse

Ich kenne den Frühling nicht so gut und habe folgende Situation:

Eine Repository-Klasse:

@Repository
public class MyRepository {
    // ...
}

Eine Klasse, die die Repository-Klasse verwendet:

public class MyClass extends AbstractClass {

    @Autowired
    private MyRepository myRepository;

    //...
}

Ich weiß, dass, wenn ich meine MyClass mit @Component kommentiere und sie mit einem @Autowired verwende, der @AutowiredMyRepository in Ordnung ist. Das Problem ist, dass ich in der Lage bin, neue Instanzen von MyClass mit Reflektion zu erstellen. MyRepository wird also niemals aufgelöst und ist immer null.

Gibt es eine Möglichkeit, @Autowired in dieser Situation zu verwenden?

Um meine Situation besser zu erklären: Ich habe einige Implementierungen von AbstractClass. In einer Setup-Phase meiner Anwendung erstelle ich eine HashMap dieser Implementierungen. Grundsätzlich gilt:

{"MyClass", MyClass.class}
//...

Dann habe ich ein generisches Controller, das der URL /{class}?options=....__ zugeordnet ist. Mit dem {class}@PathVariable, der HashMap und der Reflektion kann ich eine Instanz einer Klasse erstellen, die auf dem angegebenen options basiert (dieser Teil ist wichtig). Glaubst du, es gibt einen besseren Weg, das zu tun?

Danke im Voraus

6
João Menighin

Spring selbst bietet einige Funktionen für die automatische Verdrahtung Ihrer Objekte .__, die Sie mit new oder newInstance() oder einem anderen Objekt erstellt haben.

Um es zu verwenden, benötigen Sie eine AutowireCapableBeanFactory , Die Sie durch die normale Abhängigkeitseingabe von Spring mit @Autowired erhalten.

@Autowired
private  AutowireCapableBeanFactory autowireCapableBeanFactory;

Dann verwenden Sie seine autowireBean(Object) method, um die @Autowired-Eigenschaften in Ihre Bean einzuspeisen.

Object myBean = map.get(className).newInstance();
autowireCapableBeanFactory.autowireBean(myBean);

Design Hinweis:

Denken Sie gut darüber nach, wenn Sie wirklich den oben genannten Ansatz benötigen. Der Javadoc von AutowireCapableBeanFactory rät von der Verwendung dieser Schnittstelle für die meisten Anwendungsfälle ab:

Diese Unterschnittstelle von BeanFactory ist nicht für die Verwendung in normalem Anwendungscode vorgesehen: Halten Sie in typischen Anwendungsfällen BeanFactory oder ListableBeanFactory fest.

Integrationscode für andere Frameworks kann diese Schnittstelle nutzen, um vorhandene Bean-Instanzen zu verbinden und aufzufüllen, für die Spring den Lebenszyklus nicht steuert. Dies ist besonders nützlich, z. B. für WebWork-Aktionen und Tapestry-Seitenobjekte.

5
Thomas Fritsch

Eine Lösung besteht darin, MyClass nicht an die Hashmap zu binden, um eine Factory-Klasse zu binden. MyClassFactory. Auf diese Weise delegieren Sie die Konstruktion an eine konkrete Fabrik, die die Aufgabe übernimmt, die richtige Klasse zu instanziieren und das richtige Repository zu initialisieren.

Hier ist ein Beispiel:

{"MyClass", MyClassFactory.class}

Die Factory kann auch Component sein. Dann müssen Sie die Hashmap an die Factory-Instanz statt an die Factory-Klasse binden. Aber sagen wir mal, es ist keine Komponente:

//@Component   this is optional
    public MyClassFactory {
        //@Autowired optional
        ApplicationContext ctx;


       public MyClass createInstance() {
            MyRepository repo = ctx.getBean("")
            MyClass myclass = new MyClass(repo)
            return myclass;
       }
    }

Wenn Sie es als Komponente markieren, können Sie auch die ApplicationContextAware-Schnittstelle verwenden, wenn Sie ApplicationContext automatisch aktivieren möchten.

2

Sie können Factory Design Pattern hier verwenden.

Dies mag beim Start etwas kompliziert erscheinen, aber ich bin mir sicher, dass Sie es lieben werden, nachdem Sie es implementiert haben.

Schritte:

  1. Fügen Sie @Component zu allen Implementierungen von AbstractClass hinzu.
  2. Erstellen Sie eine Factory-Klasse als:

    @Component
    public class MyFactory {
    
        private final Map<String, AbstractClass> impletationMap = new HashMap<>();
    
        @Autowired
        ApplicationContext context;
    
        @PostConstruct
        public void initialize() {
            populateDataMapperMap(context.getBeansOfType(AbstractClass.class).values().iterator());
        }
    
        private void populateDataMapperMap(final Iterator<AbstractClass> classIterator) {
            while (classIterator.hasNext()) {
                AbstractClass abstractClassImpl = (AbstractClass) classIterator.next();
                impletationMap.put(abstractClassImpl.getClass().getName(), abstractClassImpl);
    
            }
        }
    }
    

Wenn das Bean dieser MyFactory-Klasse initialisiert wird, sucht es nach allen Beans vom Typ AbstractClass und fügt sie in die HashMap (ImplementierungsMap) ein. 

Jetzt können Sie aus dieser Factory die HashMap und dann die Implementierungen erhalten, wann und wo Sie möchten. Es ist sehr einfach, wenn Sie eine neue Implementierung von AbstractClass hinzufügen, da Factory sich darum kümmert.

1
Rishabh Agarwal

Versuche dies

@Component    
public class SomeClass extends AbstractClass {

  private static ApplicationContext applicationContext;

  public MyClass getMyClass(){
      // Now @Autowired MyRepository will work
      return applicationContext.getBean(MyClass.class);
  }

}
1
Maruthi Adithya

Ein Ansatz besteht darin, @Component über MyClass zu deklarieren. 

In der Setup-Phase können Sie in der HashMap die instance von MyClass anstelle von MyClass.class selbst übergeben. Es ist nicht nötig, Instanzen über Reflection zu erstellen.

Hinweis: Sie können die Instanz von MyClass von Ihrer ApplicationContext in der Setup-Phase abrufen.

1
Pankaj Singhal

Ja, Sie können alle AbstractClass-Implementierungs-Beans mit @Component kommentieren und die nächste Deklaration verwenden

@Autowired
private List<AbstractClass> beans;

Sie können diese dann in einer @ PostConstruct-Methode in eine Map konvertieren.

Spring beschwert sich nicht über doppelte Definitionen, wenn Sie Listen automatisch erstellen.

0
user10367961