Java Chapter 6
Java Chapter 6
1. Traditional Approach:
In a typical Java program, objects often create their own dependencies, leading
to tightly coupled code. For example:
public Car() {
this.engine = new Engine(); // Creating the depend
}
}
This approach can make the code rigid, harder to test, and less adaptable to
changes.
Java Chapter 6 1
The object (e.g., Car) doesn't create its own dependencies; instead, they are
"injected" from the outside.
Constructor Injection:
Example:
Setter Injection:
Example:
Java Chapter 6 2
Decoupling: Reduces the coupling between components, making the code
more modular and maintainable.
1. Constructor Injection:
Description:
Java Chapter 6 3
In Constructor Injection, dependencies are injected into the dependent
class through its constructor.
The dependencies are required for the proper functioning of the class.
Example:
// Constructor Injection
public Car(Engine engine) {
this.engine = engine;
}
}
Advantages:
Ensures that the required dependencies are available at the time of object
creation.
2. Setter Injection:
Description:
The dependencies are not mandatory for the class to function; they can be
set or changed after the object is created.
Example:
// Setter Injection
public void setEngine(Engine engine) {
Java Chapter 6 4
this.engine = engine;
}
}
Advantages:
3. Method Injection:
Description:
This is less common than constructor and setter injection but can be useful
in specific scenarios.
Example:
// Method Injection
public void start(Engine engine) {
this.engine = engine;
// additional logic...
}
}
Advantages:
Description:
Java Chapter 6 5
Interface Injection is a less common approach where a class implements an
interface that mandates the injection of dependencies.
This approach is not widely used due to its complexities and is less favored
compared to constructor and setter injection.
Example:
@Override
public void setEngine(Engine engine) {
this.engine = engine;
}
}
Considerations:
Can lead to a more complex design and may not provide significant benefits
over other injection types.
In most cases, Constructor Injection and Setter Injection are the predominant choices.
The selection between them depends on the specific requirements of the application,
such as whether dependencies are mandatory, need to be changed at runtime, or can
be optional.
Circular dependency occurs when two or more classes depend on each other directly or
indirectly, creating a loop in the dependency graph. This situation can lead to issues
Java Chapter 6 6
during object creation and initialization because each class in the cycle requires the
other to be instantiated first, resulting in a deadlock.
// Class A depends on B
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
// Class B depends on A
public class B {
private A a;
public B(A a) {
this.a = a;
}
}
Introduce an interface for one of the classes and use constructor injection. This
breaks the circularity by allowing the container to provide a proxy or a delayed
implementation.
// Interface
public interface AService {
// methods...
Java Chapter 6 7
}
public A(AService b) {
this.b = b;
}
}
// Class B depends on A
public class B implements AService {
private AService a;
public B(AService a) {
this.a = a;
}
}
2. Setter Injection:
Use setter injection instead of constructor injection. This way, you can break the
circular dependency by creating instances of both classes and then injecting the
dependencies later.
Java Chapter 6 8
public class B {
private A a;
3. Consider Refactoring:
Reevaluate the design and see if it's possible to reorganize the classes to break
the circular dependency. This might involve creating a new class or
restructuring the existing ones to eliminate the dependency loop.
It's crucial to carefully analyze the dependencies and their relationships to ensure a
clean and maintainable design.
1. Base Configuration:
Create a base configuration where you define a bean with some properties and
behavior.
2. Derived Configuration:
Java Chapter 6 9
The derived bean inherits properties and behaviors from the base bean.
The derived Java class extends the base Java class, inheriting its properties
and methods.
Access the beans from the context, and Spring takes care of managing the
inheritance and overriding.
5. Runtime Behavior:
When you retrieve the bean from the context, you observe the behavior based
on the configuration hierarchy.
Example Scenario:
Suppose you have a base service bean with a message property and a displayMessage
method. In a derived configuration, you may override the message property for a
specific use case while inheriting the rest of the behavior from the base configuration.
This approach allows you to create modular and reusable configurations, promoting a
more organized and maintainable code structure. It aligns with the principles of object-
oriented design and inheritance.
Example:
Java Chapter 6 10
Consider a scenario where you have a prototype-scoped bean, and you want to obtain
a new instance of this bean every time a specific method is called. Here's an example
using the @Lookup annotation:
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component
public class PrototypeBean {
In this example:
The getNewInstance() method is annotated with @Lookup , indicating that the Spring
container should override this method and provide a new instance of the bean every
time it is invoked.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
Java Chapter 6 11
public class SingletonBean {
@Autowired
private PrototypeBean prototypeBean;
In this example:
When the performOperation() method is called on the SingletonBean , you will see different
hash codes in the output, indicating that a new instance of the PrototypeBean is created
each time getNewInstance() is called.
context.close();
}
}
Java Chapter 6 12
This usage of @Lookup provides a way to achieve prototype-style behavior within a
singleton-scoped bean, offering more control over the lifecycle of certain dependencies
in a Spring application.
Spring acts like a factory for beans. There are two types: BeanFactory (basic
features) and ApplicationContext (more advanced with extra capabilities like
events).
3. Lifecycle Stages:
4. Bean Scopes:
Beans can have different "scopes." Singleton means one instance for everyone;
prototype means a new instance each time. You decide this when defining the
bean.
5. Dependency Injection:
Java Chapter 6 13
Some beans can be lazy, meaning they're created only when needed. Others
are eager, created right away when Spring starts. This influences when beans
make friends.
You can add your own methods to beans, making them unique. Spring lets you
express bean configurations using annotations or XML, depending on your
preference.
8. FactoryBeans:
There are special beans called FactoryBeans that act as master builders,
creating unique beans with special logic. They bring an additional layer of
customization.
9. Autowiring:
You can have different sets of beans for different environments using "profiles."
It's like having different groups of friends for various occasions.
Spring lets you express bean configurations in a way that suits you. You can
speak in XML (like writing a letter) or use annotations (like sending a text
message).
Java Chapter 6 14