Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
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
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[Java] 예제 소스 정리 - Spring IoC 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/Spring Framework, Spring Boot

[Java] 예제 소스 정리 - Spring IoC

끈기JK 2023. 3. 1. 12:29

com.eomcs.spring.ioc

 

 

예제 소스 정리

 

 

spring ioc

 

 

com.eomcs.spring.ioc.ex01

 

 

스프링 IoC 컨테이너 사용
package com.eomcs.spring.ioc.ex01.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {
  public static void main(String[] args) {
    // IoC(Inversion Of Control) 컨테이너
    // => bean container 라고도 부른다.
    // => 인스턴스의 생성과 관리를 담당한다.
    // => 각 객체가 의존하는 객체(dependency)를 자동으로 주입해 준다.
    //    "의존 객체 주입(dependency injection; DI)"이라 부른다.
    //    그래서 "DI 컨테이너"라고도 부른다.
    //
    // Spring IoC 컨테이너
    // => spring.io 사이트에서 제공하는 프레임워크이다.
    // => 프로젝트에 Spring IoC 컨테이너 포함하기
    //    mvnrepository.com 또는 search.maven.org에서
    //    spring-context 로 라이브러리를 검색한다.
    // => build.gradle 에 의존 라이브러리 정보를 추가한다.
    // => '$ gradle eclipse'를 실행하여 라이브러리 파일을 다운로드 받고
    //    이클립스 설정 파일을 갱신한다.
    // => 이클립스에서 프로젝트 정보를 갱신한다.
    //
    // ApplicationContext 인터페이스
    // => 스프링 IoC 컨테이너의 사용 규칙을 정의한 인터페이스이다.
    // => 모든 스프링 IoC 컨테이너는 이 규칙에 따라 정의되어 있다.
    //
    // ApplicationContext 구현체(implements, 인터페이스를 구현한 클래스 또는 그 클래스의 인스턴스)의 종류
    // 1) XML 파일에서 설정 정보를 읽어들이는 IoC 컨테이너
    // - ClassPathXmlApplicationContext : 설정 파일을 자바 CLASSPATH 경로에서 찾는다.
    // - FileSystemXmlApplicationContext : 설정 파일을 OS 경로에서 찾는다.
    // 2) 자바 클래스 파일의 애노테이션에서 설정 정보를 읽어 들이는 IoC 컨테이너
    // - AnnotationConfigApplicationContext : 설정 정보를 자바 클래스에서 찾는다.
    //

    // 1) 자바 CLASSPATH 에서 설정 파일을 찾는 IoC 컨테이너
    // => 자바 CLASSPATH?
    //    1) JVM을 실행할 때 -classpath 옵션으로 지정한 경로
    //       예) $java -class c:\a\b;d:\xxx\yyy\zzz;c:\bb\x.jar Hello
    //    2) 만약 -classpath 경로가 지정되어 있지 않으면,
    //       OS에서 CLASSPATH 환경 변수로 등록한 경로
    //
    // => 파일 경로이기 때문에 패키지와 패키지 사이에는 . 대신에 /를 사용해야 한다.
    // 
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(
        "com/eomcs/spring/ioc/ex01/a/application-context.xml");

    System.out.println("실행 완료!");
  }
}

application-context.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 
        https://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <!-- 여기에 Spring Framework의 설정 정보를 기술한다. -->

</beans>

 

 

스프링 IoC 컨테이너 사용
package com.eomcs.spring.ioc.ex01.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Exam01 {
  public static void main(String[] args) {
    // 2) 운영체제의 파일 시스템에서 설정 파일을 찾는 IoC 컨테이너
    // => 자바 classpath가 아닌 다른 폴더에 설정파일이 있을 경우 사용한다.
    // => 설정 파일 경로를 지정할 때 파일 시스템 경로를 지정해야 한다.
    // => 단, URL 형식으로 지정해야 한다.
    // => 예) file://설정파일경로
    // => URL 형식에서는 파일 시스템을 가리킬 때 접두어 'file://'를 붙인다.
    //
    ApplicationContext iocContainer = new FileSystemXmlApplicationContext(
        "file:///c:/Users/bitcamp/git/eomcs-java/eomcs-java-lang/app/bin/main/com/eomcs/spring/ioc/ex01/b/application-context.xml");

    System.out.println("실행 완료!");
  }
}

application-context.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 
      https://www.springframework.org/schema/beans/spring-beans.xsd">
  
</beans>

 

 

스프링 IoC 컨테이너 사용
package com.eomcs.spring.ioc.ex01.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Exam01 {
  public static void main(String[] args) {
    // 3) 자바 클래스 파일의 애노테이션으로부터 설정 정보를 추출한다.
    // => 자바 클래스로 설정 정보를 다루는 것을 'Java Config' 라 부른다.
    // => 생성자 파라미터로 Java Config 클래스의 타입 정보를 넘긴다.
    ApplicationContext iocContainer = new AnnotationConfigApplicationContext(
        AppConfig.class);

    System.out.println("실행 완료!");
  }
}
package com.eomcs.spring.ioc.ex01.c;

// 클래스 선언부에 애노테이션으로 스프링 설정에 관한 정보를 지정할 수 있다.
public class AppConfig {

  // 필드나 메서드로 스프링 관련 설정을 수행할 수 있다.
}

 

 

IoC 컨테이너에 보관된 객체를 확인하기
package com.eomcs.spring.ioc.ex01.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

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

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    System.out.println("실행 완료!");
  }
}

application-context.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 
      https://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <!-- 여기에 Spring Framework의 설정 정보를 기술한다. -->
  
</beans>

 

IoC 컨테이너에 보관된 객체를 확인하기
package com.eomcs.spring.ioc.ex01.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam02 {
  public static void main(String[] args) {
    ApplicationContext iocContainer = new AnnotationConfigApplicationContext(
        AppConfig.class);

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    // AnnotationConfigApplicationContext는
    // 애노테이션 처리에 필요한 도구를 기본 포함하고 있다.
    // 또한 Java Config 클래스(AppConfig)의 객체도 포함한다.


    System.out.println("실행 완료!");
  }
}
package com.eomcs.spring.ioc.ex01.d;

// 클래스 선언부에 애노테이션으로 스프링 설정에 관한 정보를 지정할 수 있다.
public class AppConfig {

  // 필드나 메서드로 스프링 관련 설정을 수행할 수 있다.
}

 

 

IoC 컨테이너에 객체를 보관하기
package com.eomcs.spring.ioc.ex01.e;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

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

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    System.out.println("실행 완료!");
  }
}

application-context.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 
      https://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <!-- 객체 생성 -->
  <bean id="c1" class="com.eomcs.spring.ioc.ex01.Car"/>
  
</beans>

 

IoC 컨테이너에 객체를 보관하기
package com.eomcs.spring.ioc.ex01.e;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam02 {
  public static void main(String[] args) {
    ApplicationContext iocContainer = new AnnotationConfigApplicationContext(
        AppConfig.class);

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    System.out.println("실행 완료!");
  }
}
package com.eomcs.spring.ioc.ex01.e;

import org.springframework.context.annotation.Bean;
import com.eomcs.spring.ioc.ex01.Car;

// 클래스 선언부에 애노테이션으로 스프링 설정에 관한 정보를 지정할 수 있다.
public class AppConfig {

  // 객체 생성
  @Bean 
  public Car c1() {
    return new Car();
  }
}

 

package com.eomcs.spring.ioc.ex01;

//@Component
public class Car {
  String model;
  String maker;
  int cc;

  public Car() {
    System.out.println("Car() 생성자 호출!");
  }

/* 게터, 세터 생략 */

 

 

패키지를 탐색하여 빈을 자동 생성하기
package com.eomcs.spring.ioc.ex01.f;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

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

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);


    System.out.println("실행 완료!");
  }
}

application-context.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 
    https://www.springframework.org/schema/beans/spring-beans.xsd 
    http://www.springframework.org/schema/context 
    https://www.springframework.org/schema/context/spring-context.xsd">
      
  <!-- 클래스를 찾을 패키지 지정한다.
       그러면 IoC 컨테이너는 @Component 등의 애노테이션이 붙은 클래스를 찾아 
       인스턴스를 생성하여 보관한다. -->
  <context:component-scan base-package="com.eomcs.spring.ioc.ex01"/>

</beans>

 

IoC 컨테이너에 객체를 보관하기 II
package com.eomcs.spring.ioc.ex01.f;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam02 {
  public static void main(String[] args) {
    ApplicationContext iocContainer = new AnnotationConfigApplicationContext(
        AppConfig.class);

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    // 애노테이션을 처리하는 기본 도구 외에
    // AppConfig에서 설정한 객체(Car)도 포함하고 있다.

    System.out.println("실행 완료!");
  }
}
package com.eomcs.spring.ioc.ex01.f;

import org.springframework.context.annotation.ComponentScan;

// 객체를 생성할 패키지 지정
@ComponentScan("com.eomcs.spring.ioc.ex01")
public class AppConfig {

}

 

package com.eomcs.spring.ioc.ex01;

import org.springframework.stereotype.Component;

@Component
public class Engine {
  String maker;
  int valve;
  int cylinder;

  public Engine() {
    System.out.println("Engine() 생성자 호출됨!");
  }

/* 게터, 세터 생략 */

 

 

IoC 컨테이너에서 객체 꺼내기
package com.eomcs.spring.ioc.ex01.g;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex01.Car;

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

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    // 1) 객체 이름으로 꺼내기
    System.out.println(iocContainer.getBean("c1"));

    // 2) 객체 타입으로 꺼내기
    System.out.println(iocContainer.getBean(Car.class));

    System.out.println("실행 완료!");
  }
}

application-context.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 
      https://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <!-- 객체 생성 -->
  <bean id="c1" class="com.eomcs.spring.ioc.ex01.Car"/>
  
</beans>

 

IoC 컨테이너에서 객체 꺼내기 II - 예외 발생
package com.eomcs.spring.ioc.ex01.g;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

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

    // 현재 IoC 컨테이너에 들어 있는 객체를 출력해 보자.
    SpringUtils.printBeanList(iocContainer);

    // 존재하지 않는 객체 꺼내기
    System.out.println(iocContainer.getBean("c2"));

    // 해당 이름의 객체가 들어 있지 않다면,
    // => null을 리턴하는 것이 아니라 예외가 발생한다.

    System.out.println("실행 완료!");
  }
}

 

 

com.eomcs.spring.ioc.ex02

 

 

객체 생성 - <bean> 태그 사용법
package com.eomcs.spring.ioc.ex02.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);
  }
}

application-context.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">
    
    <!-- IoC 컨테이너가 생성해야 할 객체에 대한 정보 설정하기 
         => IoC 컨테이너는 다음 태그를 보고 설정된 정보에 따라 객체를 생성한다.
    -->
    
    <!-- id: 빈의 이름. 생성된 객체를 컨테이너에 보관할 때 사용할 key.
         class: 빈의 클래스명. 생성할 객체의 클래스 정보. 
         반드시 fully-qualified class name(FQName, QName) 이어야 한다.
         =>  Spring IoC 컨테이너는 기본 생성자를 호출하여 객체를 생성한다.
    -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex02.Car"></bean>
    
    <!-- 시작 태그와 끝 태그 사이에 내용이 없다면 끝 태그를 생략할 수 있다.
         단 생략을 표시하기 위해 반드시 시작태그의 끝에 /을 붙여라 -->
    <bean id="c2" class="com.eomcs.spring.ioc.ex02.Car"/>     

</beans>

 

객체 생성 - <bean> 태그 사용법
package com.eomcs.spring.ioc.ex02.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex02.Car;

public class Exam02 {

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

    SpringUtils.printBeanList(iocContainer);

    Car obj1 = (Car) iocContainer.getBean("c1");
    Car obj2 = (Car) iocContainer.getBean("c2");

    System.out.println(obj1 == obj2);

    //    Car obj3 = iocContainer.getBean(Car.class);  // 같은 타입의 객체가 여러 개일 경우 예외 발생!
  }

}

 

 

객체 생성 - bean의 이름을 지정하는 다양한 방법
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);
  }

}

application-context.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">
    
    <!-- 빈의 이름을 지정하는 다양한 방법 -->
    
    <!-- id: 빈의 이름 -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex02.Car"/>
    
    <!-- id 전체가 하나의 문자열로 취급된다.  
       즉 "c11 c12 c13" 문자열이 객체 아이디로 사용된다.-->
    <bean id="c11 c12 c13" class="com.eomcs.spring.ioc.ex02.Car"/>
  
    <!-- name: 빈의 별명 -->
    <bean id="c2" name="c3" class="com.eomcs.spring.ioc.ex02.Car"/>   
    
    <!-- id를 지정하지 않고 name만 지정하면 name이 id로 사용된다. -->
    <bean name="c4" class="com.eomcs.spring.ioc.ex02.Car"/>  
    
    <!-- name 속성에 여러 개의 별명을 지정할 수 있다. -->
    <bean id="c5" name="c51 c52 c53" class="com.eomcs.spring.ioc.ex02.Car"/>  
    <bean id="c6" name="c61,c62,c63" class="com.eomcs.spring.ioc.ex02.Car"/>  
    <bean id="c7" name="c71;c72;c73" class="com.eomcs.spring.ioc.ex02.Car"/> 
    
    <!-- name 속성에 여러 개의 별명을 입력할 때 공백, 콤마(,), 세미콜론(;)을 
         사용할 수 있다. 그 외에는 불가하다! -->
    <bean id="c8" name="c81:c82:c83" class="com.eomcs.spring.ioc.ex02.Car"/>  

    <!-- id 없이 name에 여러 개의 별명을 지정할 때는 그 중에서 첫 번째 별명이 
         id로 사용된다. -->
    <bean name="c91 c92 c93" class="com.eomcs.spring.ioc.ex02.Car"/>
    
    
</beans>

 

객체 생성 - bean의 별명을 알아내기
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam0210 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);

    String[] aliases = iocContainer.getAliases("c5");
    System.out.println("[별명]");
    for (String alias : aliases) {
      System.out.println(alias);
    }
  }

}

 

객체 생성 - bean의 별명을 알아내기: id만 설정한 경우
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam0220 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);

    // id 만 지정한 경우
    String[] aliases = iocContainer.getAliases("c1");
    System.out.println("[별명]");
    for (String alias : aliases) {
      System.out.println(alias);
    }
  }

}

 

객체 생성 - bean의 별명을 알아내기: name만 설정한 경우
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam0230 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);

    String[] aliases = iocContainer.getAliases("c4");
    System.out.println("[별명]");
    for (String alias : aliases) {
      System.out.println(alias);
    }
    // name 만 설정한 경우,
    // 첫 번째 별명이 id로 사용된다.
    // 두 번째 별명을 지정하지 않는다면 별명이 없는 것이다.
  }

}

 

객체 생성 - bean의 별명을 알아내기: 별명만 여러 개인 경우
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam0240 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);

    String[] aliases = iocContainer.getAliases("c91");
    System.out.println("[별명]");
    for (String alias : aliases) {
      System.out.println(alias);
    }
    // 첫 번째 별명이 id로 사용된다.
    // 나머지 별명이 별명으로 사용된다.
  }

}

 

객체 생성 - bean의 별명을 알아내기: 별명만 여러 개인 경우
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam0250 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);

    String[] aliases = iocContainer.getAliases("c8");
    System.out.println("[별명]");
    for (String alias : aliases) {
      System.out.println(alias);
    }
    // 여러 개의 별명을 지정할 때 공백( )/콤마(,)/세미콜론(;)을 사용하여
    // 별명을 구분할 수 있다.
    // 그 외의 문자는 구분자로 사용할 수 없다.
    // 그래서 "c8" 별명은 한 개 밖에 없다.
    // 왜? 콜론(:)은 구분자로 사용하지 않기 때문이다.
    // 그냥 일반 문자로 취급한다.
  }

}

 

객체 꺼내기 - 객체를 꺼낼 때는 ID나 별명 모두 사용할 수 있다.
package com.eomcs.spring.ioc.ex02.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam03 {

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

    // 빈의 id와 클래스명을 출력하기
    SpringUtils.printBeanList(iocContainer);

    System.out.println("-------------------------");

    System.out.println(iocContainer.getBean("c5")); // ID
    System.out.println(iocContainer.getBean("c51")); // 별명
  }

}

 

 

객체 생성 - 빈 생성 정책
package com.eomcs.spring.ioc.ex02.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // IoC 컨테이너가 생성될 때,
    // "singleton" 객체는 기본으로 생성된다.

    System.out.println("[빈 이름]");
    String[] names = iocContainer.getBeanDefinitionNames();
    for (String name : names) {
      System.out.println(name);
    }
    // => 객체가 생성되지 않았더라도 빈의 이름은 모두 등록되어 있다.
  }

}

application-context.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">
    
    <!-- 빈 생성 정책 
         scope 속성에 빈의 생성 정책을 지정할 수 있다.
         => singleton: 한 개의 객체만 생성. 지정하지 않으면 기본이 singleton이다.
         => prototype: getBean() 호출할 때마다 생성
         => request: (웹) 요청이 들어올 때마다 생성
         => session: (웹) 세션이 생성될 때마다 생성
         => application: (웹) 애플리케이션을 시작할 때 생성
         => websocket: (웹) 웹소켓이 연결될 때 생성
    -->
    
    <!-- scope 속성의 기본 값은 singleton -->
    <!-- singleton 객체는 IoC 컨테이너가 생성될 때 미리 준비된다. -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex02.Car" />
    <bean id="c2" class="com.eomcs.spring.ioc.ex02.Car" scope="singleton"/>
    
    <!-- prototype 객체는 getBean()을 호출할 때 생성된다. -->
    <bean id="c3" class="com.eomcs.spring.ioc.ex02.Car" scope="prototype"/>
</beans>

 

객체 생성 - 빈 생성 정책 : singleton 객체
package com.eomcs.spring.ioc.ex02.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.ex02.Car;

public class Exam02 {

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

    Car obj1 = (Car) iocContainer.getBean("c1");
    Car obj2 = (Car) iocContainer.getBean("c1");
    Car obj3 = (Car) iocContainer.getBean("c1");

    System.out.println(obj1 == obj2);
    System.out.println(obj1 == obj3);

    // singleton으로 설정된 객체는
    // 오직 한 개만 생성된다.
    // getBean()을 여러 번 호출하더라도 같은 객체를 리턴한다.

    Car obj10 = (Car) iocContainer.getBean("c2");
    Car obj20 = (Car) iocContainer.getBean("c2");
    Car obj30 = (Car) iocContainer.getBean("c2");

    System.out.println(obj10 == obj20);
    System.out.println(obj10 == obj30);
  }

}

 

객체 생성 - 빈 생성 정책 : prototype 객체
package com.eomcs.spring.ioc.ex02.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.ex02.Car;

public class Exam03 {

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

    System.out.println("--------------------------");
    Car obj1 = (Car) iocContainer.getBean("c3");
    System.out.println("--------------------------");
    Car obj2 = (Car) iocContainer.getBean("c3");
    System.out.println("--------------------------");
    Car obj3 = (Car) iocContainer.getBean("c3");
    System.out.println("--------------------------");

    System.out.println(obj1 == obj2);
    System.out.println(obj1 == obj3);
    System.out.println(obj2 == obj3);

    // prototype 객체
    // => getBean()을 호출할 때 마다 새 객체를 만들어 리턴한다.
    // => 특별한 경우가 아니면 이 방식을 사용하지 않는다.
    // => 왜? 객체가 계속 생성되기 때문에 가비지가 많이 발생할 수 있다.
    // => 그래서 IoC 컨테이너는 기본으로 singleton 방식을 사용한다.
  }

}

 

 

객체 생성 - 익명 객체의 이름
package com.eomcs.spring.ioc.ex02.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println("[빈 이름]");
    String[] names = iocContainer.getBeanDefinitionNames();
    for (String name : names) {
      System.out.println(name);
    }
    // 클래스마다 인덱스는 0부터 시작한다.
  }

}

application-context.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">
    
    <!-- 빈의 이름을 지정하지 않을 경우 
         => FQName과 인덱스 번호가 객체의 이름으로 사용된다.
         => FQName#인덱스번호
         => 예) com.eomcs.spring.ioc.ex02.Car#0
         => 익명 객체의 수만큼 인덱스 번호가 증가한다.
    -->
    
    <!-- 
      특히 0번 익명 객체의 별명은 클래스명과 같다.
      즉 com.eomcs.spring.ioc.ex02.Car#0 이름을 가진 익명 객체의 별명은 
         com.eomcs.spring.ioc.ex02.Car 이다.
      그외 익명 객체는 별명이 붙지 않는다.  -->
    <bean class="com.eomcs.spring.ioc.ex02.Car"/>
    <bean class="com.eomcs.spring.ioc.ex02.Car"/>
    <bean class="com.eomcs.spring.ioc.ex02.Car"/>
    <bean class="com.eomcs.spring.ioc.ex02.Car"/>
    
    <!-- 인덱스 번호는 클래스마다 0부터 시작한다. -->
    <bean class="com.eomcs.spring.ioc.ex02.Engine"/>
    <bean class="com.eomcs.spring.ioc.ex02.Engine"/>
    <bean class="com.eomcs.spring.ioc.ex02.Engine"/>
</beans>

 

객체 생성 - 익명 객체의 별명
package com.eomcs.spring.ioc.ex02.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam02 {

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

    SpringUtils.printBeanAliases(iocContainer,
        "com.eomcs.spring.ioc.ex02.Car#0");
    // 같은 클래스에 대해 첫 번째 익명 객체 만이 별명을 갖는다.
    System.out.println("------------------");

    SpringUtils.printBeanAliases(iocContainer,
        "com.eomcs.spring.ioc.ex02.Car#1");
    // 같은 클래스에 대해 두 번째 익명 객체부터는 별명이 없다.

  }

}

 

 

com.eomcs.spring.ioc.ex03

 

 

생성자 호출 확인
package com.eomcs.spring.ioc.ex03.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // SpringUtils.printBeanNames(iocContainer);
  }

}

application-context.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="c1" class="com.eomcs.spring.ioc.ex03.Car"/>
    
    <!-- 다른 생성자 호출하기 : 
      => 파라미터 값을 설정하면 그 값에 맞는 생성자가 선택되어 호출된다.  
      => <constructor-arg/> 엘리먼트를 사용하여 호출될 생성자를 지정할 수 있다.
      => 즉 생성자를 호출할 때 넘겨줄 값을 지정하면 
         스프링 IoC 컨테이너는 그 값을 받을 생성자를 찾아 호출한다. 
      => 파라미터의 개수가 같은 생성자가 여러 개 있을 경우 
         스프링 IoC 컨테이너는 내부의 정책에 따라 적절한 생성자를 선택한다.
         보통 String 타입이 우선이다.-->
    <bean id="c2" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg>
            <value>티코</value>
        </constructor-arg>
    </bean>
    
    <!-- 한 개의 파라미터 값을 받는 생성자가 여러 개 있을 경우,
         String 타입의 값을 받는 생성자가 우선하여 선택된다. 
         생성자를 정의한 순서는 상관없다.-->
    <bean id="c3" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg>
            <value>1980</value>
        </constructor-arg>
    </bean>
    
    <!-- 한 개의 파라미터를 가지는 생성자가 여러 개 있을 경우, 
         특정 생성자를 지정하고 싶다면 파라미터의 타입을 지정하라! -->
    <bean id="c4" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg>
            <value type="int">1980</value>
        </constructor-arg>
    </bean>
    
    <!-- 파라미터가 여러 개인 생성자를 호출할 경우 
         IoC 컨테이너가 가장 적합한 생성자를 찾아 호출한다. -->
    <bean id="c5" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg>
            <value type="java.lang.String">소나타</value>
        </constructor-arg>
        <constructor-arg>
            <value type="int">1980</value>
        </constructor-arg>
    </bean>
    <bean id="c6" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg>
            <value type="int">1980</value>
        </constructor-arg>
        <constructor-arg>
            <value type="java.lang.String">소나타</value>
        </constructor-arg>
    </bean>
    
    <!-- 파라미터의 값을 설정할 때 이름을 지정해도 
         개발자가 임의로 특정 생성자를 호출하게 제어할 수 없다.
         IoC 컨테이너가 판단하여 적절한 생성자를 호출한다. -->
    <bean id="c7" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg name="cc">
            <value type="int">1980</value>
        </constructor-arg>
        <constructor-arg name="model">
            <value type="java.lang.String">소나타</value>
        </constructor-arg>
    </bean>
    <bean id="c8" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg name="model">
            <value type="java.lang.String">소나타</value>
        </constructor-arg>
        <constructor-arg name="cc">
            <value type="int">1980</value>
        </constructor-arg>
    </bean>
    
    <!-- index 속성을 사용하여 파라미터 값이 들어가는 순서를 지정할 수 있다.
         즉 개발자가 어떤 생성자를 호출할 지 지정할 수 있다. -->
    <bean id="c9" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg index="0">
            <value type="java.lang.String">소나타</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value type="int">1980</value>
        </constructor-arg>
    </bean>
    <bean id="c10" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg index="1">
            <value type="java.lang.String">소나타</value>
        </constructor-arg>
        <constructor-arg index="0">
            <value type="int">1980</value>
        </constructor-arg>
    </bean>
    
    <!-- 기본 생성자가 없으면 예외 발생! -->
    <!--
    <bean id="e1" class="com.eomcs.spring.ioc.ex03.Engine"/>
    -->
</beans>

 

package com.eomcs.spring.ioc.ex03;

public class Car {
  String model;
  String maker;
  int cc;

  public Car() {
    System.out.println("Car() 생성자 호출됨!");
  }


  public Car(int cc) {
    System.out.println("Car(int) 생성자 호출됨!");
    this.cc = cc;
  }

  public Car(String model) {
    System.out.println("Car(String) 생성자 호출됨!");
    this.model = model;
  }

  public Car(int cc, String model) {
    System.out.println("Car(int, String) 생성자 호출됨!");
    this.model = model;
    this.cc = cc;
  }

  public Car(String model, int cc) {
    System.out.println("Car(String, int) 생성자 호출됨!");
    this.model = model;
    this.cc = cc;
  }

/* 후략 */

 

 

생성자의 파라미터 값을 지정하는 방법 II
package com.eomcs.spring.ioc.ex03.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // SpringUtils.printBeanNames(iocContainer);
  }

}

application-context.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">
    
    <!-- 호출할 생성자 지정하기 II -->
    
    <!-- 생성자의 파라미터 값을 지정하는 간단한 방법 -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg type="java.lang.String" value="티코"/>
    </bean>
    
    <!-- index로 파라미터의 순서를 지정하기 -->
    <bean id="c2" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg index="0" type="java.lang.String" value="티코"/>
        <constructor-arg index="1" type="int" value="890"/>
    </bean>
    
    <!-- value 속성에 지정한 값은 문자열이다.
         생성자를 호출하여 값을 넣을 때 
         IoC 컨테이너는 이 문자열을 파라미터 타입으로 형변환하여 넣는다. 
         단 primitive type에 대해서만 형변환할 수 있다.
         다른 타입은 불가하다 -->
    <bean id="c3" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg index="0" value="티코"/>
        <constructor-arg index="1" value="890"/>
    </bean>
</beans>

 

 

생성자의 파라미터 값을 형변환 할 수 없는 경우 - 예외 발생
package com.eomcs.spring.ioc.ex03.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // SpringUtils.printBeanNames(iocContainer);
  }

}

application-context.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">
    
    <!-- 만약 value 속성에 설정한 문자열 값을 생성자의 파라미터 타입의 값으로 
         형변환 할 수 없다면 실행 오류가 발생할 것이다.
         즉 다음에서 value="aaa"는 cc 파라미터의 int 값으로 바꿀 수 없기 때문에 오류 발생! -->
    <bean id="c4" class="com.eomcs.spring.ioc.ex03.Car">
        <constructor-arg index="0" value="티코"/>
        <constructor-arg index="1" value="aaa"/>
    </bean>
</beans>

 

 

생성자의 파라미터 값을 지정하는 방법 III
package com.eomcs.spring.ioc.ex03.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // SpringUtils.printBeanNames(iocContainer);
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:c="http://www.springframework.org/schema/c"
    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">
    
    <!-- 호출할 생성자 지정하기 III -->
    
    <!-- 생성자의 파라미터 값을 지정할 때 constructor-arg 태그가 아닌 
         bean 태그의 속성 값으로 지정할 수 있다. 
         단, beans 태그의 선언부에 다음 설정을 추가해야 한다.
         xmlns:c="http://www.springframework.org/schema/c"
         
         방법:
         c:파라미터명="값"
         이 방식으로는 타입을 지정할 수 없다. 
         그냥 IoC 컨테이너에게 적절한 생성자를 호출하도록 맡겨야 한다.
    -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex03.Car" c:model="티코"/>
    <bean id="c2" class="com.eomcs.spring.ioc.ex03.Car" c:cc="1980"/>
    <bean id="c3" class="com.eomcs.spring.ioc.ex03.Car" c:model="티코" c:cc="890"/>

    <!-- 물론 순서를 지정할 수 있다. 
         => 방법:
           c:_인덱스번호="값"
         => 인덱스는 0부터 시작한다.
    -->
    <bean id="c4" class="com.eomcs.spring.ioc.ex03.Car" c:_1="티코" c:_0="890"/>

</beans>

 

 

com.eomcs.spring.ioc.ex04

 

 

프로퍼티 값 지정하기
package com.eomcs.spring.ioc.ex04.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // 객체가 생성될 때 생성자와 세터가 호출되는 것을 확인한다.
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="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으로 자동 변환한다.
        
        objPool.put("c1", c1);
    -->
    
    <!-- 프로퍼티의 타입을 생략하면 IoC 컨테이너가 
         String을 프로퍼티의 타입에 맞춰서 자동 형변환한다. 
    -->
    <bean id="c2" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model"><value>티코2</value></property>
        <property name="maker"><value>비트자동차</value></property>
        <property name="cc"><value>890</value></property>
    </bean>
    
    <!-- value 태그 대신에 property 태그의 value 속성으로 값을 지정할 수 있다. -->
    <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>
    
    <!-- property 태그 대신에 bean의 속성으로 프로퍼티 값을 설정할 수 있다.
         단 beans 태그에 다음 선언을 추가해야 한다.
         => xmlns:별명="http://www.springframework.org/schema/p"
         예) 
         xmlns:p="http://www.springframework.org/schema/p"
         xmlns:okok="http://www.springframework.org/schema/p"
         xmlns:nono="http://www.springframework.org/schema/p"
         
         설정하는 방법:
         p:프로퍼티명="값"
          -->
    <bean id="c4" class="com.eomcs.spring.ioc.ex04.Car"
          p:model="티코4" p:maker="비트자동차" p:cc="890"/>
          
</beans>

 

 

프로퍼티 값 자동 형변환
package com.eomcs.spring.ioc.ex04.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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에 대해서만 가능하다.
    // 그 외의 타입에 대해서는 문자열을 자동 형변환하지 않는다.
    // 형변환하고 싶으면 개발자가 형변환시키는 클래스를 만들어
    // 스프링 프레임워크에 등록해야 한다.
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="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>

 

 

프로퍼티에 객체 주입하기
package com.eomcs.spring.ioc.ex04.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
    System.out.println(iocContainer.getBean("c2"));

    System.out.println(iocContainer.getBean("e1"));
    System.out.println(iocContainer.getBean("e2"));
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="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>

 

 

프로퍼티 호출 - 객체 주입 시 객체 생성 순서
package com.eomcs.spring.ioc.ex04.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // 프로퍼티 값을 주입할 때:
    // 1) 의존 객체가 생성되지 않은 상태라면,
    // 먼저 의존 객체를 생성한 후 프로퍼티 값을 주입한다.
    //
    // 2) 의존 객체가 생성된 상태라면,
    // 그대로 프로퍼티 값을 주입한다.

    System.out.println("-------------------------");

    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="티코A"/>
        <property name="engine" ref="e1"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
    </bean>
    
    <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> 
</beans>

 

 

프로퍼티 호출 - 의존 객체 주입할 때 즉시 객체 생성하기
package com.eomcs.spring.ioc.ex04.e;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.ex04.Car;

public class Exam01 {

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

    Car c1 = (Car) iocContainer.getBean("c1");
    Car c2 = (Car) iocContainer.getBean("c2");

    System.out.println(c1.getEngine() == c2.getEngine());
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="티코A"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="engine">
          <bean class="com.eomcs.spring.ioc.ex04.Engine">
            <property name="maker" value="비트자동차"/>
            <property name="valve" value="16"/>
            <property name="cylinder" value="8"/>
          </bean>
        </property>
    </bean>
    <!-- 
      Car c1 = new Car();
      c1.setModel("티코A");
      c1.setMaker("비트자동차");
      c1.setCc(Integer.parseInt("890"));
      Engine temp = new Engine();
      temp.setMaker("비트자동차");
      temp.setValve(Integer.parseInt("16"));
      temp.setCylinder(Integer.parseInt("8"));
      c1.setEngine(temp);
     -->

    <bean id="c2" class="com.eomcs.spring.ioc.ex04.Car">
        <property name="model" value="소나타"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="1980"/>
        <property name="engine">
            <bean class="com.eomcs.spring.ioc.ex04.Engine">
                <property name="maker" value="비트자동차"/>
                <property name="valve" value="16"/>
                <property name="cylinder" value="8"/>
            </bean>
        </property>
    </bean>
</beans>

 

 

com.eomcs.spring.ioc.ex05

 

 

컬렉션 타입의 프로퍼티 값 설정 - 배열
package com.eomcs.spring.ioc.ex05.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
    System.out.println(iocContainer.getBean("c2"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex05.a.Car">
        <!-- 배열 프로퍼티 값 설정하기 -->
        <property name="tires">
            <array>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="비트타이어" p:width="100"/>
            </array>
        </property>
    </bean>
    
    <bean id="c2" class="com.eomcs.spring.ioc.ex05.a.Car">
        <!-- 배열 프로퍼티 값 설정하기 -->
        <property name="tires">
            <list> <!-- array 태그 대신에 list 태그를 사용해도 된다. -->
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.a.Tire">
                  <property name="maker" value="비트타이어"/>
                  <property name="width" value="100"/>
                </bean> 
            </list>
        </property>
    </bean>
          
</beans>
package com.eomcs.spring.ioc.ex05.a;

import java.util.Arrays;

public class Car {
  String model;
  String maker;
  int cc;
  Engine engine;
  Tire[] tires;

/* 후략 */
package com.eomcs.spring.ioc.ex05.a;

public class Tire {
  String maker;
  int width;
  int ratio;
  int wheel;

/* 후략 */

 

 

컬렉션 타입의 프로퍼티 값 설정 - List<>
package com.eomcs.spring.ioc.ex05.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
    System.out.println(iocContainer.getBean("c2"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex05.b.Car">
        <!-- List 프로퍼티 값 설정하기 : 배열과 똑같다.  -->
        <property name="tires">
            <array> <!-- list 태그 대신에 array 태그를 사용해도 된다. -->
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="비트타이어" p:width="100"/>
            </array>
        </property>
    </bean>
    
    <bean id="c2" class="com.eomcs.spring.ioc.ex05.b.Car">
        <!-- List 프로퍼티 값 설정하기 : 배열과 똑같다.-->
        <property name="tires">
            <list> 
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="금호타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="한국타이어" p:width="180"/>
                <bean class="com.eomcs.spring.ioc.ex05.b.Tire" 
                      p:maker="비트타이어" p:width="100"/>
            </list>
        </property>
    </bean>
          
</beans>

 

 

컬렉션 타입의 프로퍼티 값 설정 - Map<,>
package com.eomcs.spring.ioc.ex05.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex05.c.Car">
        <!-- Map 프로퍼티 값 설정하기 : 배열과 똑같다.  -->
        <property name="options">
            <map>
                <entry>
                    <key><value>sunroof</value></key> 
                    <value>true</value>
                </entry>
                <entry key="auto" value="true"/>
                <entry key="spareTire">
                    <!-- 객체를 바로 만들어 넣고 싶다면 bean 태그를 사용하라! -->
                    <bean class="com.eomcs.spring.ioc.ex05.c.Tire" p:maker="비트타이"/>
                </entry>
                <entry key="engine" value-ref="e1"/>
            </map>
        </property>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex05.c.Engine" p:maker="비트자동차"/>  
</beans>
package com.eomcs.spring.ioc.ex05.c;

import java.util.Map;

public class Car {
  String model;
  String maker;
  int cc;
  Map<String, Object> options;

/* 후략 */

 

 

컬렉션 타입의 프로퍼티 값 설정 - Properties
package com.eomcs.spring.ioc.ex05.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex05.d.Car">
        <!-- Properties 프로퍼티 값 설정하기  -->
        <property name="options">
            <props>
                <prop key="sunroof">true</prop>
                <prop key="auto">true</prop>
                <prop key="color">black</prop>
                <prop key="blackbox">false</prop>
            </props>
        </property>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex05.d;

import java.util.Properties;

public class Car {
  String model;
  String maker;
  int cc;
  Properties options;

/* 후략 */

 

 

com.eomcs.spring.ioc.ex06

 

 

팩토리 메서드 호출 - static 메서드 호출
package com.eomcs.spring.ioc.ex06.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

  public static void main(String[] args) {
    // 자바 코드에서 팩토리 메서드를 사용하여 객체 생성하기
    //Car c = CarFactory.create("티코");

    // Spring IoC 컨테이너에서 팩토리 메서드를 사용하여 객체 생성하기
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex06/a/application-context.xml");

    System.out.println(iocContainer.getBean("c1"));
    System.out.println(iocContainer.getBean("c2"));
    System.out.println(iocContainer.getBean("c3"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" 
          class="com.eomcs.spring.ioc.ex06.a.CarFactory"
          factory-method="create">
        <!-- create() 메서드를 호출할 때 넘겨줄 파라미터 값 -->
        <constructor-arg value="티코"/>
    </bean>
    <!--
        속성:
            factory-method="스태틱 메서드 이름"
            class="스태틱 메서드가 들어있는 클래스 이름"
        자바 코드:
            Object obj = CarFactory.create("티코");
            objPool.put("c1", obj);
        => 'c1' 이라는 이름으로 저장되는 것은 CarFactory 객체가 아니라 
           create()가 리턴한 Car 객체이다.
     -->
    
    <bean id="c2" class="com.eomcs.spring.ioc.ex06.a.CarFactory"
          factory-method="create">
        <constructor-arg value="소나타"/>
    </bean>
    
    <bean id="c3" class="com.eomcs.spring.ioc.ex06.a.CarFactory"
          factory-method="create">
        <constructor-arg value="오호라"/>
    </bean>
          
</beans>
package com.eomcs.spring.ioc.ex06.a;

import com.eomcs.spring.ioc.ex06.Car;

public class CarFactory {

  public CarFactory() {
    System.out.println("CarFactory() 생성자 호출!");
  }

  public static Car create(String model) {
    System.out.printf("CarFactory.create(%s) 호출됨\n", model);

    Car c = new Car();
    switch (model) {
      case "티코":
        c.setMaker("대우자동차");
        c.setModel("Tico");
        c.setCc(890);
        return c;
      case "소나타":
        c.setMaker("현대자동차");
        c.setModel("Sonata");
        c.setCc(1980);
        return c;
      case "SM5":
        c.setMaker("르노삼성자동차");
        c.setModel("SM5");
        c.setCc(1990);
        return c;
      default:
        c.setMaker("비트자동차");
        c.setModel("자바휘웅");
        c.setCc(5000);
        return c;
    }
  }
}

 

 

팩토리 메서드 호출 - static 메서드 호출 응용
package com.eomcs.spring.ioc.ex06.b;

import java.sql.Date;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

  public static void main(String[] args) {
    // 자바 코드로 스태틱 메서드를 호출하기
    Date totay = Date.valueOf("2020-3-20");

    // Spring IoC 컨테이너에서 스태틱 메서드를 호출하여
    // 그 리턴 값을 보관하기
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex06/b/application-context.xml");

    Date date = (Date) iocContainer.getBean("d1");
    System.out.println(date);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- 팩토리 메서드를 통해 객체를 만들기 - 응용 -->
    
    <!-- "yyyy-MM-dd" 형식의 문자열을 가지고 java.sql.Date 객체 만들기 
         자바 코드:
         Date d1 = Date.valueOf("yyyy-MM-dd");
    -->
    <bean id="d1" class="java.sql.Date"
          factory-method="valueOf">
        <constructor-arg value="2021-11-16"/>
    </bean>
          
</beans>

 

 

팩토리 메서드 호출 - 인스턴스 메서드 호출
package com.eomcs.spring.ioc.ex06.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="carFactory" class="com.eomcs.spring.ioc.ex06.c.CarFactory"/>
    
    <!-- 자바 코드:
            CarFactory carFactory = new CarFactory();
            Object obj = carFactory.create("SM5");
            objPool.put("c1", obj);
     -->
    <bean id="c1" 
          factory-bean="carFactory"
          factory-method="create">
        <constructor-arg value="SM5"/>
    </bean>
    <!--
        속성:
            factory-bean="공장역할을 수행하는 객체이름"
            factory-method="인스턴스 메서드 이름"
            => factory-bean 속성을 설정하면 factory-method는 
               스태틱 메서드가 아니라 인스턴스 메서드를 가리키게 된다.
     -->
    
</beans>
package com.eomcs.spring.ioc.ex06.c;

import com.eomcs.spring.ioc.ex06.Car;

public class CarFactory {

  // 팩토리 메서드가 인스턴스 메서드이다.
  // => 팩토리 메서드가 non-static 메서드이다.
  public Car create(String model) {
    Car c = new Car();
    switch (model) {
      case "티코":
        c.setMaker("대우자동차");
        c.setModel("Tico");
        c.setCc(890);
        return c;
      case "소나타":
        c.setMaker("현대자동차");
        c.setModel("Sonata");
        c.setCc(1980);
        return c;
      case "SM5":
        c.setMaker("르노삼성자동차");
        c.setModel("SM5");
        c.setCc(1990);
        return c;
      default:
        c.setMaker("비트자동차");
        c.setModel("자바휘웅");
        c.setCc(5000);
        return c;
    }
  }
}

 

 

팩토리 메서드 호출 - FactoryBean 구현체
package com.eomcs.spring.ioc.ex06.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1").getClass().getName());
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex06.d.CarFactory">
        <property name="model" value="소나타"/>
    </bean>
    <!-- 자바 코드:
            CarFactory carFactory = new CarFactory();
            carFactory.setModel("소나타");
            
            // FactoryBean의 구현체 여부에 따라 "c1" 이라는 이름으로 저장할 객체가 다르다.
            if (carFactory instanceof FactoryBean) {
                objPool.put("c1", carFactory.getObject());
            } else {
                objPool.put("c1", carFactory);
            }
     -->
    
</beans>
package com.eomcs.spring.ioc.ex06.d;

import org.springframework.beans.factory.FactoryBean;
import com.eomcs.spring.ioc.ex06.Car;

// 스프링 IoC 컨테이너가 정한 규칙에 따라 공장 클래스를 만들면,
// 구현할 때 복잡한 면이 있다.
// 하지만 빈 생성을 설정할 때는 기존 방식 보다 쉽다.
//
// 스프링에서 공장 클래스를 만들 때 제안한 규칙?
// => org.springframework.beans.factory.FactoryBean 인터페이스
//
public class CarFactory implements FactoryBean<Car> {

  String model;

  public CarFactory() {
    System.out.println("CarFactory() 생성자 호출됨.");
  }

  public void setModel(String model) {
    System.out.println("CarFactory.setModel() 호출됨.");
    this.model = model;
  }

  @Override
  public Car getObject() throws Exception {
    System.out.println("CarFactory.getObject() 호출됨.");
    // 객체를 생성해서 리턴하는 팩토리 메서드이다.
    // 스프링 IoC 컨테이너는 이 메서드를 호출할 것이다.
    // 이 방식으로는 객체를 생성할 때 추가적으로 필요한 값을 파라미터로 받을 수 없기 때문에
    // 프로퍼티로 받도록 해야 한다.
    Car c = new Car();
    switch (model) {
      case "티코":
        c.setMaker("대우자동차");
        c.setModel("Tico");
        c.setCc(890);
        return c;
      case "소나타":
        c.setMaker("현대자동차");
        c.setModel("Sonata");
        c.setCc(1980);
        return c;
      case "SM5":
        c.setMaker("르노삼성자동차");
        c.setMaker("SM5");
        c.setCc(1990);
        return c;
      default:
        c.setMaker("비트자동차");
        c.setModel("자바휘웅");
        c.setCc(5000);
        return c;
    }
  }

  @Override
  public Class<?> getObjectType() {
    // getObject()가 생성해주는 객체의 타입 정보를 리턴한다.
    // 이 메서드는 Spring IoC 컨테이너가 타입으로 객체를 찾을 때 사용한다.
    System.out.println("CarFactory.getObjectType() 호출됨.");
    return Car.class;
  }
}

 

 

팩토리 메서드 호출 - FactoryBean 구현체
package com.eomcs.spring.ioc.ex06.e;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.ex06.Car;

public class Exam01 {

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

    // 이 예제는 Factory 클래스의 이름을
    // CarFactory에서 CarFactoryBean으로 바꾼 것이다.
    Car obj1 = (Car) iocContainer.getBean("c1");
    System.out.println(obj1);

    // 다음과 같이 타입으로 객체를 찾을 때
    // FactoryBean의 getObjectType()이 호출된다.
    Car obj2 = iocContainer.getBean(Car.class);
    System.out.println(obj2);

    System.out.println(obj1 == obj2);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- 클래스 이름이 FactoryBean으로 끝나면 
         그 클래스가 FactoryBean 구현체임을 쉽게 눈치챌 수 있다. -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex06.e.CarFactoryBean">
        <property name="model" value="소나타"/>
    </bean>
    <!-- 자바 코드:
            CarFactoryBean carFactory = new CarFactoryBean();
            carFactory.setModel("소나타");
            
            // FactoryBean의 구현체 여부에 따라 "c1" 이라는 이름으로 저장할 객체가 다르다.
            if (carFactory instanceof FactoryBean) {
                objPool.put("c1", carFactory.getObject());
            } else {
                objPool.put("c1", carFactory);
            }
     -->
    
</beans>
package com.eomcs.spring.ioc.ex06.e;

import org.springframework.beans.factory.FactoryBean;
import com.eomcs.spring.ioc.ex06.Car;

// 보통 FactoryBean 구현체를 만들 때는
// 클래스 이름 뒤에 접미사로 FactoryBean을 붙여
// 다른 개발자가 쉽게 알아보도록 만든다.
//
public class CarFactoryBean implements FactoryBean<Car> {
  String model;

  public CarFactoryBean() {
    System.out.println("CarFactoryBean() 생성자 호출됨.");
  }

  public void setModel(String model) {
    System.out.println("CarFactoryBean.setModel() 호출됨.");
    this.model = model;
  }

  @Override
  public Car getObject() throws Exception {
    System.out.println("CarFactoryBean.getObject() 호출됨.");
    // 객체를 생성해서 리턴하는 메서드이다.
    // 스프링 IoC 컨테이너는 이 메서드를 호출할 것이다.
    // 이 방식으로는 객체를 생성할 때 추가적으로 필요한 값을 파라미터로 받을 수 없기 때문에
    // 프로퍼티로 받도록 해야 한다.
    Car c = new Car();
    switch (model) {
      case "티코":
        c.setMaker("대우자동차");
        c.setModel("Tico");
        c.setCc(890);
        return c;
      case "소나타":
        c.setMaker("현대자동차");
        c.setModel("Sonata");
        c.setCc(1980);
        return c;
      case "SM5":
        c.setMaker("르노삼성자동차");
        c.setMaker("SM5");
        c.setCc(1990);
        return c;
      default:
        c.setMaker("비트자동차");
        c.setModel("자바휘웅");
        c.setCc(5000);
        return c;
    }
  }

  @Override
  public Class<?> getObjectType() {
    // getObject()가 생성해주는 객체의 타입 정보를 리턴한다.
    // 이 메서드는 Spring IoC 컨테이너가 타입으로 객체를 찾을 때 사용한다.
    System.out.println("CarFactoryBean.getObjectType() 호출됨.");
    return Car.class;
  }
}

 

 

com.eomcs.spring.ioc.ex07

 

 

프로퍼티 에디터 - 기본으로 내장된 프로퍼티 에디터
package com.eomcs.spring.ioc.ex07.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
    // 실행 오류 발생!
    // 이유?
    // java.lang.String 값을 java.sql.Date 객체로 변환할 수 없어서!
    // 즉 스프링 IoC 컨테이너에 String을 Date으로 바꾸는 변환기가 설치되어 있지 않다.
    //
    // 프로퍼티 에디터?
    // => String 값을 다른 타입의 값으로 변환하는 객체
    //
    // 스프링 빌트인 프로퍼티 에디터(변환기)
    // => primitive 타입 변환기는 기본으로 내장하고 있다.
    // => 따라서 String을
    // byte, short, int, long, float, double, boolean, char로 바꿀 수 있다.
    // => 그 외의 타입은 개발자가 프로퍼티 에디터를 만들어 등록해야 한다.
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex07.Car">
        <property name="model" value="티코"/> <!-- String ===> String -->
        <property name="maker" value="비트자동차"/> <!-- String ===> String -->
        <property name="cc" value="890"/> <!-- String ===> int -->
        <property name="auto" value="true"/> <!-- String ===> boolean -->
        
        <!-- 자바 원시 타입이 아닌 경우 String을 그 타입으로 자동 변환하지 못한다. 
             예외 발생! -->
        <property name="createdDate" value="2021-6-1"/> <!-- String ===> Date -->
    </bean>
</beans>

 

 

프로퍼티 에디터 - String ==> java.sql.Date ; 변환기 없이 처리하기
package com.eomcs.spring.ioc.ex07.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex07.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
        <property name="createdDate">
            <!-- String을 java.sql.Date 객체로 바꿔주는 변환기가 없기 때문에
                 직접 Date 객체를 생성해야 한다. 
                 단, 이방식은 날짜 프로퍼티 값을 설정할 때마다 
                 매번 작성해야 하기 때문에 불편하다. 
                 해결책? String을 java.sql.Date 객체로 변환해주는
                 프로퍼티 에디터를 등록하면 된다.-->
            <bean class="java.sql.Date" factory-method="valueOf">
                <constructor-arg value="2021-6-1"/>
            </bean>
        </property>
    </bean>
</beans>

 

 

프로퍼티 에디터 - String ==> java.sql.Date ; 변환기 사용하여 처리하기
package com.eomcs.spring.ioc.ex07.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));

    // IoC 컨테이너는 application-context.xml 에 설정된 대로 다음과 같이 객체를 만든다.
    /*
    Car c1 = new Car();
    c1.setModel("티코");
    c1.setMaker("비트자동차");
    c1.setCc(Integer.valueOf("890"));
    c1.setAuto(Boolean.valueOf("true"));

    CustomDateEditor cde = new CustomDateEditor();
    cde.setAsText("2021-6-1");

    c1.setCreatedDate((Date)cde.getValue());

    System.out.println(c1);
     */
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex07.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
        <!-- String을 java.sql.Date 객체로 바꿔주는 프로퍼티 에디터(변환기)가 
             설정되었기 때문에 
             이제 그냥 문자열을 지정해도 된다. -->
        <property name="createdDate" value="2021-6-1"/>
    </bean>
    
    <!-- String 값을 java.sql.Date 객체로 변환시키는 프로퍼티 에디터 장착하기 -->
    <!-- Spring IoC 컨테이너가 XML 설정 파일을 읽을 때: 
         만약 CustomEditorConfigurer 객체가 정의되어 있다면 
         그 객체를 설정파일에 정의된 대로 만들고 준비시킨다.
         그리고 프로퍼티 값을 저장할 때 이 객체에 정의된 프로퍼티 에디터를 사용할 것이다.   -->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	    <property name="customEditors"> 
	        <!-- setCustomEditor(Map<Class,Class> param) {...} -->
	        <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>
	
</beans>
// 프로퍼티 에디터 만들기
package com.eomcs.spring.ioc.ex07.c;

import java.beans.PropertyEditorSupport;
import java.sql.Date;

// 프로퍼티 에디터 만드는 방법
// => java.beans.PropertyEditor 인터페이스를 구현하면 된다.
// => 그러나 이 인터페이스를 직접 구현하려면 
//    많은 메서드(12개의 메서드)를 모두 구현해야 한다.
//    너무 번거롭다.
// => 그래서 자바는 PropertyEditor를 미리 구현한 
//    PropertyEditorSupport 라는 클래스를 제공한다.
//    따라서 인터페이스를 직접 구현하지 말고 이 클래스를 상속 받아라!
//
// 1) java.beans.PropertyEditorSupport를 상속 받는다.
//
public class CustomDateEditor extends PropertyEditorSupport {
  // 이 메서드는 스프링 IoC 컨테이너가 String 타입의 프로퍼티 값을
  // 다른 타입의 값으로 바꿀 때 호출하는 메서드이다.
  @Override
  public void setAsText(String text) throws IllegalArgumentException {
    System.out.println("CustomDateEditor.setAsText()");
    // 파라미터로 넘어온 String 타입의 프로퍼티 값을
    // 원하는 타입(java.sql.Date)의 값으로 바꿔 내부에 저장한다.
    // => 그러면 스프링 IoC 컨테이너를 이 값을 꺼내서 객체에 주입할 것이다.
    this.setValue(Date.valueOf(text));
  }

  // 언제 어떤 메서드가 호출되는지 확인하기 위해 상속 받은 메서드를 오버라이딩 해보자!
  @Override
  public void setValue(Object value) {
    // 이 메서드는 호출되었을 때 그것을 표시하기 위해 오버라이딩 했다.
    System.out.println("CustomDateEditor.setValue()");
    super.setValue(value); // 원래 하던 일을 수행한다.
  }

  @Override
  public Object getValue() {
    // 이 메서드는 호출되었을 때 그것을 표시하기 위해 오버라이딩 했다.
    System.out.println("CustomDateEditor.getValue()");
    return super.getValue(); // 원래 하던 일을 수행한다.
  }
}

 

 

프로퍼티 에디터 - String ==> Engine ; 프로퍼티 에디터 변환기를 사용하여 처리하기
package com.eomcs.spring.ioc.ex07.d;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    System.out.println(iocContainer.getBean("c1"));

    System.out.println("----------------------------");

    // SpringUtils.printBeanNames(iocContainer);
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex07.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
        <property name="createdDate" value="2021-6-1"/>
        <property name="engine" value="비트자동차,16,4"/>
        <!-- 자바 코드 표현
		      => 스프링 IoC 컨테이너는 String 을 Engine 객체로 변환시켜줄 프로퍼티 에디터를 찾는다.
		      => 그 에디터를 사용하여 문자열을 Engine 객체로 바꾼다.
		         customEngineEditor.setAsText("비트자동차,16,4");
		      => 변환된 값을 꺼낸다.
		         Engine temp = customEngineEditor.getObject();
		      => engine 프로퍼티에 값을 넣는다.
		         c1.setEngine(temp);
		    -->
    </bean>
    
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	    <property name="customEditors">
	        <map>
	            <entry key="java.sql.Date" 
	                   value="com.eomcs.spring.ioc.ex07.c.CustomDateEditor"/>
	            <entry key="com.eomcs.spring.ioc.ex07.Engine" 
                     value="com.eomcs.spring.ioc.ex07.d.CustomEngineEditor"/>
	        </map>
	    </property>
	</bean>
</beans>
// 프로퍼티 에디터 만들기
package com.eomcs.spring.ioc.ex07.d;

import java.beans.PropertyEditorSupport;
import com.eomcs.spring.ioc.ex07.Engine;

public class CustomEngineEditor extends PropertyEditorSupport {
  @Override
  public void setAsText(String text) throws IllegalArgumentException {
    String[] values = text.split(",");

    Engine engine = new Engine();
    engine.setMaker(values[0]);
    engine.setValve(Integer.parseInt(values[1]));
    engine.setCylinder(Integer.parseInt(values[2]));

    this.setValue(engine);
  }
}

 

 

com.eomcs.spring.ioc.ex08

 

 

의존 객체 주입 자동화하기 - 자동화 전, XML 설정을 통한 수동 주입
package com.eomcs.spring.ioc.ex08.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Exam01 {

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

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c1" class="com.eomcs.spring.ioc.ex08.a.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
        <!-- 의존 객체 주입(Dependency Injection; DI) -->
        <property name="engine" ref="e1"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.a.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
</beans>

 

 

의존 객체 주입 자동화하기 - 셋터에 @Autowired 애노테이션 붙이기
package com.eomcs.spring.ioc.ex08.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {

    // [동작원리]
    // 1) 스프링 IoC 컨테이너는 객체를 만든다.
    // 2) 프로퍼티 값을 설정한다.
    // 3) 객체 생성 후 IoC 컨테이너에 등록된
    //    리스너(BeanPostProcessor)에게 통보한다.
    // 4) AutowiredAnnotationBeanPostProcessor 리스너가 있다면,
    //    @Autowired 애노테이션을 처리한다.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/b/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));

  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- 의존 객체를 자동 주입하는 기능을 쓰고 싶어요!
         => 그 일을 할 객체를 등록하세요.
         어떤 객체인가요?
         => AutowiredAnnotationBeanPostProcessor 입니다.
         이 객체는 어떻게 사용하나요?
         => 셋터 메서드 또는 필드에 @Autowired를 붙이면 됩니다.
    -->
    <!-- @Autowired 애노테이션을 셋터 메서드에 붙였다고 해서 
         의존 객체가 자동 주입되는 것이 아니다.
         @Autowired 애노테이션이 붙은 셋터에 대해
         프로퍼티 값을 자동으로 주입하는 일을 할 객체를 등록해야 한다.
    -->
    <!-- @Autowired 애노테이션 도우미 등록방법:
         다음 클래스의 객체를 등록하면 된다.
         org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
    -->
    <!-- 동작원리:
         => Spring IoC 컨테이너가 설정 파일에 적혀있는 대로 객체를 생성한다.
         => 객체 생성 후에 BeanPostProcessor에게 보고한다.
         => AutowiredAnnotationBeanPostProcessor는 생성된 객체에 대해 
            @Autowired 애노테이션을 검사하여 
            이 애노테이션이 붙은 프로퍼티 값을 자동 주입하는 일을 한다.
         => 이 객체를 스프링 IoC 컨테이너에 등록하지 않으면,
            @Autowired 애노테이션은 처리되지 않는다. 
    -->
    <!-- 객체 생성 후 작업을 수행하는 역할자를 정의하는 방법:
         => BeanPostProcessor 규칙에 따라 클래스를 정의한 후 객체를 등록하면 된다. 
    -->
    <!-- BeanPostProcessor 인터페이스:
         => 스프링 IoC 컨테이너는 객체 중에 이 인터페이스를 구현한 객체가 있다면,
            설정 파일에 적혀있는 객체를 생성한 후에
            이 구현체의 postProcess....() 메서드를 호출한다. 
         => 즉 빈 생성 이후의 마무리 작업을 진행시킨다.
         => 그래서 이 인터페이스의 이름이 
            BeanPostProcessor(객체 생성 후 처리기) 인 것이다.
    -->
    
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

    <bean id="c1" class="com.eomcs.spring.ioc.ex08.b.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
        <!-- Engine 객체를 주입하지 않는다. -->
        <!--  
        <property name="engine" ref="e1"/>
        -->
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.b.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex08.b;

import java.sql.Date;
import org.springframework.beans.factory.annotation.Autowired;

// 의존 객체 Engine 주입 - 셋터 메서드에 @Autowired로 표시하라!
public class Car {
  String model;
  String maker;
  int cc;
  boolean auto;
  Date createdDate;
  Engine engine;

  public Car() {
    System.out.println("Car()");
  }

  public Engine getEngine() {
    return engine;
  }

  @Autowired
  public void setEngine(Engine engine) {
    System.out.println("Car.setEngine()");
    this.engine = engine;
  }

/* 후략 */

 

 

 

BeanPostProcessor 사용법 - 동작 원리
package com.eomcs.spring.ioc.ex08.c1;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    

    <!-- BeanPostProcessor 인터페이스 구현체 만들고 등록하기 -->
    <!-- BeanPostProcessor 인터페이스:
         => 스프링 IoC 컨테이너는 객체 중에 이 인터페이스를 구현한 객체가 있다면,
            설정 파일에 적혀있는 객체를 생성한 후에
            이 구현체의 postProcess....() 메서드를 호출한다. 
         => 즉 빈 생성 이후의 마무리 작업을 진행시킨다.
         => 그래서 이 인터페이스의 이름이 
            BeanPostProcessor(객체 생성 후 처리기) 인 것이다.
    -->
    <bean class="com.eomcs.spring.ioc.ex08.c1.MyBeanPostProcessor"/>
    
    <bean id="c1" class="com.eomcs.spring.ioc.ex08.c1.Car" init-method="init">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.c1.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex08.c1;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

// Spring IoC 컨테이너에 새 기능을 추가하는 예:
// => 새 기능이 IoC 컨테이너가 생성한 객체를 사용해야 한다면,
//    객체 생성 후에 그 작업을 수행하면 된다.
// => 이렇게 개발자가 컨테이너의 기능을 확장할 수 있도록
//    BeanPostProcessor라는 규칙을 제공한다.
//
// 즉 빈 생성 후에 어떤 작업을 수행할 객체를 만들고 싶다면?
// => BeanPostProcessor 규칙에 따라 클래스를 만들라!
// => 옵저버 디자인 패턴이 적용된 대표적인 예이다.
//
// BeanPostProcessor
// => 스프링 IoC 컨테이너는 빈을 생성하고 세터를 모두 호출한 난 후
//    이 구현체에게 보고한다.
//
public class MyBeanPostProcessor implements BeanPostProcessor {

  public MyBeanPostProcessor() {
    System.out.println("MyBeanPostProcessor()");
  }

  @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;
  }

}
package com.eomcs.spring.ioc.ex08.c1;

import java.sql.Date;
import org.springframework.beans.factory.annotation.Autowired;

// 의존 객체 Engine 주입 - 셋터 메서드에 @Autowired로 표시하라!
public class Car {
  String model;
  String maker;
  int cc;
  boolean auto;
  Date createdDate;
  Engine engine;

  public Car() {
    System.out.println("Car()");
  }

  // IoC 컨테이너의 설정 파일(예:application-context.xml)에서 빈 생성 태그를 정의할 때 
  // 다음 메서드가 인스턴스를 초기화시키는 메서드임을 지정하라.
  //        <bean ... init-method="init">
  // 
  public void init() {
    System.out.println("Car.init()");
  }

  public Engine getEngine() {
    return engine;
  }

  @Autowired
  public void setEngine(Engine engine) {
    System.out.println("Car.setEngine()");
    this.engine = engine;
  }
  
/* 후략 */

 

 

BeanPostProcessor 사용법 - BeanPostProcessor 활용 예
package com.eomcs.spring.ioc.ex08.c2;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    // Spring IoC 컨테이너가 생성한 객체의 클래스 명을 로그로 남기고 싶다.
    // => LogBeanPostProcessor 를 만들어 처리한다.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/c2/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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="c" class="com.eomcs.spring.ioc.ex08.c2.C"/>

    <bean id="b" class="com.eomcs.spring.ioc.ex08.c2.B"/>

    <bean id="a" class="com.eomcs.spring.ioc.ex08.c2.A"/>
    
    <!-- BeanPostProcessor 인터페이스 구현체 만들고 등록하기 -->
    <bean class="com.eomcs.spring.ioc.ex08.c2.LogBeanPostProcessor"/>
package com.eomcs.spring.ioc.ex08.c2;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

// 역할:
// => IoC 컨테이너가 빈을 생성할 때 마다 로그를 출력한다.
//
public class LogBeanPostProcessor implements BeanPostProcessor {

  public LogBeanPostProcessor() {
    System.out.println("LogBeanPostProcessor()");
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.printf("===> %s:%s\n",
        beanName,
        bean.getClass().getName());
    return null;
  }
}

 

 

BeanPostProcessor 사용법 - BeanPostProcessor를 이용하여 @Autowired 애노테이션 처리하기
package com.eomcs.spring.ioc.ex08.c3;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    

    <!-- @Autowired 애노테이션을 처리할 BeanPostProcessor를 등록한다. --> 
    <bean class="com.eomcs.spring.ioc.ex08.c3.MyAutowiredAnnotationBeanPostProcessor"/>
    
    <bean id="c1" class="com.eomcs.spring.ioc.ex08.c3.Car" init-method="init">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.c3.Engine" init-method="init">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex08.c3;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;

// @Autowired 애노테이션을 처리해주는 역할을 한다.
// 스프링 IoC 컨테이너가 객체를 생성한 후 보고를 하면
// 이 클래스는 생성된 객체에서 @Autowired가 붙은 세터를 찾는다.
// 있다면 세터를 호출하여 의존 객체를 주입한다.
// 의존 객체가 없다면 그 의존 객체가 생성될 때까지
// 별도로 담아 둔다.
// 의존 객체가 생성되는 순간 즉시 별도로 담아 둔 그 객체에 대해 셋터를 호출할 것이다.
//
public class MyAutowiredAnnotationBeanPostProcessor implements BeanPostProcessor {

  // 생성된 모든 객체를 기록한다.
  HashMap<Class<?>, List<Object>> beans = new HashMap<>();

  // 파라미터 값이 준비되지 않아서 호출이 연기된 @Autowired 메서드를 기록한 맵
  HashMap<Class<?>, List<AutowiredMethod>> autowiredMethods = new HashMap<>();

  private void addBean(Class<?> type, Object bean) {
    List<Object> objList = beans.get(type); // 해당 타입의 객체 목록을 꺼낸다.
    if (objList == null) { // 해당 타입의 객체 목록이 없다면,
      objList = new ArrayList<>(); // 새로 객체를 저장할 목록을 준비한다.
      beans.put(type, objList); // 해당 타입의 객체를 저장할 목록을 맵에 추가한다.
    }
    objList.add(bean); // 해당 타입의 객체 목록에 새 객체를 추가한다.
  }

  private Object getBean(Class<?> type) {
    List<Object> objList = beans.get(type); // 해당 타입의 객체 목록을 꺼낸다.
    if (objList == null) { // 해당 타입의 객체 목록이 없다면,
      return null;
    }
    return objList.get(0); // 객체 목록에서 첫 번째 객체를 꺼낸다.
  }

  // 객체에 대해 모든 초기화가 끝난 후에 @Autowired 애노테이션을 처리하자!
  // 따라서 다음 메서드만 오버라이딩 한다.
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("MyAutowiredAnnotationBeanPostProcessor.postProcessAfterInitialization()");

    // 새로 생성된 객체를 기록한다.
    addBean(bean.getClass(), bean);

    // 해당 빈에서 @Autowired가 붙은 메서드를 찾아 호출하거나 호출을 연기시킨다.
    callAutowiredMethod(bean);

    // 해당 빈을 필요로하는 셋터 메서드를 찾아 호출한다.
    injectDependency(bean);

    return bean;
  }

  private void addAutowiredMethod(Class<?> paramType, AutowiredMethod autowiredMethod) {

    // 해당 의존 객체에 대해 나중에 호출하기 위해 모아둔 메서드 목록을 꺼낸다.
    List<AutowiredMethod> methods = autowiredMethods.get(paramType);

    if (methods == null) { // 의존 객체를 주입할 메서드 목록이 없다면,
      methods = new ArrayList<>(); // 메서드 목록을 새로 준비한다.
      autowiredMethods.put(paramType, methods); // 그리고 맵에 등록한다.
    }

    // 목록에 셋터 메소드 정보(셋터메서드 + 객체)를 추가한다.
    methods.add(autowiredMethod);
  }


  // 객체의 @Autowired 가 붙은 메서드를 모두 찾아 호출한다.
  private void callAutowiredMethod(Object bean) {
    // 이 객체의 모든 public 메서드를 꺼낸다.
    Method[] methods = bean.getClass().getMethods();

    // public 메서드 중에서 @Autowired로 표시된 메서드를 찾는다.
    for (Method m : methods) {

      // 메서드에서 @Autowired 애노테이션이 있는지 확인한다.
      Autowired anno = m.getAnnotation(Autowired.class);

      if (anno == null) {
        continue;
      }

      // @Autowired 가 붙은 메서드를 찾았으면 호출하여 의존 객체를 주입한다.

      // => 먼저 어떤 타입의 의존 객체인지 알아낸다. 메서드의 파라미터 타입을 알아낸다.
      Class<?> paramType = m.getParameters()[0].getType();

      // => 파라미터 타입의 객체를 보관소에서 꺼낸다.
      Object dependency = this.getBean(paramType);

      if (dependency != null) { // 해당 의존 객체가 있다면
        // 셋터를 호출한다.
        try {
          m.invoke(bean, dependency);
        } catch (Exception e) {
          e.printStackTrace();
        }
      } else { // 셋터가 원하는 파라미터 값이 없다면,
        // 일단 그 값이 나타날 때까지 호출을 연기하자!
        // 즉 호출될 메서드 목록에 추가한다.
        addAutowiredMethod(paramType, new AutowiredMethod(bean, m));
      }
    }
  }

  private void injectDependency(Object dependency) {
    // 이 메서드에서 파라미터로 받은 객체를 원하는 셋터가 있는지 확인한다.
    // => 즉 이 타입의 객체를 받기 위해 호출이 연기된 메서드가 있는지 확인한다.
    List<AutowiredMethod> setters = autowiredMethods.get(dependency.getClass());

    if (setters == null) {
      return;
    }

    // 이 객체를 간절히 원하는 셋터 메서드가 있다면 호출하여 주입한다.
    while (setters.size() > 0) {
      AutowiredMethod setter = setters.remove(0);
      try {
        setter.method.invoke(setter.object, dependency);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }


  class AutowiredMethod {
    Object object; // 메서드를 호출할 때 사용할 인스턴스
    Method method; // @Autowired가 붙은 메서드

    public AutowiredMethod(Object object, Method method) {
      this.object = object;
      this.method = method;
    }
  }
}

 

 

의존 객체 주입 자동화하기 - 인스턴스 필드에 @Autowired 애노테이션 붙이기
package com.eomcs.spring.ioc.ex08.d;

import java.lang.reflect.Field;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) throws Exception {
    // @Autowired 애노테이션을 필드(인스턴스 변수)에 붙여도 된다.
    // => 그러면 의존 객체를 직접 변수에 주입한다.
    // => 셋터를 호출하지 않는다. 즉 셋터가 없어도 된다.
    // => 인스턴스 변수에 직접 의존 객체를 주입한다는 것은
    // 캡슐화(즉 외부에서 직접 인스턴스 변수에 접근하는 것을 막는 기법)를
    // 위배하는 측면이 있기 때문에
    // 이 방식은 "객체지향을 파괴하는 방식"이라는 비난을 받는다.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/d/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));


    // private 멤버는 직접 접근 불가!
    Car c2 = new Car();
    //    c2.model = "비트비트"; // private 멤버이기 때문에 접근 불가! 컴파일 오류!
    c2.setModel("비트비트"); 
    System.out.println(c2);


    // 정말?
    // => Reflection API 사용하면 private 멤버도 접근할 수 있다.
    Field f = Car.class.getDeclaredField("model");
    f.setAccessible(true); // private 멤버이지만 난 접근할래!!!
    f.set(c2, "오호라2");
    System.out.println(c2);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- AutowiredAnnotationBeanPostProcessor은
         @Autowired가 셋터 뿐만아니라 필드에 붙은 경우도 처리한다. 
    -->
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    
    
    <bean id="c1" class="com.eomcs.spring.ioc.ex08.d.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.d.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex08.d;

import java.sql.Date;
import org.springframework.beans.factory.annotation.Autowired;

// 의존 객체 Engine 주입 - 필드에 @Autowired로 표시하라!
public class Car {

  private String model;
  private String maker;
  private int cc;
  private boolean auto;
  private Date createdDate;

  // 필드에 @Autowired를 붙인 경우,
  // 셋터를 통해 값을 넣는 것이 아니라,
  // 인스턴스 필드에 직접 값을 넣는다.
  // private 이라도 상관없다.
  // 따라서 셋터를 정의하지 않아도 된다.
  @Autowired
  private Engine engine;

  public Car() {
    System.out.println("Car()");
  }

  //  @Autowired
  //  public void setEngine(Engine engine) {
  //    System.out.println("Car.setEngine()");
  //    this.engine = engine;
  //  }

 

 

의존 객체 주입 자동화하기 - 생성자를 이용하여 의존 객체 주입
package com.eomcs.spring.ioc.ex08.e;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {

    // 생성자를 통해 의존 객체를 주입할 수 있다.
    // => AutowiredAnnotationBeanPostProcessor가 이것 또한 처리해준다.
    // => 해당 클래스에 기본 생성자가 없을 때,
    //    파라미터를 받는 다른 생성자를 찾아 호출한다.
    // => 물론 그 파라미터에 해당하는 객체가 존재해야 한다.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/e/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- 생성자를 통해 의존 객체 주입하기
      => @Autowired 나 @Resource를 사용할 필요가 없다.
      => 스프링 전문가들 사이에서는 이 방식을 권고하기도 한다.
         왜?
         생성자의 파라미터로 선언하면 해당 의존 객체가 필수 항목이 된다.
         즉 그 의존 객체없이 생성자를 호출할 수  없기 때문이다.
      => 기본 생성자가 없으면 파라미터를 받는 다른 생성자를 호출하여
         의존 객체를 자동주입하려면 다음 객체를 등록해야 한다.
         AutowiredAnnotationBeanPostProcessor
    -->

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
     
    <bean id="c1" class="com.eomcs.spring.ioc.ex08.e.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.e.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
    
</beans>
package com.eomcs.spring.ioc.ex08.e;

import java.sql.Date;

// 의존 객체 Engine 주입 - 생성자를 통해 의존 객체 주입
public class Car {

  private String model;
  private String maker;
  private int cc;
  private boolean auto;
  private Date createdDate;

  //@Autowired
  private Engine engine;

  // Spring IoC 컨테이너로부터 의존 객체를 주입 받고 싶다면,
  // 생성자에 파라미터를 선언하라.
  // @Autowired 는 붙일 필요가 없다. (붙여도 된다)
  // 주의!
  // => 이 일 또한 AutowiredAnnotationBeanPostProcessor 객체가 처리한다.
  //
  public Car(Engine engine) {
    System.out.println("Car(Engine)");
    this.engine = engine;
  }

 

 

의존 객체 주입 자동화하기 - 필수 의존 객체와 선택 의존 객체
package com.eomcs.spring.ioc.ex08.f;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {

    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/f/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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 class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    
    <bean id="c1" class="com.eomcs.spring.ioc.ex08.f.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <!--  
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.f.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
    -->
</beans>
package com.eomcs.spring.ioc.ex08.f;

import java.sql.Date;
import org.springframework.beans.factory.annotation.Autowired;

// 의존 객체 Engine 주입 - 필수 또는 선택
public class Car {

  private String model;
  private String maker;
  private int cc;
  private boolean auto;
  private Date createdDate;

  // @Autowired의 required 값은 기본이 true이다.
  // => 즉 의존객체 주입이 필수사항이다.
  //    해당하는 의존 객체가 없으면 예외가 발생한다.
  // => 선택사항으로 바꾸고 싶으면 false로 설정하라!
  // => required를 false로 설정하면 해당 객체가 없더라도 오류가 발생하지 않는다.
  @Autowired(required = false)
  private Engine engine;

 

 

의존 객체 주입 자동화하기 - 같은 타입의 의존 객체가 여러개 있을 때
package com.eomcs.spring.ioc.ex08.g;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {

    // 같은 타입의 의존 객체가 여러 개 있을 때
    // 그 중 어떤 객체를 주입해야 할 지 알 수 없기 때문에
    // Spring IoC 컨테이너는 예외를 발생시킨다.
    //
    // 해결책?
    // => @Qualifier 애노테이션을 사용하여 주입할 객체를 지정한다.
    //
    // 주의!
    // => AutowiredAnnotationBeanPostProcessor는
    // @Qualifier 애노테이션을 처리하지 못한다.
    // => 따라서 @Qualifier 애노테이션을 처리할 BeanPostProcessor를
    // 따로 등록해야 한다.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/g/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
     
    <!-- 
      클래스에 붙은 특정 애노테이션을 처리할 BeanPostProcessor 등록하기
         @Autowired => AutowiredAnnotationBeanPostProcessor
         @Qualifier => ?
         @Resources => ?
         @...       => ?
         위와 같이 특정 애노테이션을 처리하고 싶다면 
         그 애노테이션을 처리할 객체를 등록해야 한다.
         문제는, 
         각각의 애노테이션에 대해 어떤 클래스가 처리하는지 암기를 해야하고,
         또한 다음 AutowiredAnnotationBeanPostProcessor의 경우와 같이 
         <bean> 태그를 이용하여 그 객체를 등록해야 한다.
      => 결론! 
         매우 번거롭고 불편하다.
         그래서 스프링 프레임워크는 이런 개발자의 어려움을 덜어주고자
         애노테이션을 처리할 클래스를 자동 등록하는 특별한 단축 태그를 제공한다. 
      => <annotation-config> 태그
         이 태그를 선언하면,
         스프링 IoC 컨테이너는 
         애노테이션 처리와 관련된 BeanPostProcessor 들을 자동으로 생성할 것이다.
         개발자가 일일이 BeanPostProcessor를 등록할 필요가 없다.
         따라서 개발자는 각각의 애노테이션을 처리할 BeanPostProcessor의 이름을 
         암기할 필요가 없어서 편하다!
         이 태그는 context 네임스페이스 들어 있다. 
         이 태그를 사용하려면 먼저 context 네임스페이스를 선언해야 한다.
    -->
    <!--  
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
    -->
    
    <context:annotation-config/>
    
    <!-- 같은 타입(Engine)의 의존 객체가 여러 개 있을 때? 
       => 어떤 것을 꼽아야 할 지 알 수 없기 때문에 Car 객체를 생성하지 못한다. 오류 발생!
       => 해결책! 
          @Qualifier("객체이름")을 사용하여 어떤 객체를 꼽을 것인지 지정하라!
    -->
    <bean id="c1" class="com.eomcs.spring.ioc.ex08.g.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.g.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>

    <bean id="e2" class="com.eomcs.spring.ioc.ex08.g.Engine">
        <property name="maker" value="캠프자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="8"/>
    </bean>

</beans>
package com.eomcs.spring.ioc.ex08.g;

import java.sql.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

// 의존 객체 Engine 주입 - 여러 개 중에서 한 개의 의존 객체를 지정하는 방법
public class Car {

  private String model;
  private String maker;
  private int cc;
  private boolean auto;
  private Date createdDate;

  @Autowired
  @Qualifier("e2")
  // 의존 객체가 여러 개 있을 경우, 주입할 의존 객체의 이름을 지정하라!
  // 주의!
  // @Qualifier 애노테이션을 처리할 BeanPostProcessor를 등록해야 한다.
  private Engine engine;

 

 

의존 객체 주입 자동화하기 - @Resource = @Autowired + @Qualifier
package com.eomcs.spring.ioc.ex08.h;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {

    // @Autowired와 @Qualifier를 묶어서 사용하는 대신에
    // 자바 언어 확장 명세서 'JSR-250'에 정의된 @Resource를 사용할 수 있다.
    // 단 이 애노테이션을 사용하려면
    // 이 애노테이션이 정의된 라이브러리를 별도로 다운로드 받아야 한다.
    // 이 애노테이션은 자바 확장 라이브러리에 포함되어 있다.
    // search.maven.org 에서 'javax.annotation' 으로 검색해 보라.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex08/h/application-context.xml");

    // Spring IoC 컨테이너에 들어 있는 객체 출력
    SpringUtils.printBeanList(iocContainer);

    // Car 객체를 꺼내 Engine 객체가 주입되었는지 확인해보자!
    System.out.println(iocContainer.getBean("c1"));
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 다음 태그 하나로 
         여러 애노테이션을 다룰 객체들을 자동 등록할 수 있다. 
    -->
    <context:annotation-config/>

    <bean id="c1" class="com.eomcs.spring.ioc.ex08.h.Car">
        <property name="model" value="티코"/>
        <property name="maker" value="비트자동차"/>
        <property name="cc" value="890"/>
        <property name="auto" value="true"/>
    </bean>
    
    <bean id="e1" class="com.eomcs.spring.ioc.ex08.h.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
    
    <bean id="e2" class="com.eomcs.spring.ioc.ex08.h.Engine">
        <property name="maker" value="비트자동차"/>
        <property name="valve" value="16"/>
        <property name="cylinder" value="4"/>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex08.h;

import java.sql.Date;
import javax.annotation.Resource;

// 의존 객체 Engine 주입 - @Autowired + @Qualifier = @Resource
public class Car {

  private String model;
  private String maker;
  private int cc;
  private boolean auto;
  private Date createdDate;

  // => 이 애노테이션은 스프링 프레임워크가 아닌 자바에서 제공한다.
  // => 자바 기본 라이브러리에 포함되어 있지 않다.
  // => 따로 라이브러리를 다운로드 받아 포함시켜야 한다.
  // => search.maven.org 에서 'javax.annotation'으로 검색하라.
  //    1.3.2 이상의 버전 사용.
  @Resource(name = "e1")
  //  @Autowired
  //  @Qualifier("e1")
  private Engine engine;

 

 

com.eomcs.spring.ioc.ex09

 

 

객체 자동 생성 - @Component 애노테이션
package com.eomcs.spring.ioc.ex09.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <!-- 애노테이션을 처리할 도우미 객체를 등록한다. -->
    <context:annotation-config/>
    
    <!-- 객체를 생성하기 위해 bean 태그를 사용하지 않고도
         객체를 자동 생성할 수 있다.
         방법은?
         클래스 선언에 @Component 애노테이션을 붙이는 것이다.
         단 이 애노테이션을 처리할 객체를 등록해야 한다.
    -->
    <!-- component-scan 태그는 @Component, @Service, @Repository, @Controller 
         애노테이션이 붙은 클래스를 찾아서 객체를 자동 생성시키도록 명령한다.
       => base-package 속성 
          어느 패키지의 있는 클래스를 찾아서 등록할 것인지 지정하는 속성이다.
       => @Component : 일반 클래스에 대해 붙인다.
       => @Repository : DAO 역할을 수행하는 클래스에 대해 붙인다.
       => @Service : 비즈니스 로직을 수행하는 클래스에 대해 붙인다.
       => @Controller : MVC 구조에서 컨트롤러 역할을 하는 클래스에 대해 붙인다.
       => @RestController : MVC 구조에서 REST API 컨트롤러 역할을 하는 클래스에 대해 붙인다.
       이렇게 역할에 따라 애노테이션으로 클래스를 분류해두면 나중에 통제하기가 편하다. 
    -->
     
    <context:component-scan base-package="com.eomcs.spring.ioc.ex09"/>
</beans>

 

 

객체 자동 생성 - @Component 애노테이션
package com.eomcs.spring.ioc.ex09.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    // <context:annotation-config> 태그 생략하기
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(
        "com/eomcs/spring/ioc/ex09/b/application-context.xml");

    SpringUtils.printBeanList(iocContainer);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex09"/>
    
    <!-- component-scan 태그를 추가하면 
         내부적으로 annotation-config 태그가 자동으로 추가된다. 
         따라서 다음과 같이 생략해도 된다. -->
<!-- 
    <context:annotation-config/> 
-->
</beans>

 

 

객체 자동 생성 - 특정 패키지의 클래스에 대해 객체 생성하지 않기
package com.eomcs.spring.ioc.ex09.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <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>
    
</beans>

 

 

package com.eomcs.spring.ioc.ex09.p1;

import org.springframework.stereotype.Controller;

@Controller
// MVC 아키텍처에서 컨트롤러 객체에 대해 붙인다.
// 개발자가 유지보수할 때 클래스의 역할을
// 더 정확히 이해할 수 있게 도와주기 위해 사용하는 애노테이션이다.
public class Controller1 {

}

 

package com.eomcs.spring.ioc.ex09.p2;

import org.springframework.stereotype.Service;

@Service
// MVC(model-view-controller) 아키텍처에서 model 객체 중에
// 서비스 역할을 수행하는 객체에 대해 붙이는 애노테이션이다.
// @Controller로 마찬가지로 유지보수에 도움을 줄 목적으로 추가된 애노테이션이다.
public class Service1 {

}

 

package com.eomcs.spring.ioc.ex09.p3;

import org.springframework.stereotype.Repository;

@Repository
// MVC(model-view-controller) 아키텍처에서 model 객체 중에
// DAO 역할을 수행하는 객체에 대해 붙이는 애노테이션이다.
// @Controller로 마찬가지로 유지보수에 도움을 줄 목적으로 추가된 애노테이션이다.
public class Repository1 {

}

 

package com.eomcs.spring.ioc.ex09.p4;

import org.springframework.stereotype.Component;
import com.eomcs.spring.ioc.ex09.Car;
import com.eomcs.spring.ioc.ex09.Engine;

@Component
public class Truck extends Car {

  public Truck(Engine engine) {
    super(engine);
  }
}

 

 

package com.eomcs.spring.ioc.ex09;

import java.sql.Date;
import org.springframework.stereotype.Component;

// @Component
// => 스프링 IoC 컨테이너는 이 애노테이션이 붙은 클래스에 대해 객체를 자동 생성한다.
// 문법:
//      @Component(value="객체이름")
//      @Component("객체이름")
// 만약 다음과 같이 객체의 이름을 생략하면
// 클래스 이름을 객체 이름으로 사용한다.
//      예) bitcamp.java106.step09.Car => "car"
// 즉 클래스 이름에서 첫 알파벳을 소문자로 한 이름을
// 객체 이름으로 사용한다.
//
@Component
public class Car {
  String model;
  String maker;
  int cc;
  boolean auto;
  Date createdDate;
  Engine engine;

  // 의존 객체는 생성자에서 주입하는 것이 좋다.
  // 의존 객체라는 말에서 이미 그 객체없이는 작업을 수행할 수 없다는 의미이기 때문에
  // 보통 필수 객체이다.
  // 따라서 생성자에서 필수 객체를 받게 하는 것이 유지보수에 좋다.
  // 즉 의존 객체없이 해당 객체를 생성하는 일을 방지할 수 있기 때문이다.
  public Car(Engine engine) {
    this.engine = engine;
  }

  /*
   * 의존 객체는 작업하는데 사용하라고 생성자를 호출할 때 외부에서 주입하는 객체이기 때문에
   * 셋터나 겟터를 정의할 필요가 없다.
    public Engine getEngine() {
        return engine;
    }
    public void setEngine(Engine engine) {
        this.engine = engine;
    }
   */

 

 

com.eomcs.spring.ioc.ex10

 

 

클래스를 이용하여 스프링 설정하기
package com.eomcs.spring.ioc.ex10.a;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    ApplicationContext iocContainer =
        new AnnotationConfigApplicationContext(AppConfig.class);

    SpringUtils.printBeanList(iocContainer);
  }
}
package com.eomcs.spring.ioc.ex10.a;

import org.springframework.context.annotation.Bean;
import com.eomcs.spring.ioc.ex10.Car;

public class AppConfig {

  // @Component와 같은 애노테이션을 사용할 수 없는 경우
  // Java Config 에서 수동으로 객체를 생성할 수 있다.
  // 방법:
  // 1) 객체를 생성하여 리턴하는 메서드를 정의한다.
  // 2) 그리고 그 메서드에 @Bean 애노테이션을 붙인다.
  //
  // @Bean 애노테이션을 붙이면,
  // 스프링 IoC 컨테이너(AnnotationConfigApplicationContext)는
  // 해당 메서드를 호출하고,
  // 그 메서드가 리턴한 객체를 컨테이너에 보관한다.
  // 컨테이너에 보관할 때 사용할 객체 이름은
  // @Bean(객체이름) 애노테이션에 설정된 이름을 사용한다.
  // 만약 @Bean 애노테이션에 이름이 없으면,
  // 메서드 이름을 객체 이름으로 사용한다.
  //
  @Bean("car") // 애노케이션에 지정한 이름으로 리턴 값을 보관한다.
  public Car getCar2() {
    Car c = new Car(null);
    c.setMaker("비트자동차");
    c.setModel("티코");
    c.setCc(890);
    c.setAuto(true);
    return c;
  }

  @Bean // 이름을 지정하지 않으면 메서드 이름을 사용하여 저장한다.
  public Car getCar() {
    Car c = new Car(null);
    c.setMaker("비트자동차");
    c.setModel("티코");
    c.setCc(890);
    c.setAuto(true);
    return c;
  }

  // 실무에서는 스프링 설정용으로 사용하는 클래스에서
  // 객체를 리턴하는 메서드를 만들 때
  // 그 메서드의 이름을 객체 이름과 같게 짓는다.
  // => 보통 어떤 값을 리턴할 때는 getXxx()라는 이름으로 메서드를 만드는데,
  //    이처럼 객체이름으로 사용할 수 있도록 메서드를 만드는 경우도 있으니
  //    당황하지 말라!
  @Bean
  public Car car2() {
    Car c = new Car(null);
    c.setMaker("비트자동차");
    c.setModel("티코");
    c.setCc(890);
    c.setAuto(true);
    return c;
  }
}

 

 

클래스를 이용하여 스프링 설정하기 - @Configuration 애노테이션
package com.eomcs.spring.ioc.ex10.b;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    // 패키지명을 지정하면
    // 해당 패키지의 모든 클래스를 찾아
    // @Component, @Service, @Controller, @Repository
    // 애노테이션이 붙은 클래스에 대해 객체를 자동 생성한다.
    // 또한,
    // @Configuration 애노테이션이 붙은 클래스를 찾아 객체를 생성한다.
    // 그리고 그 클래스에 @Bean 애노테이션이 붙은 메서드를 호출하여
    // 그 리턴 값을 저장한다.
    //
    ApplicationContext iocContainer = new AnnotationConfigApplicationContext(
        "com.eomcs.spring.ioc.ex10.b");

    SpringUtils.printBeanList(iocContainer);
  }
}
package com.eomcs.spring.ioc.ex10.b;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.eomcs.spring.ioc.ex10.Car;

@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 이다.
//
public class AppConfig {
  @Bean
  public Car car2() {
    Car c = new Car(null);
    c.setMaker("비트자동차");
    c.setModel("티코");
    c.setCc(890);
    c.setAuto(true);
    return c;
  }

  @Bean
  public Car car3() {
    Car c = new Car(null);
    c.setMaker("비트자동차");
    c.setModel("티코");
    c.setCc(890);
    c.setAuto(true);
    return c;
  }
}
package com.eomcs.spring.ioc.ex10.b;

import org.springframework.stereotype.Component;

@Component
public class A {

}
package com.eomcs.spring.ioc.ex10.b;

public class B {

}
package com.eomcs.spring.ioc.ex10.b;

import org.springframework.stereotype.Component;

@Component
public class C {

}

 

 

클래스를 이용하여 스프링 설정하기 - @PropertySource 애노테이션
package com.eomcs.spring.ioc.ex10.c;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    ApplicationContext iocContainer = new AnnotationConfigApplicationContext(
        "com.eomcs.spring.ioc.ex10.c");

    SpringUtils.printBeanList(iocContainer);
  }
}
package com.eomcs.spring.ioc.ex10.c;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import com.eomcs.spring.ioc.ex10.Car;

@Configuration

// @PropertySource 애노테이션
// => .properties 파일을 데이터를 메모리에 로딩하는 일을 한다.
// => 파일 경로가 클래스 경로를 가리킨다면,
//    파일 경로 앞에 "classpath:" 접두어를 붙여라.
@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);
  }
}

jdbc.properties

jdbc.driver=org.mariadb.jdbc.Driver
jdbc.url=jdbc:mariadb://localhost/studydb
jdbc.username=study
jdbc.password=1111

jdbc2.properties

jdbc2.driver=org.mariadb.jdbc.Driver
jdbc2.url=jdbc:mariadb://localhost/studydb2
jdbc2.username=study2
jdbc2.password=1111

 

 

com.eomcs.spring.ioc.ex11

 

 

클래스를 이용하여 스프링 설정하기 - @ComponentScan 사용법
package com.eomcs.spring.ioc.ex11;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    ApplicationContext iocContainer = //
        new AnnotationConfigApplicationContext(AppConfig.class);

    SpringUtils.printBeanList(iocContainer);
  }
}
package com.eomcs.spring.ioc.ex11;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;


// @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
//            )
//    })
// 위의 설정은 XML에서 다음과 같다.
//<context:component-scan base-package="com.eomcs.spring.ioc.ex11">
//  <context:exclude-filter type="regex"
//    expression="com.eomcs.spring.ioc.ex11.p2.*"/>
//  <context:exclude-filter type="annotation"
//    expression="org.springframework.stereotype.Controller"/>
//</context:component-scan>


public class AppConfig {
  @Bean
  public Car car2() {
    Car c = new Car(null);
    c.setMaker("비트자동차");
    c.setModel("티코");
    c.setCc(890);
    c.setAuto(true);
    return c;
  }
}

 

 

com.eomcs.spring.ioc.ex12

 

 

Spring과 Mybatis 연동 : 단순 연동
package com.eomcs.spring.ioc.ex12.a;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex12.Board;

public class Exam01 {

  public static void main(String[] args) {

    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex12/a/application-context.xml");

    SpringUtils.printBeanList(iocContainer);

    BoardDao boardDao = iocContainer.getBean(BoardDao.class);

    // 1) 게시물 입력
    Board board = new Board();
    board.setTitle("제목입니다.");
    board.setContent("내용입니다.");
    boardDao.insert(board);

    // 2) 게시물 목록 조회
    List<Board> list = boardDao.selectList(1, 5);
    for (Board b : list) {
      System.out.printf("%d, %s, %s\n", b.getNo(), b.getTitle(), b.getRegisteredDate());
    }
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex12.a"/>

<!-- Mybatis SqlSessionFactory 준비하기 -->
<!--  
SqlSessionFactory를 만들 때 사용할 설정 파일 읽기 도구를 준비한다.
자바 코드 예:
  InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
        "com/eomcs/spring/ioc/ex12/a/mybatis-config.xml");
-->
    <bean id="mybatisConfigInputStream"
          class="org.apache.ibatis.io.Resources"
          factory-method="getResourceAsStream">
      <constructor-arg 
        value="com/eomcs/spring/ioc/ex12/a/mybatis-config.xml"/>
    </bean>


<!--  
SqlSessionFactory를 만들어주는 공장 객체를 준비한다.
자바 코드 예:
  SqlSessionFactoryBuilder sqlSessionFactoryBuilder = 
    new SqlSessionFactoryBuilder(); 
-->
    <bean id="sqlSessionFactoryBuilder"
          class="org.apache.ibatis.session.SqlSessionFactoryBuilder"/>

<!-- 
BoardDao가 사용할 SqlSessionFactory 객체를 준비한다. 
자바 코드 예:
  SqlSesssionFactory sqlSessionFactory = 
    sqlSessionFactoryBuilder.build(mybatisConfigInputStream);  
-->
    <bean id="sqlSessionFactory"
          factory-bean="sqlSessionFactoryBuilder"
          factory-method="build">
      <constructor-arg ref="mybatisConfigInputStream"/>
    </bean>
</beans>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <properties resource="com/eomcs/spring/ioc/ex12/jdbc.properties"></properties>

  <typeAliases>
    <typeAlias type="com.eomcs.spring.ioc.ex12.Board" alias="Board"/>
  </typeAliases>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
  </environments>
  
  <mappers>
    <mapper resource="com/eomcs/spring/ioc/ex12/a/BoardMapper.xml"/>
  </mappers>
</configuration>

BoardMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="BoardMapper">

  <resultMap type="Board" id="BoardMap">
    <id column="board_id" property="no"/>
    <result column="title" property="title"/>
    <result column="contents" property="content"/>
    <result column="created_date" property="registeredDate"/>
    <result column="view_count" property="viewCount"/>
  </resultMap>
  
  <select id="selectList" resultMap="BoardMap" parameterType="map">
    select 
      board_id, 
      title, 
      contents, 
      created_date 
    from x_board
    order by board_id desc
    limit #{startIndex}, #{pageSize}
  </select>
  
  <insert id="insert" parameterType="Board">
    insert into x_board(title,contents) 
    values(#{title},#{content})
  </insert>
  
  <delete id="delete" parameterType="int">
    delete from x_board 
    where board_id=#{no}
  </delete>
</mapp
package com.eomcs.spring.ioc.ex12.a;

import java.util.HashMap;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Repository;
import com.eomcs.spring.ioc.ex12.Board;

@Repository
public class BoardDao {

  SqlSessionFactory sqlSessionFactory;

  public BoardDao(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
  }

  public List<Board> selectList(int pageNo, int pageSize) {
    HashMap<String,Object> params = new HashMap<>();
    params.put("startIndex", (pageNo - 1) * pageSize);
    params.put("pageSize", pageSize);

    try (SqlSession sqlSession = this.sqlSessionFactory.openSession()) {
      return sqlSession.selectList("BoardMapper.selectList", params);
    }
  }

  public int insert(Board board) {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession()) {
      int count = sqlSession.insert("BoardMapper.insert", board);
      sqlSession.commit();
      return count;
    }
  }

  public int delete(int no) {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession()) {
      int count = sqlSession.delete("BoardMapper.delete", no);
      sqlSession.commit();
      return count;
    }
  }
}

 

 

Spring과 Mybatis 연동 : 설정이 아니라 객체를 통해 SqlSessionFactory 만들기
package com.eomcs.spring.ioc.ex12.b;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex12.Board;

public class Exam01 {

  public static void main(String[] args) {

    ApplicationContext iocContainer =
        new ClassPathXmlApplicationContext("com/eomcs/spring/ioc/ex12/b/application-context.xml");

    SpringUtils.printBeanList(iocContainer);

    BoardDao boardDao = iocContainer.getBean(BoardDao.class);

    // 1) 게시물 입력
    Board board = new Board();
    board.setTitle("제목입니다.");
    board.setContent("내용입니다.");
    boardDao.insert(board);

    // 2) 게시물 목록 조회
    List<Board> list = boardDao.selectList(1, 5);
    for (Board b : list) {
      System.out.printf("%d, %s, %s\n", b.getNo(), b.getTitle(), b.getRegisteredDate());
    }
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex12.b"/>

<!-- Mybatis SqlSessionFactory 준비하기 
     자바 코드:
     SqlSessionFactoryFactoryBean factory = new SqlSessionFactoryFactoryBean();
     factory.setConfigLocation("com/eomcs/spring/ioc/ex12/b/mybatis-config.xml");
     Object obj = factory.getObject();
     iocContainer.put("sqlSessionFactory", obj);
-->
    <bean id="sqlSessionFactory"
          class="com.eomcs.spring.ioc.ex12.b.SqlSessionFactoryFactoryBean">
      <property name="configLocation" 
                value="com/eomcs/spring/ioc/ex12/b/mybatis-config.xml"/>
    </bean>
</beans>
package com.eomcs.spring.ioc.ex12.b;

import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.beans.factory.FactoryBean;

public class SqlSessionFactoryFactoryBean implements FactoryBean<SqlSessionFactory> {

  String configLocation;

  public void setConfigLocation(String configLocation) {
    this.configLocation = configLocation;
  }

  @Override
  public SqlSessionFactory getObject() throws Exception {
    InputStream in = Resources.getResourceAsStream(configLocation);
    return new SqlSessionFactoryBuilder().build(in);
  }

  @Override
  public Class<?> getObjectType() {
    return SqlSessionFactory.class;
  }
}

 

 

Spring과 Mybatis 연동 : mybatis에서 제공해주는 spring 연동 라이브러리 사용하기
package com.eomcs.spring.ioc.ex12.c;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex12.Board;

public class Exam01 {

  public static void main(String[] args) {
    // 우리가 직접 스프링 IoC 컨테이너에서 사용할
    // SqlSessionFactory 빌더 클래스를 만들 필요가 없다.
    // mybatis 팀에서 제공해주는 FactoryBean을 사용하자!
    // 1) mvnrepository.com 에서 mybatis-spring 라이브러리 검색
    // 2) "build.gradle" 에 의존 라이브러리 정보 추가
    // 3) "gradle eclipse" 실행
    // 4) 이클립스 프로젝트 갱신
    // 5) 이 라이브러리에서 제공하는 FactoryBean 사용!
    //
    // mybatis의 SqlSessionFactoryBean 클래스에서 사용할 DataSource 구현체를 추가한다.
    // => Apache의 DataSource 구현체를 사용한다면,
    //    - apache 'commons-dbcp' 라이브러리를 추가해야 한다.
    // => Spring의 DataSource 구현체를 사용한다면,
    //    - 'spring-jdbc' 라이브러리를 추가해야 한다.
    //
    ApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex12/c/application-context.xml");

    SpringUtils.printBeanNames(iocContainer);

    BoardDao boardDao = iocContainer.getBean(BoardDao.class);

    // 1) 게시물 입력
    Board board = new Board();
    board.setTitle("제목입니다.");
    board.setContent("내용입니다.");
    boardDao.insert(board);

    // 2) 게시물 목록 조회
    List<Board> list = boardDao.selectList(1, 5);
    for (Board b : list) {
      System.out.printf("%d, %s, %s\n", b.getNo(), b.getTitle(), b.getRegisteredDate());
    }
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex12.c"/>

<!-- Mybatis SqlSessionFactory 준비하기 
  => 보통 클래스 이름이 XxxFactoryBean 이라면 
     생성되는 객체는 Xxx 이다.
  => 그런데 SqlSessionFactoryBean 클래스가 생성하는 객체는 
     SqlSession 이 아니라 SqlSessionFactory 이다.
  => 왜?
     SqlSessionFactory 객체를 만드는 클래스는 
     이름을 SqlSessionFactoryFactoryBean 이라고 짓는 것이 일반적인데,
     Mybatis 개발자는 이름 중간에 Factory가 두 번 들어가는 것이 싫었던 가보다.
-->
    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">

      <!-- mybatis 설정 파일의 경로를 프로퍼티에 저장한다. -->
      <!--  
      <property name="configLocation">
        <bean class="org.springframework.core.io.ClassPathResource">
          <constructor-arg value="com/eomcs/spring/ioc/ex12/c/mybatis-config.xml"/>
        </bean>
      </property>
      -->
      
      <!-- mybatis를 스프링과 연동하면, 
           DataSource는 스프링에서 관리하는 객체를 사용해야 한다.
           mybatis가 만든 DataSource를 사용할 수 없다. --> 
      <property name="dataSource" ref="dataSource" />

      <!-- SQL 맵퍼 파일이 있는 경로를 설정하면, 
           SqlSessionFactory에서 그 경로에서 SQL 맵퍼 파일을 찾을 것이다. -->           
      <property name="mapperLocations" 
                value="classpath*:com/eomcs/spring/ioc/ex12/c/*Mapper.xml" />  

      <!-- Board 클래스 처럼 데이터를 담는 객체(Value Object = Domain Object)의 
           별명을 자동으로 등록하기
           => 패키지를 지정하면 그 패키지에 있는 모든 클래스에 대해 
              클래스 이름을 별명으로 자동 부여한다. --> 
      <property name="typeAliases" 
                value="com.eomcs.spring.ioc.ex12.Board"/> 
    </bean>
    
<!-- mybatis가 사용할 DataSource 객체 준비하기
     => DataSource? 
        - DB 커넥션 풀의 기능을 수행한다. 
     => Spring 프레임워크에서 제공하는 DataSource 구현체를 사용할 때
        - DriverManagerDataSource 클래스가 들어있는 
          spring-jdbc 라이브러리를 추가해야 한다.
     => Apache 에서 제공하는 DataSource 구현체를 사용할 때
        - BasicDataSource 클래스가 들어 있는 
          apache-commons-dbcp 라이브러리를 추가해야 한다.
-->
  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>

	
<!-- 스프링 설정 파일에서 사용할 .properties 파일을 로딩하는 객체 준비 -->
  <context:property-placeholder 
      location="com/eomcs/spring/ioc/ex12/jdbc.properties"/>	
</beans>

 

 

Spring과 Mybatis 연동 : mybatis가 구현한 DAO 객체 사용하기
package com.eomcs.spring.ioc.ex12.d;

import java.util.HashMap;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex12.Board;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    BoardDao boardDao = iocContainer.getBean(BoardDao.class);

    //
    // //1) 게시물 입력
    Board board = new Board();
    board.setTitle("제목입니다.");
    board.setContent("내용입니다.");
    boardDao.insert(board);
    //
    // //2) 게시물 목록 조회
    // // => selectList()의 파라미터 값을 한 개만 넘겨야 하기 때문에
    // // 여러 개의 값을 넣고 싶으면 Map에 담아 넘긴다.
    HashMap<String,Object> params = new HashMap<>();
    params.put("startIndex", 0);
    params.put("pageSize", 5);
    //
    List<Board> list = boardDao.selectList(params);
    for (Board b : list) {
      System.out.printf("%d, %s, %s\n",
          b.getNo(),
          b.getTitle(),
          b.getRegisteredDate());
    }
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex12.d"/>

    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="mapperLocations" 
                value="classpath*:com/eomcs/spring/ioc/ex12/d/*Mapper.xml" />  
      
      <!-- 도메인 클래스의 패키지를 지정한다.
           그러면 해당 패키지의 전체 클래스에 대해 별명이 부여된다. -->
      <!--  
      <property name="typeAliasesPackage" 
                value="com.eomcs.spring.ioc.ex12"/>
      -->
      
      <!-- 도메인 클래스를 낱개로 지정할 때는 
          다음의 프로퍼티를 설정한다. -->   
      <property name="typeAliases" 
                value="com.eomcs.spring.ioc.ex12.Board"/>
    </bean>
    
	<bean id="dataSource" 
	      class="org.apache.commons.dbcp.BasicDataSource" 
	      destroy-method="close">
	    <property name="driverClassName" value="${jdbc.driver}" />
	    <property name="url" value="${jdbc.url}" />
	    <property name="username" value="${jdbc.username}" />
	    <property name="password" value="${jdbc.password}" />
	</bean>
	
    <context:property-placeholder 
        location="com/eomcs/spring/ioc/ex12/jdbc.properties"/>	
        
<!-- DAO 구현체를 자동으로 만들어 주는 객체를 준비
     => 개발자는 DAO를 직접 구현할 필요가 없다.
     => 단지 인터페이스만 선언하면 된다.
 -->
  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- DAO 인터페이스가 선언된 패키지를 지정한다. -->
    <property name="basePackage" 
              value="com.eomcs.spring.ioc.ex12.d" />
    <!-- DAO 인터페이스의 구현체를 자동으로 생성되게 하려면 
         다음의 규칙에 따라 인터페이스와 SQL 맵퍼 파일을 작성해야 한다.
         1) 인터페이스 작성 규칙
            - 메서드명은 SQL 맵퍼 파일의 SQL ID와 일치해야 한다.
            - 메서드의 파라미터 개수와 타입은 SQL 맵퍼 파일의 SQL 문과 일치해야 한다. 
            - 메서드의 리턴 타입은 
              => insert/update/delete 인 경우, void 또는 int로 설정하면 된다.
              => select 인 경우, List 또는 SQL 문의 리턴 타입과 같으면 된다.
         2) SQL 맵퍼 작성 규칙
            - namespace 속성의 값은 인터페이스의 전체 이름과 일치해야 한다.
            - SQL ID는 인터페이스의 메서드 이름과 같아야 한다.
            - SQL 문의 parameterType은 메서드의 파라미터 타입과 일치해야 한다.
            - SQL 문의 resultType은 메서드의 리턴 타입과 일치해야 한다.
              단 void 일 경우 상관없다.-->
  </bean>    
</beans>

application-context2.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://mybatis.org/schema/mybatis-spring
        http://mybatis.org/schema/mybatis-spring.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex12.d"/>

    <bean id="sqlSessionFactory"
          class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="mapperLocations" 
                value="classpath*:com/eomcs/spring/ioc/ex12/d/*Mapper.xml" />  
      
      <!-- 도메인 클래스의 패키지를 지정한다.
           그러면 해당 패키지의 전체 클래스에 대해 별명이 부여된다. -->
      <!--  
      <property name="typeAliasesPackage" 
                value="com.eomcs.spring.ioc.ex12"/>
      -->
      
      <!-- 도메인 클래스를 낱개로 지정할 때는 
          다음의 프로퍼티를 설정한다. -->   
      <property name="typeAliases" 
                value="com.eomcs.spring.ioc.ex12.Board"/>
    </bean>
    
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
	      destroy-method="close">
	    <property name="driverClassName" value="${jdbc.driver}" />
	    <property name="url" value="${jdbc.url}" />
	    <property name="username" value="${jdbc.username}" />
	    <property name="password" value="${jdbc.password}" />
	</bean>
	
  <context:property-placeholder 
        location="com/eomcs/spring/ioc/ex12/jdbc.properties"/>	
        
<!-- DAO 구현체를 자동으로 만들어 주는 객체를 준비 II
     => 개발자는 DAO를 직접 구현할 필요가 없다.
     => 단지 인터페이스만 선언하면 된다.
 -->
  <mybatis:scan base-package="com.eomcs.spring.ioc.ex12.d" />
</beans>

BoardMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="com.eomcs.spring.ioc.ex12.d.BoardDao">

  <resultMap type="Board" id="BoardMap">
    <id column="board_id" property="no"/>
    <result column="title" property="title"/>
    <result column="contents" property="content"/>
    <result column="created_date" property="registeredDate"/>
    <result column="view_count" property="viewCount"/>
  </resultMap>
  
  <select id="selectList" resultMap="BoardMap" parameterType="map">
    select 
      board_id, 
      title, 
      contents, 
      created_date 
    from x_board
    order by board_id desc
    limit #{startIndex}, #{pageSize}
  </select>
  
  <insert id="insert" parameterType="Board">
    insert into x_board(title,contents) 
    values(#{title},#{content})
  </insert>
  
  <delete id="delete" parameterType="int">
    delete from x_board 
    where board_id=#{no}
  </delete>
</mapper>
package com.eomcs.spring.ioc.ex12.d;

import java.util.List;
import java.util.Map;
import com.eomcs.spring.ioc.ex12.Board;

// 이 인터페이스의 구현체를 mybatis가 자동으로 생성하여
// Spring IoC 컨테이너에 등록할 것이다.
// 그러려면 인터페이스를 정의할 때 몇가지 규칙을 따라야 한다.
// 1) 맵퍼 파일의 namespace와 인터페이스명(패키지포함)과 같아야 한다.
//      예) <mapper namespace="com.eomcs.spring.ioc.ex12.d.BoardDao">
// 2) 메서드의 이름과 SQL 아이디가 같아야 한다.
// 3) 메서드의 리턴 타입과 SQL 맵퍼 파일의
//    resultType 또는 resultMap이 같아야 한다.
//    insert, update, delete일 경우
//    리턴 값이 void 또는 int가 될 수 있다.
// 4) SQL 맵퍼는 파라미터 값을 한 개만 받기 때문에
//    DAO 인터페이스의 메서드도 최대 한 개의 파라미터를 가져야 한다.
//
public interface BoardDao {
  List<Board> selectList(Map<String, Object> map);

  int insert(Board board);

  int delete(int no);
}

 

 

Spring과 Mybatis 연동 : Java Config로 설정하기
package com.eomcs.spring.ioc.ex12.e;

import java.util.HashMap;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;
import com.eomcs.spring.ioc.ex12.Board;

public class Exam01 {

  public static void main(String[] args) {
    ApplicationContext iocContainer =
        new AnnotationConfigApplicationContext(AppConfig.class);
    //new AnnotationConfigApplicationContext("com.eomcs.spring.ioc.ex12.e");

    SpringUtils.printBeanList(iocContainer);

    BoardDao boardDao = iocContainer.getBean(BoardDao.class);

    // 1) 게시물 입력
    Board board = new Board();
    board.setTitle("제목입니다.");
    board.setContent("내용입니다.");
    boardDao.insert(board);

    // 2) 게시물 목록 조회
    // => selectList()의 파라미터 값을 한 개만 넘겨야 하기 때문에
    // 여러 개의 값을 넣고 싶으면 Map에 담아 넘긴다.
    HashMap<String, Object> params = new HashMap<>();
    params.put("startIndex", 0);
    params.put("pageSize", 5);

    List<Board> list = boardDao.selectList(params);
    for (Board b : list) {
      System.out.printf("%d, %s, %s\n", b.getNo(), b.getTitle(), b.getRegisteredDate());
    }
  }
}
package com.eomcs.spring.ioc.ex12.e;

import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import com.eomcs.spring.ioc.ex12.Board;


@PropertySource("classpath:com/eomcs/spring/ioc/ex12/jdbc.properties")

// Mybatis DAO 프록시를 자동생성할 인터페이스를 지정하기
@MapperScan("com.eomcs.spring.ioc.ex12.e")
public class AppConfig {

  @Bean
  public DataSource dataSource(
      @Value("${jdbc.driver}") String jdbcDriver,
      @Value("${jdbc.url}") String jdbcUrl,
      @Value("${jdbc.username}") String jdbcUsername,
      @Value("${jdbc.password}") String jdbcPassword) {
    BasicDataSource ds = new BasicDataSource();
    ds.setDriverClassName(jdbcDriver);
    ds.setUrl(jdbcUrl);
    ds.setUsername(jdbcUsername);
    ds.setPassword(jdbcPassword);
    return ds;
  }

  @Bean
  public PlatformTransactionManager transactionManager(
      DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
  }

  @Bean
  public SqlSessionFactory sqlSessionFactory(
      DataSource dataSource, // DB 커넥션풀
      ApplicationContext appCtx // Spring IoC 컨테이너
      ) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    // sqlSessionFactoryBean.setTypeAliasesPackage("com.eomcs.spring.ioc.ex12");
    sqlSessionFactoryBean.setTypeAliases(Board.class);
    sqlSessionFactoryBean.setMapperLocations(
        appCtx.getResources("classpath:com/eomcs/spring/ioc/ex12/e/*Mapper.xml"));
    return sqlSessionFactoryBean.getObject();
  }
}

 

 

com.eomcs.spring.ioc.ex13

 

 

AOP(Aspect-Oriented Programming) - AOP 필터 적용 전
package com.eomcs.spring.ioc.ex13.a.step1;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.a.step1"/>
    
</beans>
package com.eomcs.spring.ioc.ex13.a.step1;

import org.springframework.stereotype.Component;

@Component
public class X {
  public X() {
    System.out.println("X 객체 생성됨");
  }

  public void m1() {
    System.out.println("X.m1()");
  }
}

 

 

AOP(Aspect-Oriented Programming) - AOP 필터 적용 전 : X.m1()의 실행 시간 측정하기
package com.eomcs.spring.ioc.ex13.a.step2;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    // 기존 메서드에 기능을 추가하는 고전적인 방식
    // => m1() 메서드 안에 직접 코드를 추가한다.
    // => 또는 외부 클래스에 추가할 코드를 작성해 놓고 m1()에서 호출한다.
    // => 어떤 방법을 사용하든 m1()에 기능을 추가하려면 m1()을 편집해야 한다.
    //

    ClassPathXmlApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex13/a/step2/application-context.xml");

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
  }
}
package com.eomcs.spring.ioc.ex13.a.step2;

import org.springframework.stereotype.Component;

@Component
public class X {
  public X() {
    System.out.println("X 객체 생성됨");
  }

  public void m1() {
    // m1() 메서드를 실행하기 전에 기능을 추가하기
    // => 다음과 같이 직접 해당 기능을 수행하는 코드를 삽입한다.
    long startTime = System.currentTimeMillis();
    System.out.println("시간 측정 시작!");

    // 이렇게 어떤 메서드를 실행하기 전이나 후에 기능을 추가하고 싶다면 
    // 해당 메서드를 찾아가서 코드를 편집해야 한다.
    // 
    // 이 고전적인 방식의 문제점?
    // 1) 원래 코드에 손을 대야 하기 때문에 유지보수에 좋지 않다.
    //    변경하면 또 기존 코드를 손대야 한다.
    // 2) 유사한 일을 하는 메서드에 같은 기능을 삽입할 때 여러 곳을 편집해야 하기 때문에 유지보수가 힘들다.
    // 3) 추가한 기능을 걷어 낼 때도 매우 번거롭다.
    // 
    // 예) DAO 객체의 메서드들을 실행하는데 걸리는 시간을 측정하기
    // 예) 특정 메서드가 실행될 때 로그를 남기기
    // 예) 모든 메서드의 리턴 값을 특정 타입으로 변환하기
    // 

    System.out.println("X.m1()");

    //시간 측정에 유효한 결과를 출력하기 위해 작업 실행 시간을 좀 늘린다.
    for (int i = 0; i < 1000000; i++) {
      double temp = 3.14159 / Math.random();
    }

    // => 다음과 같이 직접 해당 기능을 수행하는 코드를 삽입한다.
    long endTime = System.currentTimeMillis();
    System.out.println("시간 측정 종료!");
    System.out.println("걸린 시간: " + (endTime - startTime));
  }
}

 

 

AOP(Aspect-Oriented Programming) - AOP 필터 적용 후 : X.m1()의 실행 시간 측정하기
package com.eomcs.spring.ioc.ex13.a.step3;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    // 기존 코드를 손대지 않고 기능을 추가하기 
    // => m1() 메서드 안에 삽입할 코드를 별도의 클래스로 정의한다.
    // => 해당 기능을 m1() 메서드 전후, 언제 삽입할 것인지 설정한다.
    //

    ClassPathXmlApplicationContext iocContainer = new ClassPathXmlApplicationContext(//
        "com/eomcs/spring/ioc/ex13/a/step3/application-context.xml");

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
  }
}

// AOP 기술을 이용한 필터 적용 방법
// 1) AOP 관련 의존 라이브러리를 프로젝트에 적용한다.
//   - search.maven.org 에서 "aspectjweaver"로 검색한다.
//   - build.gradle 에 등록
//   - "gradle eclipse" 실행
//   - 프로젝트 리프래시.
// 2) 필터 클래스를 정의한다.
//   - MyAdvice 클래스 추가
// 3) XML 또는 자바 코드로 필터를 설정한다.
//   - application-context.xml 변경

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.a.step3"/>
    
    <!-- AOP 필터 객체 생성 -->
    <bean id="myAdvice" 
          class="com.eomcs.spring.ioc.ex13.a.step3.MyAdvice"/> 
    
    <!-- AOP 적용 -->
    <aop:config>
        <!-- 어느 pointcut에 어떤 advice를 끼워 넣을 지 설정한다. -->
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 필터를 끼워 넣을 위치를 지정한다. -->
            <aop:pointcut id="pointcut1"
                expression="execution(* com.eomcs.spring.ioc.ex13.a.step3.X.m1(..))"/>
            
            <!-- 해당 포인트컷을 실행하기 전에 필터의 어떤 메서드를 먼저 실행할 것인지 지정한다. -->
            <aop:before pointcut-ref="pointcut1" method="advice1"/>
            
            <!-- 해당 포인트컷을 실행한 후에 필터의 어떤 메서드를 먼저 실행할 것인지 지정한다. -->
            <aop:after pointcut-ref="pointcut1" method="advice2"/>
            
        </aop:aspect>
    </aop:config>
</beans>
// 특정 메서드 호출 전후에 실행되는 클래스
package com.eomcs.spring.ioc.ex13.a.step3;

public class MyAdvice {
  long startTime;

  // m1() 메서드를 실행하기 전에 수행할 기능을 정의한다.
  public void advice1() {
    System.out.println("MyAdvice.advice1()");

    startTime = System.currentTimeMillis();
    System.out.println("시간 측정 시작!");
  }

  // m1() 메서드를 실행한 후에 수행할 기능을 정의한다.
  public void advice2() {
    System.out.println("MyAdvice.advice2()");

    long endTime = System.currentTimeMillis();
    System.out.println("시간 측정 종료!");
    System.out.println("걸린 시간: " + (endTime - startTime));
  }
}

 

 

AOP(Aspect-Oriented Programming) - AOP 필터를 적용하는 방법
package com.eomcs.spring.ioc.ex13.b;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

// AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)?
// => 기존의 코드를 손대지 않고 특정 기능을 삽입하는 기술
// => 메서드 호출 앞뒤로 코드를 삽입할 수 있다.
// => 일종의 필터 기술이다.
// => 패턴을 이용하여 여러 클래스나 메서드에 집단적으로 기능을 추가하거나 뺄 수 있다.
//
// AOP 구동 원리
// => AOP는 "proxy 디자인 패턴"을 사용한다.
// => 즉 AOP를 설정하면, 
//    Spring IoC 컨테이너는 point-cut에 지정된 클래스에 대해 Proxy 클래스를 생성한다.
//    그리고 그 Proxy 객체를 IoC 컨테이너에 저장한다.
// => IoC 컨테이너에서 point-cut 에 지정한 객체를 꺼내면 
//    원래의 객체 대신 프록시 객체가 리턴된다.
// => 프록시 객체에 대해 메서드를 호출하면, 
//    AOP에 설정한 대로 Advice 메서드를 호출한 후 원래의 메서드를 호출한다.

// AOP 기술 사용 방법:
// 1) AOP 관련 의존 라이브러리 프로젝트에 적용한다.
// - search.maven.org 에서 "aspectjweaver"로 검색한다.
// - build.gradle 에 등록
// - '$ gradle eclipse' 실행
// - 이클립스 프로젝트 리프래시.
// 2) 필터 역할을 수행할 클래스를 정의한다.
// 3) 스프링 설정(XML 또는 자바 코드)으로 필터를 적용한다.
//
// AOP 용어:
// [Advice]
// => 메서드(join point) 호출 앞뒤에 삽입될 코드를 담고 있는 필터 객체이다.
// 예) MyAdvice 클래스
//
// [Join Point]
// => Advice가 삽입될 메서드이다.
// 예) X.m1() 메서드
//
// [Pointcut]
// => Advice를 삽입할 위치 정보이다.
// => 즉 Join Point를 가리키는 패턴 정보이다.
// 예) execution(* com.eomcs.spring.ioc.ex13.b.*.*(..))
//
// [Target Object]
// => Advice를 삽입할 대상 객체이다.
// 예) X 클래스
//
// [Aspect]
// => 어느 pointcut 에 어떤 advice를 삽입할 것인지 가리키는 정보이다.
// 예) 설정 정보
//
public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
    System.out.println("-----------------------");

    x.m2();
    System.out.println("-----------------------");

    x.m3();
  }

}
package com.eomcs.spring.ioc.ex13.b;

import org.springframework.stereotype.Component;

@Component
public class X {
  public X() {
    System.out.println("X 객체 생성됨");
  }

  public void m1() {
    System.out.println("X.m1()");
  }

  public void m2() {
    System.out.println("X.m2()");
  }

  public void m3() {
    System.out.println("X.m3()");
  }
}

application-context-01.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.b"/>
    
    <!-- AOP 필터 객체 생성 
       => 충고를 수행할 객체를 준비한다. 
       => Pointcut 패턴에 해당하는 메서드를 호출할 때
          호출 전후에 삽입될 객체이다.
    -->
    <bean id="myAdvice" 
          class="com.eomcs.spring.ioc.ex13.b.MyAdvice"/> 
    
    <!-- AOP 설정하기 -->
    <aop:config>
    
        <!-- myAdvice 필터 설정 -->
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 먼저 myAdvice 필터를 끼워 넣을 위치를 지정한다. 
                => 메서드를 명확하게 지정할 수도 있고,
                => 메서드에 대한 패턴을 지정할 수 있다.
            -->
            <aop:pointcut id="pointcut1"
                expression="execution(* com.eomcs.spring.ioc.ex13.b.X.m1(..))"/>
            
            <!-- 포인트컷에 해당하는 메서드를 실행할 때 
                 어떤 시점에 어떤 메서드를 삽입할 것인지 설정한다.
            -->
            <aop:before pointcut-ref="pointcut1" method="advice1"/>
            
        </aop:aspect>
    </aop:config>
</beans>
// 특정 메서드 호출 전후에 실행되는 클래스
package com.eomcs.spring.ioc.ex13.b;

public class MyAdvice {
  // 삽입될 메서드를 정의한다.
  public void advice1() {
    System.out.println("MyAdvice.advice1()");
  }

  public void advice2() {
    System.out.println("MyAdvice.advice2()");
  }
}

 

 

AOP(Aspect-Oriented Programming) - AOP 필터를 적용하는 방법
package com.eomcs.spring.ioc.ex13.b;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam02 {

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

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
    System.out.println("-----------------------");

    x.m2();
    System.out.println("-----------------------");

    x.m3();
  }
}

application-context-02.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.b"/>
    
    <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.b.MyAdvice"/> 
    
    <aop:config>
    
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 포인트컷과 메서드를 바로 연결할 수 있다. -->
            <aop:before pointcut="execution(* com.eomcs.spring.ioc.ex13.b.*.m1(..))" 
                        method="advice2"/>
            
        </aop:aspect>
    </aop:config>
</beans>

 

 

AOP(Aspect-Oriented Programming) - AOP 필터를 적용하는 방법
package com.eomcs.spring.ioc.ex13.b;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam03 {

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

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
    System.out.println("-----------------------");

    x.m2();
    System.out.println("-----------------------");

    x.m3();
  }
}

application-context-03.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.b"/>
    
    <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.b.MyAdvice"/> 
    
    <aop:config>
    
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 한 포인트 컷에 여러 개의 필터 꼽기(advice 지정하기)  -->
            <aop:before pointcut="execution(* com.eomcs.spring.ioc.ex13.b.*.m1(..))" 
                        method="advice1"/>
            <aop:before pointcut="execution(* com.eomcs.spring.ioc.ex13.b.*.m2(..))" 
                        method="advice2"/>            
            
        </aop:aspect>
    </aop:config>
</beans>

 

 

AOP(Aspect-Oriented Programming) - AOP 필터를 적용하는 방법
package com.eomcs.spring.ioc.ex13.b;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam04 {

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

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
    System.out.println("-----------------------");

    x.m2();
    System.out.println("-----------------------");

    x.m3();
  }
}

application-context-04.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.b"/>
    
    <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.b.MyAdvice"/> 
    
    <aop:config>
    
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 한 포인트컷에 여러 필터를 꼽을 때는 
                 포인터컷을 별도로 정의하여 재사용하는 것이 좋다  -->
            <aop:pointcut id="pointcut1"
                        expression="execution(* com.eomcs.spring.ioc.ex13.b.*.m2(..))" />
            
            <aop:before pointcut-ref="pointcut1" 
                        method="advice1"/>
            <aop:before pointcut-ref="pointcut1" 
                        method="advice2"/>            
            
        </aop:aspect>
    </aop:config>
</beans>

 

 

AOP(Aspect-Oriented Programming) - AOP 필터를 적용하는 방법
package com.eomcs.spring.ioc.ex13.b;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam05 {

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

    SpringUtils.printBeanList(iocContainer);

    X x = iocContainer.getBean(X.class);
    x.m1();
    System.out.println("-----------------------");

    x.m2();
    System.out.println("-----------------------");

    x.m3();
  }
}

application-context-05.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.b"/>
    
    <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.b.MyAdvice"/> 
    <bean id="myAdvice2" class="com.eomcs.spring.ioc.ex13.b.MyAdvice2"/>
    
    <aop:config>

        <!-- 포인트컷을 여러 aspect에서 재사용 할 수 있다. -->
        <aop:pointcut id="pointcut1"
                    expression="execution(* com.eomcs.spring.ioc.ex13.b.*.m2(..))" />

        <aop:aspect id="aspect1" ref="myAdvice">
            <aop:before pointcut-ref="pointcut1" 
                        method="advice1"/>
        </aop:aspect>
        
        <aop:aspect id="aspect2" ref="myAdvice2">
            <aop:before pointcut-ref="pointcut1" 
                        method="hahaAdvice"/>
        </aop:aspect>
    </aop:config>
</beans>

 

 

AOP(Aspect-Oriented Programming) - AOP 필터 적용 위치
package com.eomcs.spring.ioc.ex13.c;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    try {
      Caller caller = iocContainer.getBean(Caller.class);
      caller.test();
      // 실제 test()에서 호출하는 m1()은
      // X의 프록시 객체에 정의되어 있는 m1()을 호출하는 것이다.
      //
      // X의 프록시 클래스가 다음과 같이 정의되었을 것으로 짐작할 수 있다.
      // 
      //      class XProxy extends X {
      //        X original;
      //        MyAdvice myAdvice;
      //
      //        public XProxy(X original, MyAdvice myAdvice) {
      //          this.original = original;
      //          this.myAdvice = myAdvice;
      //        }
      //
      //        @Override
      //        public int m1(int a, int b) {
      //          int result = 0;
      //          myAdvice.doBefore();
      //          try {
      //            try {
      //              result = original.m1(a, b);
      //            } catch (Exception e) {
      //              throw e;
      //            } finally {
      //              myAdvice.doAfter();
      //            }
      //            myAdvice.doAfterReturning();
      //          } catch (Exception e) {
      //            myAdvice.doAfterThrowing();
      //          }
      //          return result;
      //        }
      //      }
      //
    } catch (Exception e) {
      System.err.println("오류 발생!");
    }
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.c"/>
    
    <!-- AOP 필터 객체 생성 -->
    <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.c.MyAdvice"/> 
    
    <!-- AOP 적용 -->
    <aop:config>
        <!-- 어느 pointcut에 어떤 advice를 끼워 넣을 지 설정한다. -->
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 필터를 끼워 넣을 위치를 지정한다. -->
            <aop:pointcut id="pointcut1"
                expression="execution(* com.eomcs.spring.ioc.ex13.c.X.*(..))"/>
            
            <!-- 해당 포인트컷 앞뒤로 필터를 꼽는다. -->
            
            <!-- 메서드 호출 전 -->
            <aop:before pointcut-ref="pointcut1" method="doBefore"/>
            
            <!-- 메서드 실행 후 
                 => 정상적으로 실행하든, 예외가 발생하든 상관없이 반드시 실행된다.
                 => 선언된 순서에 따라 실행된다. 
                    즉 after-returning 을 먼저 설정했다면, 
                    doAfterReturning()이 호출된 후 doAfter()가 호출된다.
                    after를 먼저 설정했다면,
                    doAfter()가 호출된 후 doAfterReturning()이 호출된다.
                 => 따라서 어드바이스 실행 순서에 상관없이
                    정상적으로 실행되었든 비정상적으로 실행되었든 간에 
                    메서드가 호출된 후 반드시 실행 해야 할 작업이 있다면
                    after 로 설정하라! 
            -->
            <aop:after pointcut-ref="pointcut1" method="doAfter"/>
            
            <!-- 메서드 값 리턴 후 -->
            <aop:after-returning pointcut-ref="pointcut1" method="doAfterReturning"/>
            
            <!-- 메서드에서 예외를 던졌을 때 -->
            <aop:after-throwing pointcut-ref="pointcut1" method="doAfterThrowing"/>
            
            <!-- <aop:around pointcut-ref="pointcut1" method="doAround"/> -->
        </aop:aspect>
    </aop:config>
</beans>
package com.eomcs.spring.ioc.ex13.c;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Caller {

  @Autowired X x;

  public void test() {
    System.out.println("test()..... 시작");
    System.out.println("-------------------------------");

    int result = x.m1(10, 2);
    System.out.printf("result: %d\n", result);
    System.out.println("-------------------------------");

    result = x.m1(10, 0);
    System.out.printf("result: %d\n", result);
    System.out.println("-------------------------------");

    System.out.println("test()..... 끝");
  }
}
package com.eomcs.spring.ioc.ex13.c;

import org.springframework.stereotype.Component;

@Component 
public class X {
  public X() {
    System.out.println("X 객체 생성됨");
  }
  public int m1(int a, int b) {
    System.out.println("X.m1()");
    int result = a / b;

    return result; 
  }
}
// 특정 메서드 호출 전후에 실행되는 클래스
package com.eomcs.spring.ioc.ex13.c;

public class MyAdvice {

  public void doBefore() {
    System.out.println("MyAdvice.doBefore()");
  }

  public void doAfter() {
    System.out.println("MyAdvice.doAfter()");
  }

  public void doAfterReturning() {
    System.out.println("MyAdvice.doAfterReturning()");
  }

  public void doAfterThrowing() {
    System.out.println("MyAdvice.doAfterThrowing()");
  }

  public void doAround() {
    System.out.println("MyAdvice.doAround()");
  }
}

 

 

AOP(Aspect-Oriented Programming) - AOP 필터에서 타겟 객체의 메서드를 호출하기 전에 파라미터를 받거나 리턴 값을 받기
package com.eomcs.spring.ioc.ex13.d;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    try {
      Caller caller = iocContainer.getBean(Caller.class);
      caller.test();
    } catch (Exception e) {
      System.err.println("오류 발생!");
    }
  }
}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.d"/>
    
    <!-- AOP 필터 객체 생성 -->
    <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.d.MyAdvice"/> 
    
    <!-- AOP 적용 -->
    <aop:config>
        <!-- 어느 pointcut에 어떤 advice를 끼워 넣을 지 설정한다. -->
        <aop:aspect id="aspect1" ref="myAdvice">
            
            <!-- 메서드 호출 전 : 파라미터 값을 미리 받기 
                 args(원본 메서드의 파라미터 값과 advice의 파라미터 변수를 연결)
                 => 원본 메서드: m1(int a, int b)
                    advice 메서드: doBefore(int x, int y)
                 => 원본 메서드의 파라미터 값과 advice 메서드의 파라미터를 연결
	                a > y,
	                b > x,
	                op > p1,
	                그래서 값이 들어갈 파라미터 이름을 순서대로 나열한다.
	                args(y,x) 
            -->
            <aop:before 
                pointcut="execution(* com.eomcs.spring.ioc.ex13.d.X.*(..)) and args(y,x)" 
                method="doBefore"/>
            
            <!-- 메서드를 호출한 후 그 메서드가 리턴한 값 받기 
                 => 메서드의 리턴 값을 받을 advice의 파라미터 이름을 지정한다. 
                    advice 메서드: doAfterReturning(Object returnValue)
            -->
            <aop:after-returning 
                pointcut="execution(* com.eomcs.spring.ioc.ex13.d.X.*(..))" 
                method="doAfterReturning"
                returning="returnValue"/>
                
            <!-- 메서드를 호출할 때 예외가 발생했다면 그 예외 정보를 받기 
                 => 예외 객체를 advice의 어떤 파라미터가 받을 지 이름을 지정한다.
                    advice메서드: doAfterThrowing(Exception error) 
            -->
            <aop:after-throwing 
                pointcut="execution(* com.eomcs.spring.ioc.ex13.d.X.*(..))" 
                method="doAfterThrowing"
                throwing="error"/>    
        </aop:aspect>
    </aop:config>
</beans>
package com.eomcs.spring.ioc.ex13.d;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Caller {

  @Autowired X x;

  public void test() {
    int result = x.m1(10, 2);
    System.out.printf("result: %d\n", result);
    System.out.println("-------------------------------");

    result = x.m1(10, 0);
    System.out.printf("result: %d\n", result);
  }
}
package com.eomcs.spring.ioc.ex13.d;

import org.springframework.stereotype.Component;

@Component 
public class X {
  public X() {
    System.out.println("X 객체 생성됨");
  }
  public int m1(int a, int b) {
    System.out.println("X.m1()");
    int result = a / b;

    return result; 
  }
}
// 특정 메서드 호출 전후에 실행되는 클래스
package com.eomcs.spring.ioc.ex13.d;

public class MyAdvice {

  // 타겟 객체의 메서드를 호출하기 전에 
  // 그 메서드가 받을 파라미터를 먼저 받을 수 있다.
  //
  // 1) advice에 파라미터 값을 받을 변수를 선언하라.
  //
  //      void doBefore(int x, int y)
  // 
  // 2) XML 설정에서 파라미터 값을 받겠다고 지정하라.
  //
  //      <aop:before 
  //            pointcut="execution(* com.eomcs.spring.ioc.ex13.d.X.*(..)) and args(y,x)" 
  //            method="doBefore"/>
  // 
  //    - 파라미터 값은 설정에서 지정한 변수에 순선대로 전달 된다.
  //    - 첫 번째 파라미터 값은 y 변수에서 받을 것이고
  //    - 두 번째 파라미터 값은 x 변수에서 받을 것이다.
  //    - 따라서 설정에서 변수를 지정할 때 변수의 이름이 중요하다.
  //
  public void doBefore(int x, int y) {
    System.out.printf("MyAdvice.doBefore(): %d, %d\n", x, y);
  }

  // 타겟 객체의 메서드를 호출한 후 리턴 값을 받을 수 있다.
  //
  // 1) advice에 리턴 값을 받을 변수를 선언하라.
  // 
  //      void doAfterReturning(Object returnValue)
  // 
  // 2) XML 설정에서 리턴 값을 받겠다고 지정하라.
  //  
  //      <aop:after-returning 
  //          pointcut="execution(* com.eomcs.spring.ioc.ex13.d.X.*(..))" 
  //          method="doAfterReturning"
  //          returning="returnValue"/>
  //  
  //    - 타겟 메서드의 리턴 값은 설정 파일에 정의한 대로 
  //      'returnValue' 라는 이름의 변수에서 받을 것이다.
  // 
  public void doAfterReturning(Object returnValue) {
    System.out.printf("MyAdvice.doAfterReturning(): %d\n", returnValue);
  }

  // 타겟 객체의 메서드를 호출할 때 예외가 발생했다면 그 예외 객체를 받을 수 있다.
  //
  // 1) advice에 예외 객체를 받을 변수를 선언하라.
  //
  //      void doAfterThrowing(Exception error) 
  // 
  // 2) XML 설정에서 예외 객체를 받겠다고 지정하라.
  // 
  //      <aop:after-throwing 
  //          pointcut="execution(* com.eomcs.spring.ioc.ex13.d.X.*(..))" 
  //          method="doAfterThrowing"
  //          throwing="error"/>
  //
  //    - 타겟 메서드의 예외 객체는 설정 파일에 정의한 대로 
  //      'error' 라는 이름의 변수에서 받을 것이다.
  //
  public void doAfterThrowing(Exception error) {
    System.out.printf("MyAdvice.doAfterThrowing(): %s\n", error.getMessage());
  }
}

 

 

AOP(Aspect-Oriented Programming) - 애노테이션으로 AOP 설정하기
package com.eomcs.spring.ioc.ex13.e;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    try {
      Caller caller = iocContainer.getBean(Caller.class);
      caller.test();
    } catch (Exception e) {
      System.err.println("오류 발생!");
    }
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.e"/>

    <!-- 애노테이션으로 AOP를 설정하려면 
         AOP 관련 애노테이션을 처리해주는 객체를 등록해야 한다.
         다음 태그를 선언하면 
         AOP 관련 애노테이션을 처리해주는 객체를 자동으로 등록한다.
    -->
    <aop:aspectj-autoproxy/>
</beans>
// 특정 메서드 호출 전후에 실행되는 클래스
package com.eomcs.spring.ioc.ex13.e;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

// 애노테이션을 이용하여 Advice 객체를 IoC 컨테이너에 등록하는 방법이다.
// => XML 설정을 사용할 때는 이 클래스의 객체 생성을 XML에서 했기 때문에 
//    @Component 애노테이션을 붙이지 않았다.
// => 이제는 XML에서 이 클래스의 객체를 만들지 않기 때문에 이 애노테이션을 붙여야 한다.
// 
// XML 설정 예:
//      <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.e.MyAdvice"/>
//
@Component

// 이 객체가 AOP Advice를 설정하는 객체임을 표시한다.
//
//   <bean id="myAdvice" class="com.eomcs.spring.ioc.ex13.e.MyAdvice"/>
//
@Aspect
public class MyAdvice {

  // 
  // XML 설정 예:
  //    <aop:before
  //        pointcut="execution(* com.eomcs.spring.ioc.ex13.e.X.*(..)) and args(a,b)"
  //        method="doBefore"/>
  //
  @Before("execution(* com.eomcs.spring.ioc.ex13.e.X.*(..)) and args(x,y)")
  public void doBefore(int x, int y) {
    System.out.printf("MyAdvice.doBefore(): %d, %d\n", x, y);
  }

  //XML 설정 예:
  //    <aop:after
  //        pointcut="execution(* com.eomcs.spring.ioc.ex13.e.X.*(..))"
  //        method="doAfter"/>
  //
  @After("execution(* com.eomcs.spring.ioc.ex13.e.X.*(..))")
  public void doAfter() {
    System.out.println("MyAdvice.doAfter()");
  }

  // XML 설정 예:
  //    <aop:after-returning 
  //        pointcut="execution(* com.eomcs.spring.ioc.ex13.e.X.*(..))"
  //        method="doAfterReturning" returning="returnValue"/>
  //
  @AfterReturning(//
      pointcut = "execution(* com.eomcs.spring.ioc.ex13.e.X.*(..))",
      returning = "returnValue")
  public void doAfterReturning(Object returnValue) {
    System.out.printf("MyAdvice.doAfterReturning(): %d\n", returnValue);
  }

  // XML 설정 예:
  //    <aop:after-throwing 
  //        pointcut="execution(* com.eomcs.spring.ioc.ex13.e.X.*(..))"
  //        method="doAfterThrowing" throwing="error"/>
  //   
  @AfterThrowing(//
      pointcut = "execution(* com.eomcs.spring.ioc.ex13.e.X.*(..))", //
      throwing = "error")
  public void doAfterThrowing(Exception error) {
    System.out.printf("MyAdvice.doAfterThrowing(): %s\n", error.getMessage());
  }
}

 

 

AOP(Aspect-Oriented Programming) - 애노테이션으로 AOP 설정하기
package com.eomcs.spring.ioc.ex13.f;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

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

    SpringUtils.printBeanList(iocContainer);

    try {
      Caller caller = iocContainer.getBean(Caller.class);
      caller.test();
    } catch (Exception e) {
      System.err.println("오류 발생!");
    }
  }

}

application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.eomcs.spring.ioc.ex13.f"/>
    <aop:aspectj-autoproxy/>
</beans>
// 특정 메서드 호출 전후에 실행되는 클래스
package com.eomcs.spring.ioc.ex13.f;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAdvice {

  // Pointcut을 미리 정의한다.
  //  => 메서드 선언부에 붙여야 한다.
  //  => 메서드의 파라미터는 없고, 구현을 비워둔다.
  //  => 이 메서드는 pointcut을 지정하는 용도로만 사용한다.
  //
  @Pointcut("execution(* com.eomcs.spring.ioc.ex13.f.X.*(..))")
  public void calculatorOperation() {}

  @Before("calculatorOperation() and args(x,y)")
  public void doBefore(int x, int y) {
    System.out.printf("MyAdvice.doBefore(): %d, %d\n", x, y);
  }

  @After("calculatorOperation()")
  public void doAfter() {
    System.out.println("MyAdvice.doAfter()");
  }

  @AfterReturning(
      pointcut = "calculatorOperation()",
      returning = "returnValue")
  public void doAfterReturning(Object returnValue) {
    System.out.printf("MyAdvice.doAfterReturning(): %d\n", returnValue);
  }

  @AfterThrowing(
      pointcut = "calculatorOperation()",
      throwing = "error")
  public void doAfterThrowing(Exception error) {
    System.out.printf("MyAdvice.doAfterThrowing(): %s\n", error.getMessage());
  }
}

 

 

AOP(Aspect-Oriented Programming) - Java Config로 AOP 설정
package com.eomcs.spring.ioc.ex13.g;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.eomcs.spring.ioc.SpringUtils;

public class Exam01 {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext iocContainer =
        new AnnotationConfigApplicationContext(AppConfig.class);

    SpringUtils.printBeanList(iocContainer);

    try {
      Caller caller = iocContainer.getBean(Caller.class);
      caller.test();
    } catch (Exception e) {
      System.err.println("오류 발생!");
    }
  }

}
// 스프링 IoC 컨테이너를 설정하는 클래스
package com.eomcs.spring.ioc.ex13.g;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@ComponentScan("com.eomcs.spring.ioc.ex13.g")

// AOP 활성화시키기
// => XML 설정에서 <aop:aspectj-autoproxy/> 태그를 선언하는 것과 같다.
// 
@EnableAspectJAutoProxy

public class AppConfig {
}

 

 

com.eomcs.spring.ioc

 

 

public class SpringUtils {

  public static void printBeanList(ApplicationContext iocContainer) {
    // IoC 컨테이너에 들어있는 객체 알아내기
    // 빈(bean) = 객체(object) = 인스턴스(instance)
    // 자바 객체 생성 규칙에 따라 만든 인스턴스를 부르는 말이었다.
    // 그러나 일반적으로 객체를 부를 때도 '빈'이라는 용어를 사용한다.
    System.out.println("--------------------------------");
    int count = iocContainer.getBeanDefinitionCount();
    System.out.printf("빈 개수: %d\n", count);

    String[] beanNames = iocContainer.getBeanDefinitionNames();
    for (String name : beanNames) {
      System.out.printf("%s = %s\n",
          name, iocContainer.getBean(name).getClass().getName());
    }
    System.out.println("--------------------------------");
  }

  public static void printBeanAliases(
      ApplicationContext iocContainer, String beanName) {
    System.out.printf("['%s' 빈의 별명 목록]\n", beanName);
    String[] aliases = iocContainer.getAliases(beanName);
    for (String alias : aliases) {
      System.out.println("  => " + alias);
    }
  }

  public static void printBeanNames(ApplicationContext iocContainer) {
    System.out.println("[생성된 빈의 이름 목록]");
    String[] names = iocContainer.getBeanDefinitionNames();
    for (String name : names) {
      System.out.println(name);
    }
    System.out.println("-----------------------------------");
  }
}