On July 25, 2011 wrote: Delineating Architectures By Extending Spring Stereotypes
In version 2.5, released in November 2007, Spring introduced annotation-based application context configuration oriented primarily around the org.springframework.stereotype package. Clearly, annotations have been around in the Java and Spring world for quite some time now. Leaving aside the question of the pros and cons of annotations themselves for another time, it occurs to me that most developers still tend to treat them as if they were a slightly mysterious form of code: they are reluctant to implement their own and tend to use those that exist in only the most normative and mundane ways. Therefore, I would like to go through a very simple example of how to take advantage of Spring’s stereotype annotations to support and enhance your own application architecture.
Out of the box, Spring provides 4 basic architectural stereotypes used commonly by n-tier applications:
@Component- Used to indicate that the annotated class encapsulates a generic application component of an essentially undefined type.
@Repository- Used to indicate that the annotated class encapulates access to a system of record (e.g. a data accessor).
@Service- Used to indicate that the annotated class encapsulates a “service”; commonly defined in terms of an API the underlying implementation of which organises sequences of lower-level technical logic into a call sequence within a transaction boundary with the aim of delivering a functional requirement.
@Controller- Used to indicate that the annotated class is a controller within an MVC application.
With the exception of the @Component annotation (which is the base type used to determine that a class is a Spring bean during context initialisation) and the @Controller annotation (which is key when using Spring’s MVC support), the specialisations indicated serve little functional use on the whole. In most applications, it would be absolutely equivalent to mark a class as @Component in preference to @Service, for example. However, it need not be that way as not only can this metadata be utilised for functional ends but also extending these stereotypes provides an excellent way to clarify the architectural role of a given unit of code when none of the pre-existing specialisations are appropriate.
For example, in my little hobby-project “Apposite” (which I have mentioned in previous posts), I have a number of requirements around ensuring state on application startup (i.e. bootstrapping).
Now, bootstrapping is a classic “non-functional” problem (in so far as it usually does not provide functionality to an end user, merely ensuring that the application is in an adequate state to be capable of subsequently delivering this) … and, by no coincidence, it is also the kind of thing that therefore also often gets left to the end and piled into some unreadable file that suffers from virulent bit-rot.
Alternatively (and a slight improvement), the more organised-of-mind will retain the basic scripting approach but structure it via the use of the Template Pattern:
// abstract class with public *final* method defining algorithm, the individual methods for which may or may not also be abstract
public abstract class AbstractTemplatePattern {
public final void doSomething() {
doStepOne();
Foo resultOfStepTwo = doStepTwo();
doStepThree(resultOfStepTwo);
}
protected void doStepOne() {
// maybe a default implementation here ...
}
protected abstracted Foo doStepTwo();
protected abstracted void doStepThree(Foo foo);
}
However, whilst this is slightly preferable, it is still not really ideal for the bootstrapping use case. This is because we expect the “algorithm” (i.e. “what needs to be done when bootstrapping”) to change with some frequency … this is usually the cause of the maintenance nightmares that can sometimes occur around such activities. Whilst scripts can become illegible under such circumstances, the template pattern becomes difficult to maintain because it is precisely intended to represent a static algorithm with changing implementations, whereas our bootstrap is the inverse: a changing algorithm with static implementations. In both cases, the code unit(s) involved are become subject to change and, consequently, bugs are liable to be introduced in the process of change. Associated tests may start to fail, and collaborators or subclasses may be affected in all kinds of ways that are, to put it bluntly, a painful waste of time to deal with.
To talk in terms of patterns again, what we really want here are commands and a command executor. What we want to be able to do is simply “drop-in” a new chunk of “stuff to do” and be comfortable in the knowledge that it will get done.
NB. It is not necessary to create your own interface for the command pattern: java.lang.Runnable should always suffice, in my view. Use the standard libraries whenever possible!
We could, of course, use the JSR-250 @PostConstruct annotation, supported by Spring, wherever we wanted to do some kind of “bootstrapping”. However, if we do so in an ad hoc fashion, (a) this logic becomes scattered throughout the codebase under generic metadata that makes it more difficult to isolate in the process (b) fragmenting this responsibility and (c) hindering re-use. Therefore, my approach was as follows …
First, create an org.apposite.command package as the namespace for all code units that constitute a command (i.e. they are an implementation of java.lang.runnable and achieve some given technical requirement (i.e. guarantee state on completion or throw an IllegalStateException on post-condition failure).
Second, extend the @Component stereotype to introduce a new specialisation, which I decided to call @StartupCommand:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface StartupCommand {
String value() default "";
}
Second, implement a java.lang.Runnable for each “thing” that needs to be done on startup. For example, in Apposite, for various reasons I will not bore you with here, I want to “register” the runtime instance that is in the proces of starting up with a repository:
@StartupCommand public class RegisterRuntimeInstanceCommand implements Runnable {
private final AppositeRuntimeInstanceDAO ridao;
private final String riname;
@Autowired public DetermineRuntimeInstanceCommand(@Value("${apposite.runtime.instance.name}") String instanceName, AppositeRuntimeInstanceDAO appositeRuntimeInstanceDAO) {
riname = instanceName;
ridao = appositeRuntimeInstanceDAO;
}
@Override public void run() {
try {
InetAddress localhost = InetAddress.getLocalHost();
ridao.insertOrUpdate(riname, localhost.getHostName(), localhost.getHostAddress());
} catch (Exception e) {
throw new IllegalStateException("Runtime instance registration failure", e);
}
}
}
Now, for the command executor, we needn’t do anything nearly so fancy: we can encapsulate our boostrap logic (@PostConstruct) into a normal Spring bean which, for the sake of argument, I am going to call a bootstrap “service” …
public interface BootstrapService {
void startup();
}
My implementation of which is as follows:
@Service public class BootstrapSpringService implements BootstrapService {
private static final Logger LOG = getLogger(BootstrapSpringService.class);
private boolean bootstrap = true;
private List<Runnable> startupCommands = new ArrayList<Runnable>();
private final PlatformTransactionManager txmanager;
@Autowired public BootstrapSpringService(PlatformTransactionManager transactionManager) {
txmanager = transactionManager;
}
@Autowired public void setBootstrap(@Value("${apposite.bootstrap}") boolean bootstrap) {
this.bootstrap = bootstrap;
}
@Autowired(required = false) public void setCommands(List<Runnable> commands) {
for (Runnable command : commands) {
if (AnnotationUtils.findAnnotation(command.getClass(), StartupCommand.class) != null) startupCommands.add(command);
}
}
// NB. we cannot use @Transactional here because this is called *before* the creation of the transaction proxies
@PostConstruct @Override public void startup() {
if (bootstrap) {
LOG.info("Bootstrapping Apposite ...");
TransactionStatus tx = txmanager.getTransaction(new DefaultTransactionDefinition());
for (Runnable command : startupCommands) {
LOG.info("Running startup action: " + command.getClass().getName());
long start = System.currentTimeMillis();
command.run();
LOG.info(command.getClass().getName() + " completed in " + (System.currentTimeMillis() - start) + "ms");
}
txmanager.commit(tx);
LOG.info("Bootstrap complete.");
}
}
}
As a result, I can very easily determine what is supposed to happen at startup and where. Moreover, neither the bootstrap “service” nor any of the individual commands will be subject to change when, as I expect, additional bootstrap logic is added in future. As a fringe benefit, the intent of the code is described with greater semantic force through the utilisation of a specific specialisation annotation.