- Authors

- Name
- Youngju Kim
- @fjvbn20031
A typical web application (backend) consists of five components:
- Controller: Handles the controller role in web MVC
- Service: Implements core business logic
- Repository: Accesses the database, stores and manages domain objects in the DB
- Domain: Business domain objects
- DB: A separate process that stores actual data (outside the scope of Spring)
In Spring, components such as services and repositories are managed as objects called Beans, which are automatically injected where and when needed. (This is called Dependency Injection.) The reasons for having Spring manage these components will be covered in detail in the next post. The minimum annotation needed to have Spring manage something as a Bean is @Component, but for services, the @Service annotation is typically used as shown below. (The @Service annotation internally includes @Component.) This is a Bean registration method using Component Scan. There is also a way to explicitly register Spring Beans using Java code instead of annotations.
Setting Up Automatic Dependency Injection with Component Scan
Registering MemberService
For example, to register a service called MemberService, add the @Service annotation as shown below. The reason @Autowired is added to the constructor is to have Spring automatically inject the memberRepository Bean that it manages.
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/*
Join membership
*/
public Long join(Member member){
// No duplicate members with the same name
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m ->{
throw new IllegalStateException("A member with this name already exists");
});
}
/*
Look up all members
*/
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
Registering MemoryMemberRepository
For a repository, add the @Repository annotation.
@Repository
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter((member) -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore(){
store.clear();
}
}
The @Repository annotation also contains @Component.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
Note These annotations do not work in all packages -- they only apply to the package containing the
@SpringBootApplicationannotation and its sub-packages. Spring Beans are created as singletons, meaning only one instance is created and managed.
Registering Spring Beans with Java Code
Let's learn how to register Spring Beans without using the @Service, @Repository, and @Autowired annotations. Remove the three annotations mentioned above that were used for Bean injection, and create a file called SpringConfig.java with the following content. Use the @Configuration annotation to register the file as Spring-related, and write functions with the @Bean annotation that create and return the objects to be registered as Beans.
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
There are three types of DI: constructor injection, field injection, and setter injection. Field injection has the disadvantage that fields cannot be changed after creation. Setter injection is undesirable because the setter must be public, which exposes it. The most recommended approach is injection through the constructor. In practice, development is typically done using the component scan approach, but in situations where the implementation class needs to be swapped with a different Bean, the Java code-based Bean configuration method is also used to make changes without modifying the existing code.
References
- Kim Young-han's Inflearn course: Spring Introduction - Spring Boot