I am writing a simple JSF application for converting currencies.
There is a Rates
class which holds the data, Currencies
class for managing requests and Manage
class for adding new currencies. My problem is: I want the currencies to be persisted as a property of Rates class hence my use of @ApplicationScoped
. However I can see that Rates
keeps getting reinitialized with every single request. Here's my code of Rates
:
@ManagedBean
@ApplicationScoped
public class Rates {
private Map<String, Double> rates = new HashMap<String, Double>();
private Set<String> currencies = new HashSet<String>(){
{
System.out.println("HashSet initializing");
}
};
private List<String> list = new ArrayList<String>(Arrays.asList("Filip", "Kasia"));
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Rates() {
rates.put("PLN_EUR", 0.25);
rates.put("EUR_PLN", 4.0);
currencies.add("PLN");
currencies.add("EUR");
}
public Map<String, Double> getRates() {
return rates;
}
public void setRates(Map<String, Double> rates) {
this.rates = rates;
}
public Set<String> getCurrencies() {
return currencies;
}
public void setCurrencies(Set<String> currencies) {
this.currencies = currencies;
}
public void addRate(String firstCurrency, String secondCurrency){
if(rates.containsKey(firstCurrency + "_" + secondCurrency))
return;
double rate = (double)(Math.random()*10);
rates.put(firstCurrency + "_" + secondCurrency, rate);
rates.put(secondCurrency + "_" + firstCurrency, 1 / rate);
currencies.add(firstCurrency);
currencies.add(secondCurrency);
System.out.println(currencies);
}
public double getRate(String currencies){
return rates.get(currencies);
}
}
and here's how it get's injected:
@ManagedBean
public class Manage {
private String firstCurrency;
private String secondCurrency;
private @Inject Rates rates;
public String getSecondCurrency() {
return secondCurrency;
}
public void setSecondCurrency(String secondCurrency) {
this.secondCurrency = secondCurrency;
}
public String getFirstCurrency() {
return firstCurrency;
}
public void setFirstCurrency(String firstCurrency) {
this.firstCurrency = firstCurrency;
}
public void addCurrencies(){
rates.addRate(firstCurrency, secondCurrency);
}
}
As you can see I added instance initializer for a property of Rates, set currencies
and the text "HashSet initializing" is printed out with every request.
@Inject
only injects CDI managed beans.
@ManagedBean
declares a JSF managed bean not a CDI managed bean.
Make the Rates
class a CDI managed bean instead.
import javax.inject.Named;
import javax.enterprise.context.ApplicationScoped;
@Named
@ApplicationScoped
public class Rates {}
Noted should be that the general recommendation is to stop using JSF managed bean facility and exclusively use CDI managed bean facility. So it'd be better if you make all your other JSF managed beans CDI managed beans too. That's exactly why I wouldn't recommend to replace @Inject
by its JSF equivalent @ManagedProperty
(which would also have worked).
As to managed bean initialization, you should not do it at class/instance level, but perform the task in a @PostConstruct
annotated method.
private Map<String, Double> rates;
private Set<String> currencies;
private List<String> list;
@PostConstruct
public void init() {
rates = new HashMap<>();
currencies = new HashSet<>();
list = new ArrayList<>(Arrays.asList("Filip", "Kasia"));
}
This is particularly important in CDI because it creates and injects proxies based on the class, so the class may be instantiated at times you don't expect.