Motivation for Component Frameworks

advertisement
Introduction to
Component Models and
Technologies
• Why do we need them ?
• What do they minimally do ?
•How do they actually do it ?
Review:
Component Models and Component Frameworks
Component
Component
Component
Component platform (component framework)
Middleware
Operating System
Hardware
Platform Services: allow
components written
according to the model to
communicate; locating,
linking, replacing
components
Horizontal Services:
application-independent
services used by different
components.
Concurrency, security,
transaction management,
Resource management
Component technologies
• Component technology = component model +
component framework
•
Different component models available:
– Old and new
– Industrial or research
– General-purpose or specialized for different domains
– Having different concepts for components
– Providing a larger or smaller set of platform services
• Examples:
– Java Beans, EJB, COM, DCOM, .NET Components, CCM,
OSGI, Spring, PicoContainer, Fractal, OpenCOM, Autosar,
KOALA, PECOS, …
Introduction goals
• Motivation
– Why do we need them ?
– What is the minimum they must do ?
• Basic principles
– How do they actually do it ?
• Bibliography: Martin Fowler: Inversion of Control
Containers and the Dependency Injection Pattern,
http://www.martinfowler.com/articles/injection.html
•
The Basic Goals of CBD
• Assemble a system out of existing (third-party)
components
• Update a system by adding / replacing components
• Component:
– Is a unit of deployment
– Is handled as it is (a blackbox)
Example 1
• A simple text editor can be composed with a spell checker for
English language or with a spell checker for Romanian language
SpellChecker1
TextEditor
SpellChecker2
Example 1
• The Component Diagram shows a simple hierarchical composition
Example 2
• From: Martin Fowler: Inversion of Control Containers and
the Dependency Injection Pattern
MovieFinder1
MovieLister
MovieFinder2
Example 2
• A MovieLister is able to list movies with certain characteristics after
being provided an exhaustive list of movies by a MovieFinder
• MovieFinder is an interface;
• There could be several different MovieFinderImpl components: one
implementation finds movies by reading a text file, one finds movies
from a relational database, one finds movies crawling the web for
movie advertisings, etc.
A naive solution design
public interface MovieFinder {
List findAll();
}
public class MovieLister {
private MovieFinder finder;
This is
good
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove(); }
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
}
BUT the MovieLister will still
need a
MovieFinderImplementation !
public interface MovieFinder {
List findAll();
}
public class MovieLister {
private MovieFinder finder;
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove(); }
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
public MovieLister() {
finder = new MySpecialMovieFinderImplem();
}
}
This is
BAD !!
The goal: loosely coupled
components
• MovieLister should work with any MovieFinderImplementation
• MovieLister does not need to know the particular type of finder
implementation it is using
• The good solution: eliminate all lines of code such as:
• MovieFinder f = new MyParticularMovieFinderImpl();
– A component should NEVER create (instantiate) its
dependencies
• The solution is called “Inversion of Control”
– In this context, IoC means that a component does not create
(instantiate) its dependencies but has someone else creating
them for it
The concept of
“Inversion of Control”
• The general definition:
– All application frameworks make use of a design
pattern known as “inversion of control”
• It occurs whenever we define code that will be called by the
framework to handle application specific behavior
• This is what distinguishes a framework from a library
The concept of
“Inversion of Control”
• In the context of components:
– The application independently defines a set of
components and their dependencies and the
component framework (called container) uses this
information to
• wire the components together at run-time
• call its code at specific times in the life cycle
Inversion of Control
• Inversion of Control can be achieved through
several patterns:
– Dependency Injection
– Service Locator
Dependency Injection
An Assembler instantiates concrete implementations and
“injects” them into the component that needs them
Forms of Dependency Injection
• Constructor Injection
– MovieLister has a constructor that will get the
MovieFinderImplementation
• Setter Injection
– MovieLister has a setter method that will get the
MovieFinderImplementation
• Interface Injection
– An interface InjectFinder, with method injectFinder, defined by
the provider of the MovieFinder interface
– MovieLister (and any class that wants to use a MovieFinder)
needs to implement this interface
Component Containers
• What has the Dependency Injector pattern to do with
component frameworks ?
• The “Assembler” component of the Dependency
Injection pattern is called a “Component
Container” and is part of the component
framework
– The assembler (Container) is generic (for any
application), thus it:
• Requires that components follow a certain convention
(constructor, setter, injector interfaces)
• Requires to be told (by code or configuration files) which
implementation to associate with which interface
Using Component Containers
1)
Take a set of components (concrete classes +
interfaces)
•
2)
3)
they have to implement constructors, setters or interfaces for
dependency injection – according to the convention required
by the component framework
Add configuration info in form of:
•
Configuration metadata (config files)
•
Configuration code
Add client code to use the container
Examples
• Component Containers are used in many component
frameworks:
– “Lightweight” IoC Containers
– Complex component frameworks, that provide also other
features beyond IoC Containers
• Examples:
•
•
•
•
•
•
•
PicoContainer
Spring
Unity
Guice
EJB
CCM
OSGI with DS
Example: Constructor Injection with PicoContainer
(1) Including Constructors
class MovieLister...
public MovieLister(MovieFinder finder) {
this.finder = finder;
}
class ColonMovieFinder...
public ColonMovieFinder(String filename) {
this.filename = filename;
}
Each class declares
constructors that
include everything it
needs injected
Example: Constructor Injection with PicoContainer
(2) Describing the configuration
private MutablePicoContainer configureContainer() {
MutablePicoContainer pico = new DefaultPicoContainer(); Parameter[]
finderParams = {new ConstantParameter("movies1.txt")};
pico.registerComponentImplementation(MovieFinder.class,
ColonMovieFinder.class, finderParams);
pico.registerComponentImplementation(MovieLister.class);
return pico;
}
The pico container needs to
This kind of configuration information could
be told which implementation
come as well from configuration
files. Some
class to associate
with each
component frameworks
support
it to
interface,
andreading
which string
from config inject
files into the finder
Example: Constructor Injection with PicoContainer
(3) Using the composed system
public void testWithPico() {
MutablePicoContainer pico = configureContainer();
MovieLister lister = (MovieLister)
pico.getComponentInstance(MovieLister.class);
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}
Example: Setter Injection with Spring
(1) Defining setters
class MovieLister...
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
class ColonMovieFinder...
public void setFilename(String filename) {
this.filename = filename;
}
Each class defines
setters that include
everything it needs
injected
Example: Setter Injection with Spring
(2) Describing the configuration
<beans>
<bean id="MovieLister" class="spring.MovieLister">
<property name="finder">
<ref local="MovieFinder"/>
</property>
</bean>
<bean id="MovieFinder" class="spring.ColonMovieFinder">
<property name="filename">
<value>movies1.txt</value>
</property>
</bean>
</beans>
Example: Setter Injection with Spring
(3) Describing the configuration
public void testWithSpring() throws Exception {
ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");
MovieLister lister = (MovieLister) ctx.getBean("MovieLister");
Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}
Inversion of Control
• Inversion of Control can be achieved through
several patterns:
– Dependency Injection
– Service Locator
Service Locator
A Service Locator knows how to get hold of
all of the services that an application might need.
Example: Service Location with OSGI
(1) Registering the service
public class BasicMovieFinderActivator implements BundleActivator {
private ServiceRegistration registration;
public void start(BundleContext context) {
MovieFinder finder = new BasicMovieFinderImpl();
registration = context.registerService( MovieFinder.class.getName(),
finder, null);
….
}
This Service Locator is a Dynamic one
(it allows to stash any service you need
into it and make the choices at runtime)
Example: Service Location with OSGI
(2) Retrieving the service
public class MovieListerActivator implements BundleActivator {
private ServiceTracker finderTracker;
public void start(BundleContext context) throws Exception {
…
finderTracker = new ServiceTracker(context, MovieFinder.class.getName(), null);
finderTracker.open();
MovieFinder finder = (MovieFinder) finderTrack.getService();
….
}
Comparison:
Service Locator vs Dependency Injection
• Both provide decoupling (keep application code
dependent only on interfaces, not on implementations)
• Other criteria:
– How explicit are the dependencies ?
– How “intrusive” is the approach into the application code ?
– Dynamic dependencies are possible ?
Comparison: (1)
Service Locator vs Dependency Injection
• “Intrusive” into application code:
– With service locator, the application component
explicitly asks the locator for its dependency
• Every component has a dependency to the locator
• This is “intrusive” into the component development process
(such components cannot be used without the framework)
– With dependency injection, the application component
does not have to ask anything, its dependencies just
appear injected
• Less intrusive, such components could be used without the
framework
Comparison: (2)
Service Locator vs Dependency Injection
• Explicit dependencies:
– With service locator you have to search the source
code for calls to the locator.
– With dependency injection you can just look at the
injection mechanism, such as the constructor, and
see the dependencies.
Comparison: (3)
Service Locator vs Dependency Injection
• Dynamic dependencies:
– With dependency injection you don't have a
dependency from a component to the injector, the
component cannot obtain further services from the
injector once it's been configured.
– With service locator a component can locate new
services at any time or replace/update them .
Conclusion
• Component frameworks must support the assembly of
independent components into applications at
deployment time or even runtime
– lightweight IoC containers at least
• Component frameworks may support different additional
aspects:
– Component lifecycle support
– Hierarchical components
– Concurrency, distribution, transactions, …
Download