스프링 삼각형과 설정 정보
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 - 생성자를 통한 의존성 주입
- 주입이란?
- 자동차 내부에서 타이어를 생산하는 것이 아니라 외부에서 생산된 타이어를 자동차에 장착
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 - 속성을 통한 의존성 주입
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
를 통해car
의property
를 자동으로 엮어 줌
package expert004;
import org.springframework.beans.factory.annotation.Autowired;
public class Car {
@Autowired
Tire tire;
public String getTireBrand() {
return "장착된 타이어: " + tire.getBrand();
}
}
스프링을 통한 의존성 주입 - @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가 인터페이스 기반으로 작동하므로 필요 (
Person.java
) MyAspect.java
:Boy
와Girl
에서 나타나는 횡단 관심사 처리<aop:aspectj-autoproxy />
: 프록시 패턴을 이용해 횡단 관심사를 핵심 관심사에 주입- 스프링 AOP는
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("열쇠로 문을 열고 집에 들어간다.");
}
}
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;