I created a sub-package named control, and in it I put an interface, Greeter, that exposes the only method I really care of:
public interface Greeter { String greeting(); }Then I refactored my GreetingController to use that interface to do the dirty job. Something like this:
public class GreetingController { private Greeter greeter; public String greeting() { return greeter.greeting(); } }The reason for doing this should be clear. My controller won't care about the details of how a greeting is generated. It just knows that exists a hierarchy of classes rooted in the Greeter interface, and it would call its greeting() method when it intercepts a user call for this job.
Then I created a couple of concrete classes implementing Greeter. A mock one, thought to be used just in development, and the actual stuff designed to be used in production.
public class MockGreeter implements Greeter { private static final Log log = LogFactory.getLog(MockGreeter.class); @Override public String greeting() { log.debug("Generating mock greeting"); return "mock hello!"; } } public class PlainGreeter implements Greeter { private static final Log log = LogFactory.getLog(PlainGreeter.class); @Override public String greeting() { log.trace("Generating plain greeting"); return "Hello!"; } }I know, it is not easy to spot a difference between them. Let's assume they will grow and diverge to something more useful in the future.
The interesting stuff here is defining how the controller should know which Greeter to use. In the old pre-IoC days it would be its job to create a dependency with the selected Greeter, creating an object of a concrete type. Nowadays we prefer to do the other way round, we invert the control, and let the concrete Greeter signal its availability to be used by the controller to the framework (in this case Spring). We can do that in a few different ways, being annotation usually considered the preferred one.
The controller should tell to Spring in some way that a field it owns should be injected with a dependency (here is where DI enters in the game). There are a few ways to do it. I usually prefer to annotate the data member, like this:
@RestController public class GreetingController { @Resource private Greeter greeter; // ... }On the other side, each Greeter implementation that could be selected from Spring for the controller should show it up:
@Component public class MockGreeter implements Greeter { // ... }We still have a problem. We have two different implementations of Greeter competing to be injected to the controller. Which one should Spring choose?
One way of solving it is by configuration. We specify the spring.profiles.active property in the Spring application.properties file giving to it a value that would act as a selector for the appropriate Greeter. In my case, I want to play with two different configurations, dev and prod. When in development (dev) I want the mock greeter to be injected in the controller, while in production (prod) the plain greeter should be used.
In case of production my Spring configuration file would have this line in it:
spring.profiles.active=prodMy Greeter classes would be annotated in this way:
@Component @Profile("prod") public class PlainGreeter implements Greeter { // ... } @Component @Profile("!prod") public class MockGreeter implements Greeter { // ... }
The complete Spring Boot project is on github. The relevant files are
- GreetingController.java, modified to use a Greeter object annotated as resource;
- application.properties, in which I added the active profile property;
- the package logic; containing the Greeter interface and its two implementations.
No comments:
Post a Comment