Injecting String Resource Services with CDI
In this post we looked at adding String resource bundles to our JSF applications to move our string constants into external resources that we can define for different locales. Now I want to extend that example to show how you can expand on that by using injection to access those resources.
We finished off with a MessageProvider
class that was responsible for fetching the resource bundle and had methods for fetching values from that bundle. We created this class manually and accessed the string resources from it. The problem here is that we need to keep on recreating the provider in each piece of code that needs it.
Doing things the CDI way
You could construct an instance of the MessageProvider
each time you need it, but instead, we should look at how we can change our existing code to make it more CDI like. We can make the method that fetches the bundle a producer method and then inject the resource bundle where needed. If we give it a long enough scope, we can re-use it in the same request.
First we’ll write a new Qualifier for the bundle so we can uniquely identify this bundle in a type safe manner. We may want to produce and inject multiple resources that will all be of the same type (ResourceBundle
) so using a qualifier is a probably a good idea.
@Qualifier @Target({ TYPE, METHOD, PARAMETER, FIELD }) @Retention(RUNTIME) @Documented public @interface MessageBundle { }
We’ll add a @Produces
annotation to the getBundle()
method.
public class MessageProvider { @Produces @MessageBundle public ResourceBundle getBundle() { if (bundle == null) { FacesContext context = FacesContext.getCurrentInstance(); bundle = context.getApplication().getResourceBundle(context, "msgs"); } return bundle; } ... ... ... }
Now we just need to inject this into our bean and use the injected bundle instead of constructing our own instance of the MessageProvider
class.
@Named @RequestScoped public class SomeBean { @Inject @MessageBundle private ResourceBundle bundle; public String getMessage() { String msg = null; String key = "someMessage"; try { msg = bundle.getString(key); } catch (MissingResourceException e) { msg = "??"+key+"?? is missing!"; } return MessageFormat.format(msg, "SomeValue"); } }
While this is more CDI-ish, it does have the problem that it consists of more code since we re-produce the exception handling code each time. So we probably need to use something that consists of more than just the bundle, say maybe the whole MessageProvider
class with the methods to fetch key values. The beauty here is that we we don’t need to do anything to achieve this except give the MessageProvider
class a scope :
@RequestScoped public class MessageProvider { .... .... }
No we just add an injection point for the MessageProvider
class in our bean. We’ll create a new bean for this called AnotherBean
:
@Named @RequestScoped public class AnotherBean { @Inject private MessageProvider provider; public String getFirstNameCaption() { return provider.getValue("firstName"); } }
In our JSF page we add a reference to the property to display the value :
First Name Caption = #{anotherBean.firstNameCaption}
This results in the following text being displayed :
First Name Caption = First Name
You might notice that our old bean is still using the resource bundle from the producer method, and everything is still working. This is because beans can be injectable and still contain producer methods. You will also find that across the request, the injected instance of the bundle is always the same as the one in provider.getBundle()
. This is because every time the bundle is produced, the MessageProvider
is constructed and put into request scope and the bundle value is returned. This bundle value is retained in the MessageProvider
instance. No matter how many times we inject the bundle, the same one is used, even though we didn’t give it a request scope. This is actually a useful thing because the resource bundle cannot be proxied by CDI so it cannot be put into request scope itself, but it can be put into request scope if it is contained in another bean (in this case, the MessageProvider
).
This shows not only how flexible CDI is in making objects available to your applications, but how easy it is to tweak how those objects are made available, all without changing the underlying code.
Next time we’ll look at using event handling to notify us of missing resources.
You can download the Maven source code for this project and run it locally by unzipping it and running mvn clean jetty:run
in the command line and going to http://localhost:8080/resourcedemo/.
One thought on “Injecting String Resource Services with CDI”
Comments are closed.
Perfect, as always.