2020-12-15 TIL

4 minute read

Bean Container + DI = IoC Container

프로퍼티 설정 = 셋터 호출

<bean id="c1" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model"><value type="java.lang.String">티코1</value></property>
        <property name="maker"><value type="java.lang.String">비트자동차</value></property>
        <property name="cc"><value type="int">890</value></property>
</bean>
<!-- 위의 설정의 자바 코드로 표현:  
Car c1 = new Car();
        c1.setModel("티코");
        c1.setMaker("비트자동차");
        c1.setCc(Integer.parseInt("890")); <== 문자열을 primitive type으로 자동 변환한다.
        
				객체 풀이 있다면 c1 이라는 이름으로 저장한다.
        objPool.put("c1", c1);
-->
  • 프로퍼티 타입을 생략하면 IoC 컨테이너가 셋터 메소드에 맞춰서 자동으로 형변환한다.
    • 그러므로 타입을 굳이 지정하지 않아도 된다.
<!-- 이렇게 하는 것이 제일 깔끔하다 -->
		<bean id="c3" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="티코3"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
    </bean>

<!-- p:프로퍼티명="값"  -->
    <bean id="c4" class="com.eomcs.spring.ioc.ex04.Car"
          p:model="티코4" p:maker="비트자동차" p:cc="890"/>  
		</beans>

xml 네임스페이스와 xml 스키마

자동형변환

  • 프리머티브 타입만 가능
  <!-- 프로퍼티 설정하기 = 셋터 호출하기 -->
    
    <bean id="c1" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="티코3"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
    </bean>
    <!-- 위의 설정의 자바 코드로 표현:  
        Car c1 = new Car();
        c1.setModel("티코");
        c1.setMaker("비트자동차");
        c1.setCc(Integer.parseInt("890"));
        
        objPool.put("c1", c1);
    -->
    
    <!-- 문자열을 프로퍼티의 타입으로 형변환 할 수 없다면 예외가 발생한다. 
    -->
    <bean id="c2" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="티코3"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="aaa"/>
    </bean>
    <!-- 위의 설정의 자바 코드로 표현:  
        Car c1 = new Car();
        c1.setModel("티코");
        c1.setMaker("비트자동차");
        c1.setCc(Integer.parseInt("aaa"));
        
        objPool.put("c1", c1);
    -->
          
</beans>
public class Exam01 {

  public static void main(String[] args) {
    ApplicationContext iocContainer =
        new ClassPathXmlApplicationContext("com/eomcs/spring/ioc/ex04/b/application-context.xml");

    // 프로퍼티의 타입이 int 일 경우 XML에 작성한 문자열이
    // 자동으로 int 값으로 형변환된다.
    // 만약 형변환할 수 없다면 예외가 발생한다.
    // 자동 형변환은 primitive type에 대해서만 가능하다.
    // 그 외의 타입에 대해서는 문자열을 자동 형변환하지 않는다.
    // 형변환하고 싶으면 개발자가 형변환시키는 클래스를 만들어
    // 스프링 프레임워크에 등록해야 한다.
  }
  
}
Car() 생성자 호출됨!
setModel() 호출됨!
setMaker() 호출됨!
setCc() 호출됨!
Car() 생성자 호출됨!
12월 15, 2020 11:06:09 오전 org.springframework.context.support.AbstractApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'c2' defined in class path resource [com/eomcs/spring/ioc/ex04/b/application-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'cc'; nested exception is java.lang.NumberFormatException: For input string: "aaa"
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'c2' defined in class path resource [com/eomcs/spring/ioc/ex04/b/application-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'cc'; nested exception is java.lang.NumberFormatException: For input string: "aaa"
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:617)

의존 객체 주입

  <!-- 의존 객체 주입하기 -->
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex04.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="8"/>
    </bean>
    <!-- 
      Engine e1 = new Engine();
      e1.setMaker("비트자동차");
      e1.setValve(Integer.parseInt("16"));
      e1.setCylinder(Integer.parseInt("8"));
     -->
    
    <bean id="e2" class="com.eomcs.spring.ioc.ex04.Engine">
        <property name="maker" value="캠프자동차"/>
        <property name="valve" value="8"/>
        <property name="cylinder" value="4"/>
    </bean>
    <!-- 
      Engine e2 = new Engine();
      e1.setMaker("캠프자동차");
      e1.setValve(Integer.parseInt("8"));
      e1.setCylinder(Integer.parseInt("4"));
     -->
    
    <bean id="c1" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="티코A"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <!-- 의존 객체 설정하기 
             ref="객체이름"
        -->
        <property name="engine" ref="e1"/>
    </bean>
    <!-- 
      Car c1 = new Car();
      c1.setModel("티코A");
      c1.setMaker("비트자동차");
      c1.setCc(Integer.parseInt("890"));
      c1.setEngine(e1);
     -->
     
    <!-- p 속성으로 프로퍼티에 객체를 주입할 때는 
         p:프로퍼티명-ref="객체이름" -->
    <bean id="c2" class="com.eomcs.spring.ioc.ex04.Car"
          p:model="티코" 
          p:maker="비트자동차" 
          p:cc="890" 
          p:engine-ref="e2"/>
          
</beans>

프로퍼티 호출 - 객체 주입 시 객체 생성 순서

  • 프로퍼티 값을 주입할 때
    • 의존 객체가 생성되지 않은 상태라면 먼저 의존 객체를 생성한 후 프로퍼티 값을 주입한다.
    • 의존 객체가 생성된 상태라면 그대로 프로퍼티 값을 주입한다.
  • 즉 작성자가 순서를 고려해서 xml 파일을 작성하지 않아도 된다.

팩토리 메서드

  • c1이라는 이름으로 저장되는 객체는 carFactory의 create 메서드가 리턴한 값(객체)이다.
    • Factory-method=”create”
      • create는 스태틱 메서드여야 한다.
  • Constructor-arg value 는 create에 넘겨주는 값

factory Bean 구현체

  • 클래스 이름이 factoryBean으로 끝나면 그 클래스가 factoryBean 구현체임을 알 수 있다.
  • 이때 c1에 저장되는 것은 factoryBean의 메서드의 리턴값이다.

프로퍼티 에디터

  • 프로퍼티 값이 primitive 타입인 경우 자동으로 형변환 할 수 있다.
    • 자동으로 형변환 되지 않는 것들은 예외를 띄운다.
  • 하지만 그 외의 타입은 자동으로 바꾸지 않기 때문에 개발자가 프로퍼티 에디터를 만들어 등록해야 한다.

의존객체 주입 자동화

  • 스프링 IoC 컨테이너는 객체를 만든다.
  • 프로퍼티 값을 설정한다.
  • 객체 생성 후 IoC 컨테이너에 등록된 리스너(BeanPostProcessor)에게 통보한다.
  • AutowiredAnnotationBeanPostProcessor 리스너가 있다면
  • @Autowired 애노테이션을 처리한다.
  • 리스너를 등록할 때는 id를 주지 않아도 된다.

  • 리스너는 순서에 상관없이 가장 맨 처음 생성된다.

    • 원래는 xml의 순서대로 생성된다.
  • 의존 객체 주입 방법 3

    • 생성자에서 주입 = public Car(Engin engin) {this.engin = engin}
      • 생성자의 파라미터로 선언하면 해당 의존 객체가 필수 항목이 되기 때문에 그 의존 객체 없이 생성자를 호출할 수 없다.
      • 기본 생성자가 없으면 파라미터를 받는 다른 생성자를 호출하여 의존 객체를 자동 주입하려면 AutowiredAnnotationBeanPostProcessor 를 xml 파일에 등록해야 한다.
    • 필드에 선언 = @Autowired private Engin engin;
    • 세터 메서드에 선언 = 세터 메서드에 @Autowired 를 붙인다.
  • 의존 객체를 주입하는 것은 필수 사항이나 선택 사항으로 변경할 수도 있다.

    • @Autowired(required = false)
  • 여러 개의 의존 객체 중에서 한개를 선택할 수도 있다.

    • @Qualifier(“e2”)
    • @Qualifier 애노테이션을 처리할 BeanPostProcessor를 등록해야 한다.
  • Duplicated 에러 -> xml의 xsi:shemaLacation의 주소 중에 xsd의 주소가 https여야한다.

  • 애노테이션과 관련된 객체를 생성하라고 하는 단축 명령어

  • @Autowired와 @Qualifier를 합친 것이 @Resource이다.

    • jcp 자바 커뮤니티 기구(자바 표준에 관여하는 기구)에서 정의했다.
    • @Resource를 자바 기본으로 추가하지 않고 확장 라이브러리로 만들어놨다.

애노테이션 사용법

component

  • 클래스 이름의 첫번째 알파벳을 소문자로 바꾼 이름을 사용해서 객체를 자동 생성한다.
    • 자동 생성하는 것을 등록해야 한다.
  • 의존 객체는 생성자에서 주입하는 것이 좋다.
    • Spring setter constructor injection
    • 하지만 개발자가 더 편한 방법을 사용하는 것이 좋다.
  • Component-scan 태그를 추가하면 내부적ㅇ으로 annotation-config 태그가 자동으로 추가된다.
  • 특정 패키지의 특정 클래스를 객체 생성 대상에서 제외하기
  <context:component-scan base-package="com.eomcs.spring.ioc.ex09">
        <!-- 다음 패키지의 클래스 중에서 @Component,@Service,@Controller,@Repository
             애노테이션이 붙은 것은 객체를 생성한다. -->
        <context:include-filter type="regex" 
            expression="com.eomcs.spring.ioc.ex09.p2.Service2"/>
        
        <!-- 특정 패키지의 특정 클래스를 객체 생성 대상에서 제외하기  -->
        <context:exclude-filter type="regex" 
            expression="com.eomcs.spring.ioc.ex09.p2.Service1"/>
        
        <!-- 특정 애노테이션이 붙은 클래스는 객체 생성에서 제외시킨다. -->
        <context:exclude-filter type="annotation" 
            expression="org.springframework.stereotype.Controller"/>
            
        <!-- 특정 패키지만 제외하기 -->
        <context:exclude-filter type="regex" 
            expression="com.eomcs.spring.ioc.ex09.p4.*"/>
            
        <!-- 특정 패키지에서 지정된 패턴의 이름을 가진 클래스를 제외하기 -->
        <context:exclude-filter type="regex" 
            expression="com.eomcs.spring.ioc.ex09.p5.*Truck"/>
    </context:component-scan>

@configuration

  • AppConfig 클래스가 스프링 설정 정보를 갖고 있는 클래스임을 선포한다!

    그러면 AnnotationConfigApplicationContext 에서

    이 클래스를 찾아 적절한 작업을 수행할 것이다.

    => AnnotationConfigApplicationContext 컨테이너에

    Java config 클래스를 직접 지정할 경우에는

    굳이 @Configuration 애노테이션을 붙일 필요가 없다.

    예) ApplicationContext iocContainer =

    new AnnotationConfigApplicationContext(AppConfig1.class);

    => 그런데 다음과 같이 컨테이너에

    Java config 클래스를 직접 알려주지 않을 경우에는,

    예) ApplicationContext iocContainer =

    new AnnotationConfigApplicationContext(“com.eomcs.spring.ioc.ex10”);

    이 클래스가 Java config 클래스임을 표시해야만 컨테이너가 안다.

    Java config 클래스임을 표시할 때 붙이는 애노테이션이

    바로 @Configuration 이다.

Categories:

Updated: