스프링의 핵심을 담당하는 것은 빈 팩토리 또는 애플리케이션 컨텍스트라고 불리는 것입니다. 한번 알아보죠~!
애플리케이션 컨텍스트, 빈 팩토리
설정정보를 만드는 방법은 여러가지가 있는데,
AppConfig.java
@RequiredArgsConstructor
public class AppConfig {
private final EntityManager em;
public UserRepositoryV5 userRepository(){
return new UserRepositoryV5Impl(em);
}
public UserServiceV5 userService(){
return new UserServiceV5(userRepository());
}
}
이러한 (롬복을 사용하긴 했지만?) 순수 자바 코드도, 스프링 애노테이션을 활용하 간단하게 설정정보를 만들어 줄 수 있습니다.
설정 정보 만드는 방법
1. 애플리케이션 컨텍스트를 위한 오브젝트 설정을 담당하는 클래스라고 인식할수 있도록 @Configuration
이라는 어노테이션을 추가합니다.
2. 오브젝트를 만들어주는 메소드에는 @Bean
이라는 애노테이션을 추가합니다.
AppConfig.java
@Configuration
public class SpringAppConfigV1 {
@Bean
@Primary
public LocalEntityManagerFactoryBean getEmf(){
LocalEntityManagerFactoryBean emf=new LocalEntityManagerFactoryBean();
emf.setPersistenceUnitName("hello");
return emf;
}
@Bean
@Primary
public EntityManager getEm(){
return getEmf().getObject().createEntityManager();
}
@Bean
public UserRepositoryV5 userRepository(){
return new UserRepositoryV5Impl(getEm());
}
@Bean
public UserServiceV5 userService(){
return new UserServiceV5(userRepository());
}
}
현재, 이 설정 정보에서는 EntityManager Bean을 직접 만들어줬습니다. 헌재까지는, 스프링 부트에서 자동 주입하여 주는 EntityManager를 사용해주었습니다., 지금
이 설정정보 클래스에서는 아직 스프링 부트에서 만든 EntityManager Bean이 존재하지 않기 때문에 따로 EntityManager Bean과 TransactionManager Bean을
만들어야합니다. 저는 Transaction이 존재하지 않아도 테스트를 수행하는데는 무리가 없다 생각하여, EntityManager만 생성해 주었고
스프링 애플리케이션이 로드될때, 스프링 부트에서 만드는 EntityManager와 혼동이 생길 수 있으므로 @Primary 애노테이션을 추가로 붙여 주었습니다.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- 필수 속성 -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/dependencytest"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
</persistence>
이렇게, 애플리케이션 컨텍스트가 Ioc방식의 기능을 제공할 때 사용할 완벽한 설정정보를 만들었습니다.
이제, 테스트 코드를 통해 ApplicationContext를 만들어봅시다.
1. AnnotationConfigApplicationContext를 이용하여 설정정보를 적용한 애플리케이션 컨텍스트 생성
2. getBean() 메소드를 활용하여 설정정보에 정의해 놓은 Bean을 가져올 수 있습니다.
SpringAppConfigTest.java
@SpringBootTest
@Transactional
public class SpringAppConfigV1Test {
@Test
@DisplayName("Bean Test")
void 빈_테스트(){
User user=createUser("hong","123");
AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(SpringAppConfigV1.class);
UserServiceV5 userService= ac.getBean("userService",UserServiceV5.class);
Long saveId = userService.join(user);
User findUser = userService.findOne(saveId);
Assertions.assertThat(findUser.getName()).isEqualTo(user.getName());
Assertions.assertThat(findUser.getPassword()).isEqualTo(user.getPassword());
}
private User createUser(String name, String password){
return User.createUser()
.name(name)
.password(password)
.build();
}
}
동작 확인
동작 확인 까지 하였습니다. 그런데 이렇게 봐서는 더 번거로울 뿐이지 딱히 장점은 없어보입니다.
이러한 고민은, 스프링은 날려버리라고 합니다. 얻을 수 없는 방대한 기능을 제공할테니..
오브젝트 팩토리와 애플리케이션 컨텍스트
사용 방식 및 설정 정보 먼저 보시죠.
오브젝트 팩토리
@RequiredArgsConstructor
public class AppConfig {
private final EntityManager em;
public UserRepositoryV5 userRepository(){
return new UserRepositoryV5Impl(em);
}
public UserServiceV5 userService(){
return new UserServiceV5(userRepository());
}
}
AppConfig appConfig=new AppConfig(em);
UserServiceV5 userService= appConfig.userService();
애플리케이션 컨텍스트
@Configuration
public class SpringAppConfigV1 {
@Bean
@Primary
public LocalEntityManagerFactoryBean getEmf(){
LocalEntityManagerFactoryBean emf=new LocalEntityManagerFactoryBean();
emf.setPersistenceUnitName("hello");
return emf;
}
@Bean
@Primary
public EntityManager getEm(){
return getEmf().getObject().createEntityManager();
}
@Bean
public UserRepositoryV5 userRepository(){
return new UserRepositoryV5Impl(getEm());
}
@Bean
public UserServiceV5 userService(){
return new UserServiceV5(userRepository());
}
}
AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(SpringAppConfigV1.class);
UserServiceV5 userService= ac.getBean("userService",UserServiceV5.class);
여기서 보시면, 오브젝트 팩토리는 service
오브젝트를 생성하고 repository
와 관계를 맺어주는 제한적인 역할을 하는 데에 반해,
애플리케이션 컨텍스트는 IOC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당합니다.
애플리케이션 컨텍스트는 @Configuration 이 붙은 설정 정보를 활용하여 등록 된 빈을 호출해서 가져온 것을 클라이언트가 getBean()을 요청할 때
전달해 줍니다.
애플리케이션 컨텍스트를 사용하는 이유는 범용적이고 유연한 방법으로 Ioc기능을 확장하기 위해서 입니다. 이렇게만 해서는 아직 까지도 장점이 뭔지
감이 안잡힙니다. 자세히 살펴보죠.
애플리케이션 컨텍스트를 사용했을 때의 장점
AppConfig ac=new AppConfg()
, 새로운 팩토리인 DaoFactory가 만들어 졌다면DaoFactory da=new DaoFactory()
이렇게 구체적으로 알아야 함.)AnnotaionConfigApplicationContext ac=new Annotaion ConfigApplication(@Configuration이 붙은 클래스)
)