Day88 Java프로그래밍 기초(Spring IOC)
Spring IoC
1. property 설정
1) Property
- Property는 Setter 및 Getter의 이름이다.
- Spring IoC는 property Editor가 내장되어 있어, String과 Primitive Type을 자동 형 변환을 한다.
- Editor에 등록되어 있지 않으면 CustumEditor에 등록해야한다.
2) 자동형변환
Sprint IoC는 Primitive을 자동 형변환 할 수 있다.
1 2 3 4 5 6 7 8
<bean id="varName" class="FQname"> <property name="StringName" value="StringValue"/> <property name="IntName" value="IntegerValue"/> <property name="BooleanName" value="BooleanValue"/> <!-- Date은 자동 형변환을 할 수 없다. <property name="DateName" value="DateValue"/> --> </bean>
Factory Menthod로 객체를 변환하여 대입해야한다.
1 2 3 4 5 6 7 8 9 10
<bean id="varName" class="FQname"> <property name="StringName" value="StringValue"/> <property name="IntName" value="IntegerValue"/> <property name="BooleanName" value="BooleanValue"/> <property> <bean class="java.sql.Date" factory-method="valueOf"> <constructor-arg value="2024-10-04"/> </bean> </property> </bean>
3) CustomEdit
java.beans.PropertyEditor
인터페이스를 상속받은PropertyEditorSupport
를 상속받아 구현한다.1 2 3 4 5 6 7 8 9 10 11
public class CustomDateEditor extends PropertyEditorSupport { // 이 메서드는 스프링 IoC 컨테이너가 String 타입의 프로퍼티 값을 // 다른 타입의 값으로 바꿀 때 호출하는 메서드이다. @Override public void setAsText(String text) throws IllegalArgumentException { // 파라미터로 넘어온 String 타입의 프로퍼티 값을 // 원하는 타입(java.sql.Date)의 값으로 바꿔 내부에 저장한다. // => 그러면 스프링 IoC 컨테이너를 이 값을 꺼내서 객체에 주입할 것이다. this.setValue(Date.valueOf(text)); } }
CustomDateEditor 적용하면 Spring IoC에서 자동으로 객체를 생성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<bean id="varName" class="FQname"> <property name="StringName" value="StringValue"/> <property name="IntName" value="IntegerValue"/> <property name="BooleanName" value="BooleanValue"/> <property name="DateName" value="DateValue"/> </bean> <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <!-- 프로퍼티 에디터를 설정하는 방법 key: String 값을 어떤 타입의 값으로 바꿀 것인지에 대한 타입 이름이다. value: 커스텀 에디터(프로퍼티 값 변환기) 클래스 이름이다. 의미? => 스프링 IoC 컨테이너가 프로퍼티 값을 설정할 때 특히 String 값을 java.sql.Date 객체로 바꿔야 할 때 이 클래스를 사용하여 값을 바꾸라는 뜻이다.--> <entry key="java.sql.Date" value="com.eomcs.spring.ioc.ex07.c.CustomDateEditor"/> </map> </property> </bean>
2. annotation-config
1) 객체주입
의존객체를 주입하는 하기위해서는 bean메서드를 통해 객체를 생성하고 주입한다.
1 2 3 4 5 6 7 8
<bean id="varName" class="FQname"> <property name="propertyName" value="propertyValue"/> <property name="propertyName" ref="refName"/> </bean> <bean id="refName" class="FQname"> <property name="propertyName" value="propertyValue"/> </bean>
2) 객체 자동 주입
- @Autowired 애노테이션을 사용하면, 자동으로 객체를 주입한다.
- Spring IoC컨테이너가 설정 파일에 적혀 있는 대로 객체를 생성한다.
AutowiredAnnotationBeanPostProcessor는 생성된 객체에 대해 @Autowired 애노테이션을 검사하여 이 애노테이션이 붙은 프로퍼티 값을 자동 주입하는 일을 한다.
1 2 3 4
@Autowired public setPropertyName2(DIname DIname){ this.DIname = DIname; }
- xml에
BeanPostProcessor
를 설정한다.1 2 3 4 5 6 7 8 9 10
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/> <bean id="varName" class="FQname"> <property name="propertyName1" value="propertyValue"/> <property name="propertyName2" ref="refName"/> </bean> <bean id="refName" class="DIname"> <property name="propertyName3" value="propertyValue"/> </bean>
3) BeanPostProcessor 구현체
- 새 기능이 IoC 컨테이너가 생성한 객체를 사용해야 한다면, 객체 생성 후에 그 작업을 수행하면 된다.
빈 생성 후에 어떤 작업을 수행할 객체를 만들고 싶다면 BeanPostProcessor 규칙에 따라 클래스를 만들면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization( Object bean, String beanName) throws BeansException { // XML 설정에서 init-method 속성에 지정된 메서드가 호출되기 전에 // 이 메서드가 먼저 호출된다. System.out.println("postProcessBeforeInitialization()"); System.out.printf(" => %s : %s\n", // beanName, // bean.getClass().getName()); System.out.printf(" => %s\n", bean.toString()); return bean; } @Override public Object postProcessAfterInitialization( Object bean, String beanName) throws BeansException { // XML 설정에서 init-method 속성에 지정된 메서드가 호출된 후에 // 이 메서드가 호출된다. System.out.println("postProcessAfterInitialization()"); System.out.printf(" => %s : %s\n", // beanName, // bean.getClass().getName()); System.out.printf(" => %s\n", bean.toString()); return bean; } }
xml에서
MyBeanPostProcessor
를 등록하고 새용하면 된다.1
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
4) Autowired 위치
- @Autowired 애노테이션을 필드(인스턴스 변수)에 붙여도 된다.
- 의존 객체를 직접 변수에 주입한다.
- -인스턴스 변수에 직접 의존 객체를 주입한다는 것은 캡슐화(즉 외부에서 직접 인스턴스 변수에 접근하는 것을 막는 기법)를 위배하는 측면이 있기 때문에 이 방식은 “객체지향을 파괴하는 방식”이라는 비난을 받는다.
5) 생성자 주입
- 생성자를 통해 의존 객체를 주입할 수 있다.
- AutowiredAnnotationBeanPostProcessor가 이것 또한 처리해준다.
해당 클래스에 기본 생성자가 없을 때, 파라미터를 받는 다른 생성자를 찾아 호출한다.
1 2 3
public modifier(DIclass dIclass) { this.dIclass = dIclass; }
6) Autowired 옵션
- @Autowired의 required 값은 기본이 true이다.
@Autowired(required = false)
이면 해당 객체가 없으면 null을 반환한다.
7) Qualifier 사용법
- 의존 객체가 여러 개 있을 경우, 주입할 의존 객체의 이름을 지정해야한다.
@Qualifier("varName")
을 통해 주입할 객체을 정할 수 있다.1 2
@Autowired @Qualifier("varName")
8) @Resource 사용법
@Resource
는 스프링 프레임워크가 아닌 자바에서 제공한다.@Autowired + @Qualifier = @Resource의 기능이다.
1
@Resource("varName")
3. context 활용
- xml에서 context를 활용하면, 별도의 bean태그를 사용하지 않아도 기본적인 객체를 생성할 수 있다.
- 루트 bean에서
xmlns:context="http://www.springframework.org/schema/context"
로 네임스페이스를 지정해야한다. - schemaLocation은
"http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
이다
1) context:annotation-config
<context:annotation-config/>
은 아래 코드를 대신 생성해 준다.1 2 3 4
<bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor"/> <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/> <bean class="org.springframework.context.event.EventListenerMethodProcessor"/> <bean class="org.springframework.context.event.DefaultEventListenerFactory"/>
2) context:component-scan
- component-scan 태그는 지정된 패키지에서 @Component, @Service, @Repository, @Controller 애노테이션이 붙은 클래스를 찾아서 객체를 자동 생성하도록 명령을 내리는 태그이다.
- base-package 속성은 어느 패키지의 있는 클래스를 찾아서 등록할 것인지 지정하는 속성이다.
- @Component : 일반 클래스에 대해 붙인다.
- @Repository : DAO 역할을 수행하는 클래스에 대해 붙인다.
- @Service : 비즈니스 로직을 수행하는 클래스에 대해 붙인다.
- @Controller : MVC 구조에서 컨트롤러 역할을 하는 클래스에 대해 붙인다.
- @RestController : MVC 구조에서 REST API 컨트롤러 역할을 하는 클래스에 대해 붙인다.
- component-scan 태그를 추가하면 내부적으로 annotation-config 태그가 자동으로 추가된다.
3) context:component-scan 활용
- include는 특정 타입의 클래스를 포함해서 생성한다.
exclude는 특정 타입의 클래스를 제외하고 생성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<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>
4. AppConfig 사용
1) @Bean
- Spring에서는 xml의 bean태그 처럼 수동으로 메서드를 생성할 수 있다.
AppConfig.java를 활용한 경우 @Bean 에노테이션을 붙인다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
//AppConfig.java @Bean("varName") // 애노케이션에 지정한 이름으로 리턴 값을 보관한다. public Class var() { Class c = new Class(); c.setProperty("var"); return c; } @Bean // 이름을 지정하지 않으면 메서드 이름을 사용하여 저장한다. public Class varName() { Class c = new Car(); c.setProperty("var"); return c; } //test.java public static void main(String[] args) { ApplicationContext iocContainer = new AnnotationConfigApplicationContext(AppConfig.class); }
2) Configuration
- AnnotationConfigApplicationContext에서 파라미터에 클래스 패키지명 사용하면 AppConfig에 @Configuration을 붙여야 Spring에서 인식을 한다.
패키지명을 사용하면, 해당 패키지의 @Component, @Service, @Controller, @Repository의 객체도 자동 생성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
//AppConfig.java public class AppConfig{ @Bean public Class var(){ Class c = new Class(); c.setProperty("var"); return c; } } //A.java @Component public class A { } //test.java public static void main(String[] args) { ApplicationContext iocContainer = new AnnotationConfigApplicationContext("packageName"); } // a = packageName.A // varName = packageName.Class
3) @PropertySource
.properties
파일을 데이터를 메모리에 로딩하는 일을 한다.@PropertySource({classpath:path/fileName.properties})
로 설정한다.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
@Configuration @PropertySource({ "classpath:com/eomcs/spring/ioc/ex10/c/jdbc.properties", "classpath:com/eomcs/spring/ioc/ex10/c/jdbc2.properties" }) public class AppConfig { // @PropertySource를 통해 로딩된 프로퍼티 값을 사용하고 싶다면 // Environment 타입의 객체를 주입 받아라! @Autowired Environment env; // Spring IoC 컨테이너는 Environment 객체를 주입해 준다. // @PropertySource를 통해 로딩된 프로퍼티 값 중에서 // 특정 값만 필드로 주입 받을 수 있다. // => 필드 선언에 @Value 애노테이션을 붙인다. // => @Value("${프로퍼티이름}") @Value("${jdbc.url}") String jdbcUrl; @Value("${jdbc2.url}") String jdbc2Url; // Environment 객체를 통해 메모리에 보관된 .properties 의 값 가져오기 @Bean public Car car1() { System.out.println("car1() 호출: "); System.out.println(" " + this.env.getProperty("jdbc.username")); System.out.println(" " + this.env.getProperty("jdbc2.username")); return new Car(null); } // @Value를 통해 필드로 주입 받은 .properties 값 꺼내기 @Bean public Car car2() { System.out.println("car2() 호출: "); System.out.println(" " + this.jdbcUrl); System.out.println(" " + this.jdbc2Url); return new Car(null); } // @Value를 사용하여 파라미터로 .properties 값 주입 받기 @Bean public Car car3( @Value("${jdbc.username}") String username, @Value("${jdbc2.username}") String username2) { System.out.println("car3() 호출 :"); System.out.println(" " + username); System.out.println(" " + username2); return new Car(null); } }
4) @CompnetnScan
-
태그와 같은 일을 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// @ComponentScan // => <context:component-scan/> 태그와 같은 일을 한다. // // 사용법1: // => 한 개의 패키지를 지정하기 @ComponentScan(basePackages = {"com.eomcs.spring.ioc.ex11"}) // => 배열 항목이 한 개일 경우 중괄호({}) 생략 가능 @ComponentScan(basePackages = "com.eomcs.spring.ioc.ex11") // 사용법2: // => 여러 개의 패키지 지정하기 @ComponentScan(basePackages = { "com.eomcs.spring.ioc.ex11.p1", "com.eomcs.spring.ioc.ex11.p2", "com.eomcs.spring.ioc.ex11.p3" }) // 사용법3: // => 특정 패키지나 클래스 제외하기 @ComponentScan( basePackages = "com.eomcs.spring.ioc.ex11", excludeFilters = { @ComponentScan.Filter( type = FilterType.REGEX, pattern = "com.eomcs.spring.ioc.ex11.p2.*" ), @ComponentScan.Filter( type = FilterType.ANNOTATION, value = org.springframework.stereotype.Controller.class ) })