Link Search Menu Expand Document

스프링 삼각형과 설정 정보

IoC/DI - 제어의 역전/의존성 주입

프로그래밍에서 의존성이란?

  • 집합 관계: 부분이 전체와 다른 생명 주기를 가질 수 있음 (집 - 냉장고)
  • 구성 관계: 부분은 전체와 같은 생명 주기를 가짐 (사람 - 심장)

클래스 다이어그램: 의존 관계를 직접 해결

시퀀스 다이어그램: 의존 관계를 직접 해결

pacakge expert001_01;

interface Tire {
	String getBrand();
}
pacakge expert001_01;

public class KoreaTire implements Tire {
	public String getBrand() {
  	return "코리아 타이어";
  }
}
pacakge expert001_01;

public class AmericaTire implements Tire {
	public String getBrand() {
  	return "미국 타이어";
  }
}
package expert001_01;

public class Car {
	Tire tire;

  public Car() {
  	tire = new KoreaTire();
    // tire = new AmericaTire();
  }

  public String getTireBrand() {
  	return "장착된 타이어: " + tire.getBrand();
  }
}
package expert001_01;

public class Car {
	Tire tire;

  public Car() {
  	tire = new KoreaTire();
    // tire = new AmericaTire();
  }

  public String getTireBrand() {
    return "장착된 타이어: " + tire.getBrand();
  }
}
package expert001_01;

public class Driver {
	public static void main(String[] args) {
  	Car car = new Car();
    System.out.println(car.getTireBrand());
  }
}
package expert001_01;

import static org.junit.Assert.*;

import org.junit.Test;

public class CarTest {
	@Test
  public void 자동차_장착_타이어브랜드_테스트() {
  	Car car = new Car();
    
  	assertEquals("장착된 타이어: 코리아 타이어", car.getTireBrand());
  }
}

스프링 없이 의존성 주입하기 1 - 생성자를 통한 의존성 주입

  • 주입이란?
    • 자동차 내부에서 타이어를 생산하는 것이 아니라 외부에서 생산된 타이어를 자동차에 장착

Tire에 대한 의존성을 Car 생성자의 인자 주입으로 해결

Tire에 대한 의존성을 Car 생성자의 인자 주입으로 해결

package expert001_02;

public interface Tire {
  String getBrand();
}
pacakge expert001_02;

public class KoreaTire implements Tire {
	public String getBrand() {
  	return "코리아 타이어";
  }
}
pacakge expert001_02;

public class AmericaTire implements Tire {
	public String getBrand() {
  	return "미국 타이어";
  }
}
package expert001_02;

public class Car {
	Tire tire;

  public Car(Tire tire) {
  	this.tire = tire;
  }

  public String getTireBrand() {
    return "장착된 타이어: " + tire.getBrand();
  }
}
package expert001_02;

public class Driver {
	public static void main(String[] args) {
    Tire tire = new KoreaTire();
    //Tire tire = new AmericaTire();
  	Car car = new Car(tire);
    
    System.out.println(car.getTireBrand());
  }
}
package expert001_02;

import static org.junit.Assert.*;

import org.junit.Test;

public class CarTest {
	@Test
  public void 자동차_코리아타이어_장착_타이어브랜드_테스트() {
    Tire tire1 = new KoreaTire();
  	Car car1 = new Car(tire1);
    
  	assertEquals("장착된 타이어: 코리아 타이어", car1.getTireBrand());
  }

  	@Test
  public void 자동차_미국타이어_장착_타이어브랜드_테스트() {
    Tire tire2 = new AmericaTire();
  	Car car2 = new Car(tire2);
    
  	assertEquals("장착된 타이어: 미국 타이어", car2.getTireBrand());
  }
}

스프링 없이 의존성 주입하기 2 - 속성을 통한 의존성 주입

Tire에 대한 의존성을 Car의 속성 주입으로 해결 Tire에 대한 의존성을 Car의 속성 주입으로 해결

package expert001_03;

public class Car {
  Tire tire;

  public Tire getTire() {
    return tire;
  }

  public void setTire(Tire tire) {
    this.tire = tire;
  }

  public String getTireBrand() {
    return "장착된 타이어: " + tire.getBrand();
  }
}
package expert001_03;

public class Driver {
  public static void main(String[] args) {
    Tire tire = new KoreaTire();
    Car car = new Car();
    car.setTire(tire);

    System.out.println(car.getTireBrand());
  }  
}
package expert001_03;

import static org.junit.Assert.*;

import org.junit.Test;
                                 
public class CarTest {
  @Test
  public void 자동차_코리아타이어_장착_타이어브랜드_테스트() {
    Tire tire1 = new KoreaTire();
    Car car1 = new Car();
    car1.setTire(tire1);

    assertEquals("장착된 타이어: 코리아 타이어", car1.getTireBrand());
  }

  @Test
  public void 자동차_미국타이어_장착_타이어브랜드_테스트() {
    Tire tire2 = new AmericaTire();
    Car car2 = new Car();
    car2.setTire(tire2);

    assertEquals("장착된 타이어: 미국 타이어", car2.getTireBrand());
  }
}

스프링을 통한 의존성 주입 - XML 파일 사용

스프링을 도입하기 전/후의 클래스 다이어그램 비교 시퀀스 다이어그램: 스프링 도입 후 의존성 해결

package expert002;

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

public class Driver {
  public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("expert002/expert002.xml");
    
    Car car = new Car();
    
    car.setTire(tire);

    System.out.println(car.getTireBrand());
  }  
}
<?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="tire" class="expert002.KoreaTire"></bean>

  <bean id="americaTire" class="expert002.AmericaTire"></bean>

  <bean id="car" class="expert002.Car"></bean>
  
</beans>

스프링 설정 파일을 통한 의존성 주입

package expert003;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpiringJUnit4ClassRunner.class)
@ContextConfiguration("expert003.xml")
public class CarTest {
  @Autowired
  Car car;

  @Test
  public void 자동차_코리아타이어_장착_타이어브랜드_테스트() {
    assertEquals("장착된 타이어: 코리아 타이어", car.getTireBrand());
  }
}

스프링을 통한 의존성 주입 - @Autowired를 통한 속성 주입

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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-3.1.xsd">

  <context:annotation-config />

  <bean id="tire" class="expert004.KoreaTire"></bean>

  <bean id="americaTire" class="expert004.AmericaTire"></bean>

  <bean id="car" class="expert004.Car"></bean>
</beans>
  • property 태그가 사라지고 @Autowired를 통해 carproperty를 자동으로 엮어 줌
package expert004;

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

public class Car {
  @Autowired
  Tire tire;

  public String getTireBrand() {
    return "장착된 타이어: " + tire.getBrand();
  }
}

Autowired를 통한 매핑 규칙

스프링을 통한 의존성 주입 - @Resource를 통한 속성 주입

package expert005;

import javax.annotation.Resource;

public class Car {
  @Resource
  Tire tire;

  public String getTireBrand() {
    return "장착된 타이어: " + tire.getBrand();
  }
}
  • @Resource 는 자바 표준 어노테이션, type과 id 중 type 우선 매칭
  • @Autowired 는 스프링 어노테이션, type과 id 중 id 우선 매칭

    스프링을 통한 의존성 주입 - @Autowired vs. @Resource vs. 태그

    | |@Autowired|@Resource| |–|–|–| |출처|스프링 프레임워크|표준 자바| |소속 패키지|org.springframework.beans.factory.annotaion.Autowired|javax.annotation.Resource| |빈 검색 방식| byType 먼저, 못 찾으면 byName|byName 먼저, 못 찾으면 byType| |특이사항|@Qualifer(“”) 협업|name 어트리뷰트| |byName 강제하기|@Autowired
    @Qualifier(“tire1”)|@Resource(name=”tire1”)|

    AOP - Aspect? 관점? 핵심 관심사? 횡단 관심사?

  • AOP: Aspect-Oriented Programming
  • 횡단 관심사 (cross-cutting concern): 다수의 모듈에 공통적으로 나타나는 부분
  • 핵심 관심사: 모듈별로 고유한 부분

횡단 관심사

package aop001;

public class Boy {
  public void runSomething() {
		System.out.println("열쇠로 문을 열고 집에 들어간다.");

    try {
      System.out.println("컴퓨터로 게임을 한다.");
    } catch (Exception ex) {
      if (ex.getMessage().equals("집에 불남")) {
        System.out.println("119 에 신고한다.");
      }
    } finally {
      System.out.println("소등하고 잔다.");
    }

    System.out.println("자물쇠를 잠그고 집을 나선다.");
  }
}
package aop001;

public class Girl {
  public void runSomething() {
    System.out.println("열쇠로 문을 열고 집에 들어간다.");

    try {
      System.out.println("요리를 한다.");
    } catch (Exception ex) {
       if (ex.getMessage().equals("집에 불남")) {
        System.out.println("119 에 신고한다.");
      }     
    } finally {
      System.out.println("소등하고 잔다.");
    }

    System.out.println("자물쇠를 잠그고 집을 나선다.");
  }
}
package aop001;

public class Start {
  public static void main(String[] args) {
    Boy romeo = new Boy();
    Girl juliet = new Girl();

    romeo.runSomething();
    juliet.runSomething();
  }
}

메서드에 로직(코드)을 주입할 수 있는 곳들

일단 덤벼 보자 - 실전편

package aop002;

public interface Person {
  void runSomething();
}
package aop002;

public class Boy implements {
  public void runSomething() {
    System.out.println("컴퓨터로 게임을 한다.");
  }
}
package aop002;

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

public class Start {
  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("aop002/aop002.xml");

    Person romeo = context.getBean("boy", Person.class);

    romeo.runSomething();
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       
       xsi:schemaLocation="
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd">

  		<aop:aspectj-autoproxy />

  		<bean id="myAspect" class="aop002.MyAspect" />
  		<bean id="boy" class="aop002.Boy" />
</beans>
package aop002;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;

@Aspect
public class MyAspect {
  @Before("execution(public void aop002.Boy.runSomething())")
  public void before(JoinPoint joinPoint) {
    System.out.println("얼굴 인식 확인: 문을 개방하라");
    // System.out.println("열쇠로 문을 열고 집에 들어간다.");
  }
}
  • @Aspect는 이 클래스를 AOP에서 사용하겠다는 의미
  • @Before는 대상 메서드 실행 전에 이 메서드를 실행하겠다는 의미
  • 실행 결과
    얼굴 인식 확인: 문을 개방하라
    컴퓨터로 게임을 한다.
    

    일단 덤벼 보자 - 설명편

    AOP를 통해 런타임에 로직 주입

AOP를 통해 런타임에 로직 주입(상세)

AOP 적용 전과 후 비교

  • 초록색 부분: 횡단 관심사
  • 붉은색 부분: 스프링 AOP가 인터페이스 기반으로 작동하므로 필요 (Person.java)
  • MyAspect.java: BoyGirl에서 나타나는 횡단 관심사 처리
  • <aop:aspectj-autoproxy />: 프록시 패턴을 이용해 횡단 관심사를 핵심 관심사에 주입

  • 스프링 AOP는
    • 인터페이스 기반
    • 프록시 기반
    • 런타임 기반

      일단 덤벼 보자 - 용어편

      Pointcut - 자르는 지점? Aspect 적용 위치 지정자!

      ```java package aop002;

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;

@Aspect public class MyAspect { @Before(“execution(* runSomething())”) public void before(JoinPoint joinPoint) { System.out.println(“얼굴 인식 확인: 문을 개방하라”); } }


- `@Before("execution(* runSomething())")`: 선언한 메소드 (`before`)를 `* runSomething`이 실행되기 전에 실행
- 타겟 메서드 지정자: `[접근제한자패턴] 리턴타입패턴 [패키지&클래스패턴] 메서드이름패턴(파라미터패턴) [throws 예외패턴]`
#### JoinPoint - 연결점? 연결 가능한 지점!
- `Pointcut`은 `JoinPoint`의 부분 집합
- `Aspect` 적용이 가능한 모든 지점
- `romeo.runSomething()` 메서드를 호출한 상태라면 `JoinPoint`는 `romeo` 객체의 `runSomething()` 메서드

- 넓은 의미의 `JoinPoint`란 `Aspect` 적용이 가능한 모든 지점
- 좁은 의미의 `JoinPoint`란 호출된 객체의 메서드
#### Advice - 조언? 언제, 무엇을!
- `PointCut`에 언제, 무엇을 적용할 지 정의한 메서드
#### Aspect - 관점? 측면? Advisor의 집합체!
- 여러 개의 `Advice`와 여러 개의 `PointCut`의 결합체
#### Advisor - 조언자? 어디서, 언제, 무엇을!
- `Advisor` = 한 개의 `Advice` + 한 개의 `PointCut`
- 스프링 AOP에서만 사용
### 일단 덤벼 보자 - POJO와 XML 기반 AOP
```java
package aop003;

import org.aspectj.lang.JoinPoint;

public class MyAspect {
  public void before(JoinPoint joinPoint) {
    System.out.println("얼굴 인식 확인: 문을 개방하라");
    //System.out.println("열쇠로 문을 열고 집에 들어간다.");
  }
}

변경 전 MyAspect.java와 변경 후 스프링 설정 파일

  • aop003MyAspect는 스프링 프레임워크에 의존하지 않는 POJO

    AOP 기초 완성

    ```java package aop004;

import org.aspectj.lang.JoinPoint;

public class MyAspect { public void before(JoinPoint joinPoint) { System.out.println(“얼굴 인식 확인: 문을 개방하라”); }

public void lockDoor(JoinPoint joinPoint) { System.out.println(“주인님 나갔다: 어이 문 잠가!!!”); } }


```java
package aop004;

PSA - 일관성 있는 서비스 추상화