Dependency Injection (1)

Dependency injection is a programming technique that makes a class independent of its dependencies. That enables you to replace dependencies without changing the class that uses them, and It reduces the risk that you have to change a class just because one of its dependencies changed. It also makes code easier to test What DI does is loosing coupling or decoupling.

Create beans

There are three ways to create beans: automatic configuration, JavaConfig, and XML.

Automatic Spring configuration

Component scanning is that Spring automatically discovers beans to be created in the application context. Autowiring is that Spring automatically satisfies bean dependencies.

@Component annotation identifies this class as a component class and serves as a clue to Spring that a bean should be created for the class.

1
2
3
4
5
6
7
8
9
10
11
package soundsystem;
import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play() {
System.out.println("Playing " + title + " by " + artist);
} }

Way 1 to enable component scanning

@ComponentScan enables component scanning in Spring.

@Configuration annotation identifies this as a configuration class

1
2
3
4
5
6
7
8
9
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
// @ComponentScan(basePackages={"soundsystem", "video"})
// if you want to scan multiple base package
public class CDPlayerConfig {}

Way 2 to enable component scanning

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
// define base-package here
<context:component-scan base-package="soundsystem" />
</beans>

Wire beans with JavaConfig

The @Bean annotation tells Spring that this method will return an object that should be registered as a bean in the Spring application context.

This method annotated with @Bean should be in a configuration class annotated with @Configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
// No @ComponentScan() for @Component
public class AppConfig {

@Bean
// The ID of the bean will be cdPlayer, the same as the method’s name.
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}

@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers() {
return new SgtPeppers();
}
}

Wire beans with XML

When Spring encounters this element, it will create an instance of CDPlayer. The element tells it to pass a reference to the bean whose ID is compactDisc to the CDPlayer’s constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<bean class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>
</beans>

Inject beans

CDPlayerTest takes advantage of Spring’s SpringJUnit4ClassRunner to have a Spring application context automatically created when the test starts. And the @ContextConfiguration annotation tells it to load its configuration from the CDPlayerConfig class. Because that configuration class includes @ComponentScan, the resulting application context should include the CompactDisc bean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package soundsystem;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Autowired
private CompactDisc cd;

@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}

There are three ways to inject beans, please see another blog for more details.

Difference between @Component and @Bean

  1. @Component auto detects and configures the beans using classpath scanning whereas @Bean explicitly declares a single bean, rather than letting Spring do it automatically.
  2. @Component does not decouple the declaration of the bean from the class definition where as @Bean decouples the declaration of the bean from the class definition.
  3. @Component is a class level annotation whereas @Bean is a method level annotation and name of the method serves as the bean name.
  4. @Component need not to be used with the @Configuration annotation where as @Bean annotation has to be used within the class which is annotated with @Configuration.
  5. We cannot create a bean of a class using @Component, if the class is outside spring container whereas we can create a bean of a class using @Bean even if the class is present outside the spring container.
  6. @Component has different specializations like @Controller, @Repository and @Service whereas @Bean has no specializations.

Let’s say that you want to wire components from some third-party library into your application. Because you don’t have the source code for that library, there’s no opportunity to annotate its classes with @Component and @Autowired. Therefore, automatic configu- ration isn’t an option.

Especially when you import the client of another service.

1
2
3
4
5
6
7
@Configuration
public class WireThirdLibClass {
@Bean
public ThirdLibClass getThirdLibClass() {
return new ThirdLibClass();
}
}

Let’s consider I want specific implementation depending on some dynamic state. @Bean is perfect for that case.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class WebSocketConfig {
@Bean
public OneService getService(status) {
int choice = (int) Math.floor(Math.random() * 4);
case (choice) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}
}

Note

  1. By default, all beans in Spring are singletons
  2. Decoupling is essential for unit testing. DI is a great way to achieve decoupling
  3. @Inject and @Autowired are interchangeable in many cases except some subtle differences

Summary

the core of the Spring Framework is the Spring container. This container manages the lifecycle of the components of an application, creating those components and ensuring that their dependencies are met so that they can do their job.

Reference: Spring in Action

Thank you for reading!