Related to Factory Objects - Prototype Scope
In the previous post, I mentioned a few ways to make a factory or provider object.- A configuration bean - The bean class is annotated with @Configuration, and you can add various @Bean methods that get called to create the instances.
- Factory Bean / Factory Method -
<bean id="makeOne" class="com.foo.SomeBean" scope="prototype"/>
Similarly, with annotations:
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class SomeBean { ... }
Events
Spring also has an event framework, along with some standard events that the framework produces, allowing you to extend the framework more easily. While this is not as annotation driven and fully decoupled as the CDI event framework, it functions in pretty much the same way.To create your own event, simply extend ApplicationEvent.
public class MyEvent extends ApplicationEvent { private final String message; public MyEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } }
To produce events, beans must implement ApplicationEventPublisherAware. Usually this class will store the ApplicationEventPublisher and use it later on to publish events.
@Component public class MyEventProducer implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void someBusinessMethod() { ... applicationEventPublisher.publishEvent(new MyEvent(this, "Hey! Something happened!")); ... } }
NOTE: It is important to understand that all of the listeners will be called on the caller's thread unless you configure the application event system to be asynchronous. I'll cover that in another blog post. The benefit of having the listeners execute on the caller's thread is that the Spring transactional context will propagate to the listeners.
To observe events, have a component implement ApplicationListener<T>, where T is the event class.
@Component public class MyListener implements ApplicationListener<MyEvent> { @Autowired private SomeBusinessLogic logic; @Override @Transactional public void onApplicationEvent(MyEvent event) { logic.doSomething(event.getMessage()); } }
The Downside of ApplicationEvent
One noticeable downside of using Spring's ApplicationEvents is that IDEA does not recognize them as it does with CDI events. This is kind of a bummer, but it's no worse than using Guava's EventBus, for example.Mitigation? I think that using the event class (the subclass of ApplicationEvent) for one and only one purpose is probably sufficient. It's a good idea to have purpose built DTOs anyway.
The Benefits of ApplicationEvent
The benefits of using ApplicationEvent over other possibilities can make them very worthwhile:- De-coupling excessively coupled components - Often, a business logic component will trigger many different actions that don't need to be tightly coupled. For example, notifying users via email / SMS and IM is best left de-coupled from the actual business logic. The notification channels don't need to know about the business logic, and vice versa. Also, you can much more easily add new notification channels without modifying the business logic at all!
This was a very useful technique in improving the architecture of an existing Spring application that I have been working on. - Zero additional libraries - You're already using Spring, so there's nothing to add. No additional dependencies.
- Listen for Spring's own events - You can hook into events that Spring itself fires, which can be very useful. Application start and stop, for example.
Request and Session Scope
Request and Session scopes are not hard to understand - each scope defines a set of objects that exist for the duration of the scope, and are destroyed when the scope ends. However, the challenge comes when a longer lived scope wants to inject a bean in a shorter lived scope (e.g. an application scoped bean wants to inject a session or request scoped bean), this gets a little more complicated.In implementing this, Spring takes a very different approach than that of CDI and Seam. In CDI and Seam, an application scoped component is injected with request / session / conversation scoped beans on every method call (and un-injected when the method completes!).
Spring takes a different approach: rather than inject the beans on every single method call, Spring injects a proxy and that proxy is modified to refer to the bean instance in the proper scope by the framework.
@Component @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public class RequestBean { private final long createdOn = System.currentTimeMillis(); public long getCreatedOn() { return createdOn; } }
Of course, this only works when Spring MVC is enabled, as otherwise there is no request context.
See also:
No comments:
Post a Comment