1
  private Last60MinutesArray last60MinutesOfBytes = new Last60MinutesArray();
  @Bean
  Last60MinutesArray last60MinutesOfBytes()
  {
    return last60MinutesOfBytes;
  }

  private Last60MinutesArray last60MinutesOfStartedTrackersCount = new Last60MinutesArray();
  @Bean
  Last60MinutesArray last60MinutesOfStartedTrackersCount()
  {
    return last60MinutesOfStartedTrackersCount;
  }

  @Bean
  InLast60MinutesChart bytesUploadedInLastMinutesChart()
  {
    return new InLast60MinutesChart("Bytes Uploaded"
        ,last60MinutesOfBytes);
  }

  @Bean
  InLast60MinutesChart uploadsStartedInLastMinutesChart()
  {
    return new InLast60MinutesChart("Uploads Started"
        ,last60MinutesOfStartedTrackersCount);
  }

I'm creating two beans of type InLast60MinutesChart which takes a Last60MinutesArray in the constructor. I rather not have 'Last60MinutesArray' brought in through the constructor but rather instantiated in the InLast60MinutesChart. However, I still want two separate instances of 'Last60MinutesArray' and I want them both to be Beans so I can use springs annotations such as @Scheduled. Thanks for any thoughts :)

I want it to look something like this:

  @Bean
  InLast60MinutesChart bytesUploadedInLastMinutesChart()
  {
    return new InLast60MinutesChart("Bytes Uploaded");
  }

  @Bean
  InLast60MinutesChart uploadsStartedInLastMinutesChart()
  {
    return new InLast60MinutesChart("Uploads Started");
  }
public class InLast60MinutesChart
{
  @Autowired
  protected Last60MinutesArray last60MinutesArray;
}

And I want each InLast60MinutesChart to have its own distinct Last60MinutesArray and lets also say I want 50 instances of InLast60MinutesChart and so I don't really want to create a Bean for each Last60MinutesArray. I have to create a Bean for each InLast60MinutesChart but I'd rather not create that many Last60MinutesArray Beans on top of that along with any other Beans I want to add to the InLast60MinutesChart Bean. Kind of weird scenario but I hope if gets my question across.

JaredCS
  • 427
  • 4
  • 11

2 Answers2

3

your title doesn't actually match your question description, but in case you want something like it says in your title, then you need beans with a scope of "prototype". by default, Spring beans are Singletons. they are created once and injected each time they are autowired. However, if you'd like a new instance of the bean each time it is autowired, then you need to specify a "scope" of type "prototype"

@Bean("bytesChart")
@Scope("prototype")
public InLast60MinutesChart bytesUploadedInLastMinutesChart(@Qualifier("bytesCount") Last60MinutesArray last60MinutesOfBytes) {
    return new InLast60MinutesChart("Bytes Uploaded", last60MinutesOfBytes);
}

now, a new instance of "InLast60MinutesChart" will be created each time it is autowired. there are other scopes as well, most of them are not useful :P

https://www.baeldung.com/spring-bean-scopes

Tom Elias
  • 751
  • 6
  • 15
  • I thought this might do it from the look of it but when I tested it out I realized that this creates fake/prototype objects. I'm looking for something similar to this but creating real instances of `Last60MinutesArray` – JaredCS Nov 10 '20 at 21:36
  • 1
    i don't know what you mean by "fake/prototype" objects...this is Java, not JavaScript. The annotation instructs the container to create a new instance of the bean each time is it autowired. to test this, you can put a breakpoint in the function that creates the bean and see for your self. – Tom Elias Nov 11 '20 at 08:14
  • Okay you're right, my first test using the debugger was showing me weird results. I created a new project and ran some experiments and yes @Scope("prototype") is what I was looking for thanks :) – JaredCS Nov 11 '20 at 17:27
  • According to the question description, prototype scope is not what you want. Prototype creates a new bean *every time it is requested* (via e.g. `applicationContext.getBean(Last60MinutesArray.class)`. If the beans you are requesting also perform scheduled tasks, this will probably not behave as expected. – filpa Nov 11 '20 at 18:25
0

If you want to have multiple @Bean definitions of the same type available in the application context, you can define your Last60MinutesArray instances as @Beans and then provide them a specific bean name as a qualifier, as follows:

@Configuration
public class MyConfiguration {

    @Bean("bytesCount")
    public Last60MinutesArray last60MinutesOfBytes() {
         return new Last60MinutesArray();
    }

    @Bean("trackersCount")
    public Last60MinutesArray trackersCount () {
        return new Last60MinutesArray();
    }
}

This makes your Last60MinutesArrays into full-fledged Spring beans which are part of the application lifecycle (and among other things, capable of having @Scheduled methods as you desire).

Afterwards, you can pass the relevant @Bean to the proper InLast60MinutesChart by using @Qualifier, as follows:

  // in the same class or another marked as @Configuration

    @Bean("bytesChart")
    public InLast60MinutesChart bytesUploadedInLastMinutesChart(@Qualifier("bytesCount") Last60MinutesArray last60MinutesOfBytes) {
        return new InLast60MinutesChart("Bytes Uploaded", last60MinutesOfBytes);
    }

    @Bean("uploadsChart")
    public InLast60MinutesChart uploadsStartedInLastMinutesChart(@Qualifier("trackersCount") Last60MinutesArray last60MinutesOfStartedTrackersCount ) {
        return new InLast60MinutesChart("Uploads Started", last60MinutesOfStartedTrackersCount);
    }

Note that your InLast60MinutesChart beans should likely also be qualified, as using @Autowired to inject either of them (or either of the Last60MinutesArrays) will yield a NoUniqueBeanDefinitionException upon injection since there is more than one bean of the matching type. In this case, autowiring would also require a @Qualifier, e.g. as follows:

@Service
public class MyService {

    @Autowired
    @Qualifier("uploadsChart")
    private InLast60MinutesChart uploadsChart;

}

Alternatively, if you don't care about the specific instances, you can inject all beans of the given type as follows:

@Service
public class MyService {

   @Autowired
   private List<InLast60MinutesChart> charts;

}

which you can then iterate over.

Note that you do not have to qualify the bean names if you instead mark one of them as @Primary, but judging from your code, this is probably not what you want to do.

It would also likely help you to go through the reference documentation, which goes into a bit more detail.


Lets say I create 50 Beans of InLast60MinutesChart in the config. Is there a way that I don't then have to create 50 Beans of Last60MinutesArray / any other bean that I may want included in InLast60MinutesChart. Also keeping in mind that I want them to each be a separate instance.

The number of beans that you need to create is an important detail, making the maintenance effort non-trivial for the above naïve approach. In such a case, you will most likely want to basically do what I said above, but programmatically:

  1. Create n Last60MinutesArray beans programmatically, each of them having a separate name (what kind of pattern you want to use for the naming is up to you).
  2. Create n InLast60MinutesChart beans programmatically, which have a constructor taking a Last60MinutesArray.

The important part is that you must keep track of which Last60MinutesArrays to pass to which InLast60MinutesChart, essentially emulating the above declarative bean creation.

Instead of repeating already-present information, have a look at this answer for programmatically registering beans in the application context using BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry().

filpa
  • 3,651
  • 8
  • 52
  • 91
  • Lets say I create 50 Beans of `InLast60MinutesChart` in the config. Is there a way that I don't then have to create 50 Beans of `Last60MinutesArray` / any other bean that I may want included in `InLast60MinutesChart`. Also keeping in mind that I want them to each be a separate instance. – JaredCS Nov 10 '20 at 21:40