Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Howto generate prototype objects with in a singleton bean using spring java configurations

Here is what i have right now which works fine. All it does is a market class which returns an array of items objects:

I have class market

class market {

    public ArrayList<Items> createItems(HashMap<String,String> map) {
        ArrayList<Items> array = new ArrayList<Items>();
        for (Map.Entry<String, String> m : map.entrySet()) {
            Item item = new Item();
            item.setName(m.key());
            item.setValue(m.value());
            array.add(item);
        }
        return array;
    }
}

class Item is simple class with getter and setter for name and value

Here is how my config file looks:

@Configuration
public class MarketConfig {

    @Bean
    public Market market() {
        return new Market();
    }
}

How I want to change my code:( REASON: i dont want

Item item = new Item(); 

in then method. I want Spring to inject it into Market)

class market {

    public Item item;
    //getters and setters for item

    public ArrayList<Items> createItems(HashMap<String,String> map) {
        ArrayList<Items> array = new ArrayList<Items>();
        for (Map.Entry<String, String> m : map.entrySet()) {
             item.setName(m.key());
             item.setValue(m.value());
             array.add(item);
        }
        return array;
    }
}

@Configuration
public class MarketConfig {

    @Bean
    @Scope("prototype")
    public Item item() {
        return new Item();
    }

    @Bean
    public Market market() {
        Market bean = new Market();
        bean.setItem(item());
    }
}

I know that prototype scope will give me new bean each time i call item(); Now i want new bean for each iteration in the for loop of createItems method. How can i tell spring to give me.

One way i know is Do

applicationContext context = new AnnotationConfigApplicationContext();
context.getBean(Item.class);

But is there any other way to get my work done. Thanks

like image 442
javaMan Avatar asked Feb 14 '13 17:02

javaMan


People also ask

Can we create prototype bean in singleton bean?

You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs only once, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies.

What if prototype bean is Autowired in singleton Bean?

When you work with a prototype bean in a singleton, you have three options to get a new instance of the prototype: Spring can autowire a single prototype instance when it creates the singleton. It's the default framework behavior. Spring can create a new prototype instance on every call to any method of this prototype.

Where is singleton and prototype used in Spring?

As a rule of thumb, you should use the prototype scope for all beans that are stateful, while the singleton scope should be used for stateless beans.


2 Answers

Yes, you can create prototype method on demand using look-up method

public abstract class ItemFactory {

    public abstract Item createItem();

}

now in applicationContext.xml just put the following:

<bean id="item" class="x.y.z.Item" scope="prototype" lazy-init="true"/>

and configure factory:

<bean id="itemFactory" class="x.y.z.ItemFactory">
<lookup-method name="createItem" bean="item"/>
</bean>

Now all that you need to do in order to use it is Autowire it inside any bean:

and call yout look-up method:

@Service 
public class MyService{

   @Autowired
   ItemFactory itemFactory;

   public someMethod(){
      Item item = itemFactrory.createItem();
   } 

}

each time you call createItem() you will receive the reference to newly created instance of Item class.

P.S: I see that you are using @Configuration instead of xml you need check if look-up method can be configured inside configuration bean.

Hope it helps.

Update: The trick is simple:

@Configuration
public class BeanConfig {

    @Bean
    @Scope(value="prototype")
    public Item item(){
        return new Item();
    }


    @Bean
    public ItemManager itemManager(){
        return new ItemManager() {

            @Override
            public Item createItem() {
                return item();
            }
        };
    }
}
like image 115
danny.lesnik Avatar answered Nov 15 '22 06:11

danny.lesnik


It can be simplified if you are using Java 8

@Configuration
public class Config {

  @Bean
  @Scope(value = "prototype")
  public Item item() {
      return new Item();
  }

  @Bean
  public Supplier<Item> itemSupplier() {
      return this::item;
  }
}

And after that you can use this supplier in your Market class to create prototype Item beans.

@Component
public class Market {

  private final Supplier<Item> itemSupplier;

  @Autowired
  public Market(Supplier<Item> itemSupplier) {
      this.itemSupplier = itemSupplier;
  }

  private Item createItem() {
      return itemSupplier.get();
  }

}

Quite simple and there is no need in additional factory beans or interfaces.

like image 31
Egor Lyashenko Avatar answered Nov 15 '22 07:11

Egor Lyashenko