2020-12-15 TIL
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는 스태틱 메서드여야 한다.
- Factory-method=”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 를 붙인다.
- 생성자에서 주입 = public Car(Engin engin) {this.engin = engin}
-
의존 객체를 주입하는 것은 필수 사항이나 선택 사항으로 변경할 수도 있다.
- @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 이다.