Rafael Ferreira Rocha
Rafael Ferreira Rocha

Reputation: 987

How to create a new instance of a bean for each generic type?

So I have my Subject class:

@Component
public class Subject<T extends Monitorable> {

    @Autowired
    private List<Observer<T>> observers;

    public void fireListeners(T monitorable){
        for (Observer<T> observer : observers) {
            observer.doSome(monitorable);
        }
    }
}

Is that a way to create a new subject instance for each implementation of Monitorable like:

@Autowired
private Subject<Trip> tripSubject;

Trip is a Monitorable and it has its own observers

@Autowired
private Subject<Truck> truckSubject;

and truck as well

The problem is. It creates only one Subject with all observers mixed how to separate them without create a new subject class for each monitorable?

Upvotes: 3

Views: 277

Answers (4)

Rafael Ferreira Rocha
Rafael Ferreira Rocha

Reputation: 987

I improved oleg.cheredinik answer because there is no way to do it. Here's what I think is the best solution:

I changed Subject to receive Observer as construct params

public class Subject<T extends Monitorable> {

    private final List<Observer<T>> observers;
    public Subject(final List<Observer<T>> observers) {
        this.observers = observers;
    }

    public void fireListeners(T monitorable){
        for (Observer<T> observer : observers) {
            observer.doSome(monitorable);
        }
    }
}

and then I created subject with SubjectSimpleFactory :

@Configuration
public class SubjectSimpleFactory {

    @Bean
    @Autowired(required = false)
    public Subject<Trip> getTripSubject( Optional<List<Observer<Trip>>> observers){
        return new Subject<>(getListenersIfPresent(observers));
    }

    @Bean
    @Autowired(required = false)
    public Subject<Truck> getTruckSubject( Optional<List<Observer<Truck>>> observers){
        return new Subject<>(getListenersIfPresent(observers));
    }
}
private static <M extends Monitorable> List<Observer<M>> getListenersIfPresent(
        final Optional<List<Observer<M>>> observers )
    {
        return observers.isPresent() ? observers.get() : Collections.emptyList();
    }

In this way my observers are not mixed and I only have to create one class without repeat code or subclass Subject and I can use generic type as qualifier as well

Upvotes: 1

sanit
sanit

Reputation: 1764

@Service
@Scope("prototype")
public class Subject<T extends Monitorable> {

}

@Component
public class RunSubject {

    @Autowired
    private Subject<Monitorable1> subject1;

    @Autowired
    private Subject<Monitorabl2> subject2;

    public void run(ApplicationArguments args) throws Exception {
       System.out.println(subject1);
       System.out.println(subject2);
    }

 }

Upvotes: 0

Oleg Cherednik
Oleg Cherednik

Reputation: 18255

It is pretty simple. You could do it in a few ways, one of them is just create @Configuration and defind separate method for each required instance.

First, do remove @Component annotation from Subject definition:

//@Component
public class Subject<T extends Monitorable> {
}

Second, do define custom configuration with @Configuration:

@Configuration
public class MonitorableConfiguration {

    @Bean
    public Subject<Trip> tripSubject() {
        return new Subject<>();
    }

    @Bean
    public Subject<Truck> documentSubject() {
        return new Subject<>();
    }
}

Third, do use @Qualified to select required instance of Subject bean:

@Service
public class BusinessLogicService {
    @Autowired
    @Qualifier("tripSubject")
    private Subject<Trip> tripSubject;
    @Autowired
    @Qualifier("documentSubject")
    private Subject<Truck> documentSubject;
}

NOTE In this situation, I would reccomend to go a little bit further. It could be more readable from my point of view.

First, do remove @Component annotation from Subject definition:

//@Component
public class Subject<T extends Monitorable> {
}

Second, do declare separate class definition for all required types:

@Component
public class TripSubject extends Subject<Trip> {
}

@Component
public class TruckSubject extends Subject<Truck> {
}

Third use is as any other singletons cope beans:

@Service
public class BusinessLogicService {
    @Autowired
    private TripSubject tripSubject;
    @Autowired
    private TruckSubject documentSubject;
}

Upvotes: 1

Ralph
Ralph

Reputation: 120851

This is not the answer to your question, but maybe a solution for your problem: Spring 4.2 has a build in event (observer) mechanism :

   @Autowire ApplicationEventPublisher publisher;
   public void doSomething() {
      ...
      //fire the event
      publisher.publishEvent(new YourEvent());
      ...
   }

Some other bean:

   //This is the observer, it "catch" the event
   @EventListener
   public void handleOrderCreatedEvent(YourEvent your) {
        ...
   }

Upvotes: 1

Related Questions