kenzi

컨테이너와 빈 방식에 관하여 본문

Spring

컨테이너와 빈 방식에 관하여

kenzi 2022. 3. 30. 23:56

스프링에서 컨테이너란 무엇인가? 

Container는 자바 객체를 관리하는 객체간의 연관관계를 생성하는 역할 

여기서 자바 객체를 Bean이라고 한다 

 

왜 Container를 사용하는가?

제어의 흐름을 외부에서 관리하기 위해서

 

왜 제어의 흐름을 외부에서 관리해야하는데? 

객체를 재사용하기 쉽고 유지보수를 용이하게 하기 위해서 

클래스에서 매번 객체를 생성하여 사용했을 시 객체가 추가되거나 수정되어야 하는 상황이 일어났을때 클래스마다 쫓아가서 수정해야하는 일이 발생 = 너무 번거로움 

객체를 외부에서 주입시켜주는 방식이라면 클래스마다 쫓아가지 않아도 주입할때 다른 것을 주입시켜주면 수정이 가능 

예시) 회사마다 다른 자소서를 쓸 때 지원회사이름을 눈빠지게 찾아서 수정하지 않아도 한번 수정하면 다같이 수정되는 그런 느낌적인 느낌....예시가 이게 맞나...

 

 

 

Container 안에 Bean 넣는 방법 

1) xml에 <bean id ="~" class = "~"/> 태그로 넣고 

//Container1.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
	xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd">
	

<bean id = "greeter" class = "ch02_obj.Greeter">
	<property name ="format" value = "안녕하세요 스프링 첫날입니다">	
	</property>	
</bean>

<bean id = "pwChangeSvc" class = "ch02_obj.PasswordChangeService">
		<constructor-arg>
			<ref bean = "userRepository"></ref>
		</constructor-arg>
</bean>
	
	
</beans>

1-1) Main에서 

GenericXmlApplicationContext로 빈 팩토리를 만들고 .getBean(bean의 id, class)로 bean을 불러오기

GenericXmlApplicationContext factory  
		= new GenericXmlApplicationContext("classpath:ch02_DIXML/Container1.xml");
 Greeter g1 = factory.getBean("greeter", Greeter.class);
 
 
 //getBean하는 방식 중에 이런 방식도 있습니다
 PasswordChangeService pcw = (PasswordChangeService) factory.getBean("pwChangeSvc");

 

 

2) xml에서 <context:component-scan base-package="패키지명"/> 을 사용하고 - 왜? 매번 xml등록하기 번거로워..

//Container4_scan.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
	xmlns:context = "http://www.springframework.org/schema/context"
	xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
	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">
	
	<!-- scan으로 패키지에 있는 bean을 가져오겟다 -->
	<context:component-scan base-package="ch02_objScan"/>


	<bean id = "user1" class = "ch02_obj.User">
	<constructor-arg value = "kicuser"></constructor-arg>
	<constructor-arg value = "1111"></constructor-arg>
	</bean>
	
	
	
	<bean id = "user2" class = "ch02_obj.User">
	<constructor-arg value = "kicstu"></constructor-arg>
	<constructor-arg value = "1234"></constructor-arg>
	</bean>
	
	
	<bean id = "authFailLogger" class = "ch02_obj.AuthFailLogger">
		<property name = "threshold" value = "2" />
	</bean>
	
	
</beans>

2-1) 스캔할 패키지에 있는 클래스들에 @Component 사용 

@Autowired란 xml에서 의존성 주입을 해주었던 참조객체들을 표시해줘야하니까 

AuthenticationService는 UserRepository와 AuthFailLogger에 의존(참조)하고 있었거든 

//AuthenticationService.java

@Component //이 클래스가 Bean이 될거야 
public class AuthenticationService {
	
	@Autowired //프로퍼티를 자동으로 연결해줍니다
	UserRepository userRepository;
	
	@Autowired
	AuthFailLogger authFailLogger;
	
	/*
	Aurowired에 의해 자동으로 연결해주니까 이 과정이 필요없습니다
	//setter injection 방식
	public void setUserRepository(UserRepository userRepository) {
		this.userRepository = userRepository;
	}
	
	//setter injection 방식
	public void setAuthFailLogger(AuthFailLogger authFailLogger) {
		this.authFailLogger = authFailLogger;
	}
	*/
	
	
}

 

xml에 있었다면 이런 bean태그가 아래와 같이 있었을 것이다...

<bean id = "authenticationService" class ="ch02_obj.AuthenticationService">
		<property name = "userRepository" ref = "userRepository" />
		<property name = "authFailLogger" ref = "authFailLogger"/>
</bean>

 

 

//PasswordChangeService.java

@Component("pwChangeSvc")
public class PasswordChangeService {
	
	UserRepository userRepository;
	
	@Autowired
	public PasswordChangeService(UserRepository userRepository) {
		this.userRepository = userRepository;
	}
	
  }

이것 역시 xml에 있었다면 아래와 같이 있었을 것이다..

<bean id = "pwChangeSvc" class = "ch02_obj.PasswordChangeService">	
		<constructor-arg>
			<ref bean = "userRepository"></ref>
		</constructor-arg>
</bean>

 

 

//UserRepository.java

@Component
public class UserRepository {

	//<property name = "users"> : setter injection
	
	private Map<String, User> map = new HashMap<>();
	
	@Autowired
	public void setUsers(List<User> li) {
		for(User u : li) {
			map.put(u.getId(), u);
			System.out.println(u);
		}
		
	}
}

이것 역시 xml에 있었다면? 아래와 같이!

<bean id = "userRepository" class = "ch02_obj.UserRepository">
		<property name = "users">
			<list>
				<ref bean = "user1" />
				<ref bean = "user2" />
			</list>
		</property>		
</bean>

즉 xml에서 bean태그로 안 걸고 component-scan으로 클래스를 bean으로 스캔해달라~ 

Bean으로 스캔할 클래스가 어떤건지 어떻게 알아?

----> @Component 걸어줄게~~~~~~

 

Q) 그럼 property는 어떻게 알아? xml에서는 property 명시해줬는데 클래스에서는 어떻게 알아?

@Autowired 걸어줄게~~~~

 

Q) @Autowired는 어디에 아무렇게나 걸어? 

1) 필드 2) 메서드 3) 생성자에 걸 수 있다!

한 클래스에서 동시다발적으로 걸 수 없고 하나만 선택해서 걸어야 한다 

@Autowired는 굴비처럼 줄줄이 엮는것 

 

Q) 그럼 xml에 component-scan하고 나머지 Bean객체 모두 scan할 패키지에 넣고 @Component사용하면 되는데 

왜 어떤 객체(user1,user2...)는 xml에 <bean/>태그를 사용해?

value 자체를 정해서 넣어준 객체는 Component로 따로 빼놓을 시 수정,관리가 번거로움

 

 

 

3) xml 없이 컨테이너 만들어서 Bean 등록하기

자바클래스에서 @Configuration으로 여기가 Container다 알려주기 

Bean은 @Bean으로 등록하겠다...!

여기서 의존성주입은 set메서드 바로 쓰거나 생성자 넣어버림으로서 해결

//Container3_Anno.java

@Configuration
public class Container3_Anno {
	
	
	@Bean
	//public xml에서의 class, id ()    
	public User user1() {
		return new User("kicuser","1111");
	}
	
	@Bean
	public User user2() {
		return new User("kicstu","1234");
	}
	
	
	@Bean
	public AuthenticationService authenticationService() {
		AuthenticationService a = new AuthenticationService();
		a.setUserRepository(userRepository());
		a.setAuthFailLogger(authFailLogger());
		return a;
		
	}
	
	
	@Bean
	public UserRepository userRepository() {
		UserRepository u = new UserRepository();
		u.setUsers(Arrays.asList(user1(),user2()));
		return u;
	}
	

	@Bean
	public AuthFailLogger authFailLogger() {
		AuthFailLogger auth = new AuthFailLogger();
		auth.setThreshold(2);
		return auth;
	}
	
	
	@Bean("pwChangeSvc")
	public PasswordChangeService pwChangeSvc() {
		return new PasswordChangeService(userRepository());
	}
	
	
} //end class

3-1) Main에서는 

AnnotationConfigApplicationContext 로 빈 팩토리 만들어서 .getBean하기 

//Main.java

AnnotationConfigApplicationContext factory
	 = new AnnotationConfigApplicationContext(Container3_Anno.class);
		
		
		//getBean하는 방식 중에 이런 방식도 있습니다
		PasswordChangeService pcw = (PasswordChangeService) factory.getBean("pwChangeSvc");

정리하자면 컨테이너를 만드는 방식은 2가지가 있음

1) GenericXmlApplicationContext : xml방식

2) AnnotationConfigApplicationContext : Annotation 방식

 

Bean을 등록하는 방식은 3가지가 있음

1) xml에서 bean태그 사용 

2) @Component 사용 (xml에서 component-scan을 쓸때에)--->이때 @Autowired 사용 

3) @Bean 사용 

 

 

 

'Spring' 카테고리의 다른 글

스프링 4일차 정리  (0) 2022.03.31
스프링은 3가지로 끝난다  (0) 2022.03.31
스프링 3일차  (0) 2022.03.30
스프링 2일  (0) 2022.03.29
스프링 기초 복습 ( feat 빈 설정방법)  (0) 2022.03.28
Comments