들어가며
- 스프링에서 제공하는 DI와 AOP를 적용하는 과정을 정리하고자 한다.
- DI란 하나의 객체를 만들기 위한 클래스 간 결합 스프링의 기법 중 하나이며, xml을 통한 외부 설정 방식과, 어너테이션을 통한 내부 설정 방식이 있다. 이번 논의는 자바를 통한 방식과 xml을 통한 방식 두 가지로 정리하겠다.
JAVA를 통한 DI 적용
클래스
Cook.java
public class Cook {
private String food;
public Cook(String food) {
this.food = food;
}
public String getFood() {
return food;
}
public void cooking() {
System.out.println(food+"를 요리합니다.");
}
}
Eat.java
public class Eat {
private Cook cook;
public Eat() {
}
public Eat(Cook cook) {
super();
this.cook = cook;
}
public void eatting() {
System.out.println(cook.getFood()+"를 맛있게 먹습니다.");
}
}
Shower.java
public class Shower {
public void takingShower() {
System.out.println("샤워를 합니다.");
}
}
public class Watch {
private String tvShow;
public void setTvShow(String tvShow) {
this.tvShow = tvShow;
}
public void watching() {
System.out.println(tvShow+"를 시청합니다.");
}
}
메인 스레드
public class Home {
public static void main(String[] args) {
Cook cook = new Cook("부대찌개");
Eat eat = new Eat(cook);
Shower shower = new Shower();
Watch watch = new Watch();
watch.setTvShow("뮤직뱅크");
System.out.println("-----집에 도착했다.-------");
cook.cooking();
eat.eatting();
shower.takingShower();
watch.watching();
}
}
- 메인스레드는 cooking -> eatting -> takingShower->watching 을 순서로 작동한다. 이를 aop라고 핵심 기능이 takingShower이다. takingShower 매서드를 통해 나머지를 자동으로 사용할 수 없을까? 이러한 과정을 외부 xml을 통해 할 수 있을까?
- xml을 통해 Bean의 선언과 초기화, 스레드의 작동 순서를 문서를 통해 통제한다. 이는 아래와 같다.
XML 설정
xml 설정 di, aop 설정
applicationContext.xml
<bean id="cook2" class="com.spring.test.Cook" c:food="짜장면"/>
<bean id="eat2" class="com.spring.test.Eat" c:cook-ref="cook2"/>
<bean id="shower2" class="com.spring.test.Shower"/>
<bean id="watch2" class="com.spring.test.Watch" p:tvShow="놀면뭐하니"/>
<aop:config>
<aop:aspect ref="cook2" order="2">
<aop:before method="cooking"
pointcut="execution(* com.spring.test.Shower.*())"/>
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspect ref="eat2" order="1">
<aop:before method="eatting"
pointcut="execution(* com.spring.test.Shower.*())"/>
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspect ref="watch2">
<aop:after method="watching"
pointcut="execution(* com.spring.test.Shower.*())"/>
</aop:aspect>
</aop:config>
- bean 태그 : 빈 태그를 통해 객체를 생성한다. 생성한 객체는 두 가지 방식으로 데이타를 주입할 수 있다. 1) 생성자의 매개변수(c:food="짜장면", c:cook-ref="cook2") 2) 매소드의 매개변수(p:tvShow="놀면뭐하니")
- aop 태그 : pointcut : 핵심 기능이 무엇인지를 작성한다. execution()에 해당 내용을 삽입한다. 리턴 타입, 접근제어자, 클래스, 매서드 등 원하는 핵심 기능을 세세하게 작성할 수 있다. 여기서는 Shower 클래스가 어떤 매서드(*)를 사용하든 AOP를 작동한다는 의미이다.
- aop 태그 : after/before : 핵심기능이 작동하기 이전과 이후 무엇을 할 것인지 작성한다. order를 통해 순서를 설정한다.
메인 스레드
public class Home {
public static void main(String[] args) {
System.out.println("------AOP와 함께 집에 도착했다.--------");
AbstractApplicationContext context =
new GenericXmlApplicationContext("classpath:applicationContext.xml");
Shower shower2 = (Shower) context.getBean("shower2");
shower2.takingShower();
}
}
- ApplicationContext 인터페이스는 네 가지의 상속한 클래스가 있다. GenericXmlApplicationContext은 xml 파일을 통해 di와 aop를 설정한다.
- getBean을 통해 컨텍스트로부터 이미 초기화가 된 빈을 가지고 온다. 해당 메서드(takingShower)가 실행 될 때, 전후 작업을 자동적으로 수행한다(AOP).
DI, AOP를 위한 몇 가지 설정
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.7.4</version>
</dependency>
- di의 초기화 때, NoArgsConstructor 를 통해 초기화한다. 빈에 매개변수가 있는 생성자가 있을 경우, 반드시 매개변수가 없는 생성자도 만들자.