Aspect Oriented Programming의 약자이다. 즉, 관점 지향 프로그래밍이다.
🤔 그게 뭐지...?
사실 나는 크게 느끼지는 못했지만...ㅎ😅 프로그래밍을 하다보면 기계적으로 코딩을 하게된다고 한다. 하나의 모듈마다 반복적으로 코딩을 하게 되는 부분이 있다.
좋은 예시인지는 모르겠지만, 백준알고리즘을 풀다보면 입력받기 - 알고리즘짜기 - 출력하기
를 반복하게 된다. 입력과 출력은 어떤 문제든 항상 반복해서 등장하게 되는 부분이다. 바로 이 부분이 💡횡단 관심사이다.
횡단 관심사란, 모듈별로 반복되어 중복해서 나타나는 부분이다.
또한, 각 문제마다 목표가 다른 알고리즘 부분인 핵심 관심사가 존재한다.
결국 코드는 핵심관심사와 횡단 관심사로 이루어져있다.
코드 = 핵심관심사 + 횡단 관심사
잠깐 생각해보자.
🤔 그런데 반복과 중복은 피해야하는거 아닌가? 👉 맞아!!
이를 도와주는 것이 바로 AOP!!
그렇다면 뭔가 로직을 주입해야겠다... 어디에?
👉 ① 시작 직전 ② 종료 직전 ③ 정상 종료 ④ 비정상 종료
남자
열쇠로 문을 열고 집에 들어간다.
컴퓨터로 게임을 한다.
소등하고 잔다.
자물쇠를 잠그고 집을 나선다.
여자
열쇠로 문을 열고 집에 들어간다.
요리를 한다.
소등하고 잔다.
자물쇠를 잠그고 집을 나선다.
Boy 클래스
package aop001;
public class Boy {
public void runSomething() {
System.out.println("열쇠로 문을 열고 집에 들어간다.");
try {
System.out.println("컴퓨터로 게임을 한다.");
} catch (Exception e){
if (e.getMessage().equals("집에 불이 남")){
System.out.println("119에 신고한다.");
}
} finally {
System.out.println("소등하고 잔다.");
}
System.out.println("자물쇠를 잠그고 집을 나선다.");
}
}
Girl 클래스
package aop001;
public class Girl {
public void runSomething() {
System.out.println("열쇠로 문을 열고 집에 들어간다.");
try {
System.out.println("요리 한다.");
} catch (Exception e){
if (e.getMessage().equals("집에 불이 남")){
System.out.println("119에 신고한다.");
}
} finally {
System.out.println("소등하고 잔다.");
}
System.out.println("자물쇠를 잠그고 집을 나선다.");
}
}
Start
package aop001;
public class Start {
public static void main(String[] args) {
Boy boy = new Boy();
Girl girl = new Girl();
System.out.println("=================");
boy.runSomething();
System.out.println("=================");
girl.runSomething();
}
}
DI가 의존성 주입이라면 AOP는 로직 주입이다!
스프링 AOP를 통해서 횡단관심사와 핵심 관심사를 분리하고, 횡단 관심사를 주입할 수 있다.
어디에?
👉 ① 시작 직전 ② 종료 직전 ③ 정상 종료 ④ 비정상 종료
package aop002;
public interface Person {
void runSomething();
}
횡단관심사를 제외한 핵심관심사만 적어준다.
package aop002;
public class Boy implements Person {
public void runSomething() { //핵심관심사만 남겨놓음
System.out.println("컴퓨터로 게임을 한다.");
}
}
@Aspect
,@Before
의 어노테이션을 가지고있다.
@Aspect
: 이 클래스를 AOP에 사용할 수 있도록 등록@Before(대상 메서드)
: 대상 메서드 전에 실행할 메서드package aop002;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //이 클래스를 이제 AOP에서 사용하겠다는 의미이다.
public class MyAspect {
@Before("execution(public void aop002.Boy.runSomething ())") //대상 메서드 실행전에 이 메서드를 실행하겠다.
public void before(JoinPoint joinPoint){
System.out.println("얼굴 인식 확인: 문 개방");
// System.out.println("열쇠로 문을 열고 집에 들어간다. ");
}
}
<?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/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" >
<aop:aspectj-autoproxy/>
<bean id="myAspect" class="aop002.MyAspect"/>
<bean id="boy" class="aop002.Boy"/>
</beans>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
before()
가 runtime()
시작전에 주입된다.
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();
}
}
얼굴 인식 확인: 문 개방
컴퓨터로 게임을 한다.
AOP의 강점
AOP
를 적용하면 핵심관심사만 분리할 수 있다. 따라서 단일책임원칙인 SRP를 지킬 수 있다.
🤔 왜 로직을 분리하고 주입했을까??
👉 바로 코드의 변경을 줄이고, 확장성을 높이기 위해서이다. 따라서 하나에게만 사용하는 것이 아니라 여러클래스의 동일하고 반복되는 횡단 관심사를 주입하는 것이다.
따라서 여러 클래스에 적용하기 위한 인터페이스기반으로 사용하게 된다.
먼저 빈을 등록해주는 xml 파일을 살펴보자.
<aop:aspectj-autoproxy/>
<bean id="myAspect" class="aop002.MyAspect"/>
<bean id="boy" class="aop002.Boy"/>
<bean id="girl" class="aop002.Girl"/>
Boy
와 Girl
은 AOP 적용대상이다. Aspect
는 AOP의 Aspect<aop:aspectj-autoproxy/>
는?<aop:aspectj-autoproxy/>
AspectJ를 사용한 AOP 적용 대상에 대해 자동으로 프록시를 생성하고 관련된 Aspect를 적용할 수 있다.
👉 AOP 적용 대상 : Boy
와 Girl
@Before("execution(public void aop002.Boy.runSomething ())")
👉 관련 대상 : @Before
이를 통해 코드에서는 명시적으로 Aspect를 호출하는 대신, AspectJ가 자동으로 프록시를 생성하여 필요한 Aspect를 적용하게 됩니다.
👉 romeo.runSomething()
에 실제로 호출되는 것은 프록시이다.
호출하는 쪽이나, 호출당하는 객체는 모두 프록시의 존재를 모르고 스프링 프레임워크만 자동으로 프록시를 사용해서 존재를 안다.
package aop002;
public interface Person {
void runSomething();
}
package aop002;
public class Boy implements Person {
public void runSomething() { //핵심관심사만 남겨놓음
System.out.println("컴퓨터로 게임을 한다.");
}
}
package aop002;
public class Girl implements Person {
public void runSomething() {
System.out.println("요리한다.");
}
}
package aop002;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //이 클래스를 이제 AOP에서 사용하겠다는 의미이다.
public class MyAspect {
@Before("execution(* runSomething ())") //대상 메서드 실행전에 이 메서드를 실행하겠다.
public void before(JoinPoint joinPoint){
System.out.println("얼굴 인식 확인: 문 개방");
}
}
<aop:aspectj-autoproxy/>
<bean id="myAspect" class="aop002.MyAspect"/>
<bean id="boy" class="aop002.Boy"/>
<bean id="girl" class="aop002.Girl"/>
</beans>
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.xml",Start.class);
Person romeo = context.getBean("boy", Person.class);
romeo.runSomething();
Person juliet = context.getBean("girl", Person.class);
juliet.runSomething();
}
}
뭐가 다른거지?
ApplicationContext context = new ClassPathXmlApplicationContext("aop002.xml",Start.class);
ApplicationContext context = new ClassPathXmlApplicationContext("aop002/aop002.xml");