Spring Bean 등록 이유 - Spring Bean deunglog iyu

Spring이 객체를 제어하기 위해서는 객체들이 Bean으로 등록되어 있어야 한다. 

기존엔 xml을 통해 bean으로 등록했는데 Spring MVC에서는 @Controller, @Service, @Repository 등의 어노테이션을 통해 Spring Container에 Bean으로 등록할 수 있다.

그리고 Configuration 관련 객체는 @Bean과 @Component 어노테이션을 통해 Bean으로 등록할 수 있다.

여기서 @Bean과 @Component의 차이는 무엇일까?

@Bean

직접 컨트롤할 수 없는 외부 라이브러리를 Bean으로 등록하고 싶은 경우에 사용한다.

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {

	//생략
    
}

@Bean의 Target을 살펴보면 Method로 지정되어 있으며, Method Level에서 적용할 수 있다.

@Configuration을 선언한 클래스 내부에서 사용할 수 있는데, 개발자가 작성한 메소드를 통해 반환되는 객체를 Bean으로 등록해준다.

예를 들어 Session time out listener라던지 Lucy xss 필터를 적용할 경우 아래와 같이 @Bean 어노테이션을 선언해주면 된다. 해당 class는 개발자가 작성한 코드가 아니기때문에 class에 @Component 어노테이션을 붙일 수 없어서 메소드를 통해 객체를 반환시키고 반환된 객체가 Spring Bean에 등록되도록 한다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public ServletListenerRegistrationBean<MySessionListener> listenerRegistrationBean() {
        ServletListenerRegistrationBean<MySessionListener> bean = new ServletListenerRegistrationBean<>();
        bean.setListener(new MySessionListener());
        return bean;
    }

    @Bean
    public FilterRegistrationBean<XssEscapeServletFilter> filterRegistrationBean() {
        FilterRegistrationBean<XssEscapeServletFilter> filterRegistration = new FilterRegistrationBean<>();
        filterRegistration.setFilter(new XssEscapeServletFilter());
        filterRegistration.setOrder(1);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }
    
 }

@Component

직접 컨트롤가능한 class를 Bean으로 등록하고자 할 때 사용한다. 

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {

	//생략
    
}

@Bean의 Target을 살펴보면 Type으로 지정되어 있으며 Class Level에서 적용할 수 있고, 스프링이 런타임시 컴포넌트스캔을 통해 자동으로 bean을 찾아 등록하게 된다.

이렇게 @Bean과 @Component는 Target이 달라서 바꿔서 사용할 수 없다! 

스프링의 장점 중 하나인 빈을 싱글톤 레지스트리로 관리하는 것이 왜 장점인지에 대해 알아보자.

스프링은 was 시작과 동시에 Ioc컨테이너에 빈을 생성하고 관리한다.

이것을 싱글톤 레스지스트리로 관리한다고 한다.

왜 싱글톤 레지스트리로 관리하여 was 시작과 동시에 빈을 생성하는 것이 좋은것일까?

서버의 성능에 좋기 때문이다.

만약, 싱글톤으로 빈을 생성하지않고 매번 요청 시 마다 생성한다고 가정하자.

요청 하나당 5개의 객체를 생성하고 초당 500개의 요청이 올 경우 객체는 2500개를 생성하여 서버의 성능을 저하시키게된다.

싱글톤으로 bean을 생성하게되면, was시작시에만 생성이되고 요청이 계속들어오더라도 bean은 더이상 생성되지않기 때문에 성능을 높일 수있다.

스프링에서는 싱글톤 레지스트리를 사용한다고 했다.

여기서, 싱글톤 레지스트리와 자바의 싱글톤패턴과는 다르다.

싱글톤 레지스트리는 자바의 싱글톤패턴의 단점을 보완한것이라고 할 수 있다.

자바의 싱글톤패턴의 단점은 아래와 같다.

  1. private로 생성자를 지정하기 때문에, 상속되지 않아 다형성을 지원하지 않기때문에 객체지향적이지 못하다.
  2. static으로 지정하기 때문에, 전역상태로 사용이되어 아무 객체나 자유롭게 접근하고 수정하고 공유 할 수있는것은 객체지향프로그래밍에서 권장하지 않는 모델이다.
  3. 싱글톤으로 생성되면 객체테스트가 어렵기 때문에, 테스트를 위한 객체를 새로만들어야한다.

단점을 보완하기 위해 스프링은 싱글톤 레지스트리로 싱글톤 객체생성 관리를 하기때문에 

객체에서는 싱글톤 생성과 관리를 할 필요가 없어져 평범한 객체로써 사용이 가능하다.(스프링의 POJO 기반)

평범한 객체로써 사용이 가능해지면서 아래와 같은 장점을 같게된다.

  1. 생성자를 public으로 지정 가능하여 객체를 객체지향적으로 프로그래밍 가능.
  2. static으로 지정할 필요없기 때문에 전역상태로 관리 되지않아도 된다.
  3. 테스트를 위해 해당객체를 그대로 사용가능하여 테스트에도 용이하다.(자바엔터프라이즈 환경에서는 가장중요)

이러한 스프링에 장점을 활용하여

초기에 설정하면 사용중에는 변하지않는 읽기전용 인스턴스 변수인경우에는

서버의 성능향상을 위해 bean으로 등록하고 사용하자. 

바닥코딩

Bean

 간단하게 한줄로 요약하자면 자바의 객체입니다. spring container에 의해 자바 객체가 만들어 지게 되면서 이 객체를 스프링 빈이라고 부르게 된것이지만 스프링 빈과 일반 객체와의 차이점은 별다르게 없습니다 그렇지만 Spring container  에서 만들어지는 객체를 스프링 빈이라고 부를 뿐입니다 

Spring Bean을 사용하는 이유

그렇다면 자바의 객체를 굳이 bean을 통해 쓰는 이유가 뭐일까 결론부터 이야기 하자면 자주 사용하는 객체를 singleton으로 만들어 놓고 어디서든 불러쓸 수 있도록 한다는 것의 의미가 있습니다 이전 포스팅을 보면 DI(의존성 주입)에 대한 포스팅 내용이 있습니다 의존성 주입은 간단히 말해 모듈간의 결합도를 낮추어 클래스를 수정해야하는 상황을 줄여줍니다 바로 이 과정에서 Spring bean의 활용도가 높아집니다 예를 들어 의존성 주입이 되지 않은 모델은 클래스를 변경해야 하는 상황일떄 직접 다른 연관 클래스를 수정해야 합니다 하지만 di를 사용한다면 개발자는

1.Bean class를 작성한다

2.주입을 위한 xml 설정(spring container 에서는 @어노테이션 기술 사용)

이 두가지 과정을 통해 코드를 수정할 수 있습니다. 해당 포스트 에서는 이 Sprign Bean을 사용해 의존성을 주입하는 방법을 간단한 예제를 통해 만들어 볼겁니다 

Code review(의존성 주입 과 Bean)

먼저 spring project를 생성해 줍니다

Spring Bean 등록 이유 - Spring Bean deunglog iyu

 spring Legacy Project 에서 simple spring maven 을 선택해 프로젝트를 만듭니다 

Spring Bean 등록 이유 - Spring Bean deunglog iyu

그 다음 src/main/java 폴더에 패키지를 추가해 줍니다 

Spring Bean 등록 이유 - Spring Bean deunglog iyu

방금 만든 패키지 내부에 Gender라는 인터페이스 하나를 구현해봅니다

package kr.dkkim.spring.di;

public interface Gender {
	
	public void print_gender();

}
Spring Bean 등록 이유 - Spring Bean deunglog iyu

그 다음 Man 이라는 클래스를 만들어 봅니다 해당 클래스는 인터페이스를 사용할 거기 때문에 인터페이스를 추가해 줍니다 (직접 implements 해서 오버라이딩 해도 상관없음)

package kr.dkkim.spring.di;

public class Man implements Gender {
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public void print_gender() {
		// TODO Auto-generated method stub	
		System.out.println(name+"은 남자입니다");
	}

}

Man 클래스에서는 name변수를 만들고 setter를 설정한다음 오버라이딩  

package kr.dkkim.spring.di;

public class Woman implements Gender {

	private String name;

	@Override
	public void print_gender() {
		// TODO Auto-generated method stub

		System.out.println(name+"는 여자 입니다");

	}

	public void setName(String name) {
		this.name = name;
	}

}

 동일한 방식으로 woman 클래스를 하나 생성해 줍니다

Spring Bean 등록 이유 - Spring Bean deunglog iyu

그다음 빈을 설정할 패키지를 하나 더 생성해 보겠습니다 

Spring Bean 등록 이유 - Spring Bean deunglog iyu

new -> orther 에 들어가 spring Bean Configuration File을 하나 생성해줍니다

<?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">

<!-- 여기에다 코드 삽입 하면 되여 ~~ -->
</beans>

저는 Bean.xml 이라는 이름으로 파일을 하나 추가 했습니다 그 다음 bean을 통해 아까 만들었던 객체와 연결을 해보겠습니다

<bean id="man" class="kr.dkkim.spring.di.Man">
		<property name="name" value="철수"></property>

	</bean>

코드 내에 이렇게 작성을 해줍니다 이렇게 한다면 이전에 생성했던 Man 클래스의 name이라는 변수에 철수라는 이름을 주입하게 됩니다 물론 저 코드를 직접 써도 되지만 Woman 클래스에 주입은 더 간결하게 만들어 보겠습니다

Spring Bean 등록 이유 - Spring Bean deunglog iyu

xml 파일에서 아래 보이는 beans를 클릭한후 새로은 빈을 생성해줍니다

Spring Bean 등록 이유 - Spring Bean deunglog iyu

위와 같이 id를 입력하고 Woman클래슬르 매핑해 줍니다 

Spring Bean 등록 이유 - Spring Bean deunglog iyu

next를 눈르면 프로퍼티의 이름과 value를 지정할 수 있는데 지정 후 완료를 해주면 

<bean id="woman" class="kr.dkkim.spring.di.Woman">
		<property name="name" value="영희"></property>
	</bean>

해당 내용이 추가가 된 것을 확인할 수 있습니다.

이제 bean을 만들었으니 bean을 활용해 보겠습니다 bean 을 활용해 보기 위해 클래스들과 인터페이스를 정의했던 패키지내에 mainApp 클래스를 하나 추가한 다음 아래 코드를 추가해 보겠습니다

package kr.dkkim.spring.di;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("kr.dkkim.spring.di.bean.bean.xml");
		Man man = (Man)context.getBean("man");
		man.print_gender();
	
		context.close();
	}

}

해당코드를 실행하면 bean 의 원리를 이해할 수 있습니다