- OOP를 보완하는 수단으로 흩어진 Aspect를 모듈화 할 수 있는 프로그래밍 기법
- 흩어져 있으면 유지보수가 어렵다.
- 관심사별로 모은 뒤 적용할 곳을 입력해준다.
- Aspect
- 각 모듈
- Target
- Aspect가 적용되는 대상
- Advice
- 해야할 일
- Join point
- 끼워넣을 지점
- ex. 메서드 실행 시
- Pointcut
- 어디에 적용되어야 하는지 지정하는 곳
- Java
- AspectJ
- 스프링 AOP
- 컴파일
- 로드 타임
- 클래스 파일을 로딩하는 시점
- 런타임
- 프록시 기반 AOP 구현체
- 스프링 빈에만 AOP를 적용할 수 있다.
- 스프링 IoC와 연동해 애플리케이션에서 가장 흔한 문제를 해결하는 것이 목적이다.
- 모든 AOP 기능을 제공하기 위함이 아니다.
- 기존 코드 변경 없이 접근을 제어하거나 부가 기능을 추가할 수 있다.
{% tabs %} {% tab title="AppRunner.java" %}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService;
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
}
}
{% endtab %} {% tab title="EventService.java" %}
public interface EventService {
void createEvent();
void publishEvent();
}
{% endtab %} {% tab title="SimpleEventService.java" %}
@Service
public class SimpleEventService implements EventService {
// 이 코드는 건드리지 않고 시간을 측정하려 한다.
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Created an event");
}
@Override
public void publishEvent() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Published an event");
}
}
{% endtab %} {% tab title="ProxySimpleEventService.java" %}
// AppRunner는 이 빈을 먼저 불러와 실행한다.
@Primary
@Service
public class ProxySimpleEventService implements EventService {
@Autowired
SimpleEventService simpleEventService;
// 부가 기능은 여기서 적용한다.
@Override
public void createEvent() {
long begin = System.currentTimeMillis();
simpleEventService.createEvent();
System.out.println(System.currentTimeMillis() - begin);
}
@Override
public void publishEvent() {
long begin = System.currentTimeMillis();
simpleEventService.publishEvent();
System.out.println(System.currentTimeMillis() - begin);
}
}
{% endtab %} {% endtabs %}
- 매번 프록시 클래스를 작성해야 한다.
- 여러 클래스 여러 메서드에 적용하려면 복잡해진다.
- 이런 문제를 해결하기 위해 등장했다.
- 스프링 IoC 컨테이너가 제공하는 기반 시설과 Dynamic 프록시를 사용해 복잡한 문제를 해결한다.
- 스프링 IoC는 기존 빈을 대체하는 동적 프록시 빈을 만들어 등록한다.
- 클라이언트 코드는 변경되지 않는다.
- 동적으로 프록시 객체를 생성하는 방법
- 자바는 인터페이스 기반의 프록시를 생성한다.
- CGlib은 클래스 기반도 지원한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 애너테이션 기반의 스프링 @AOP
- @Aspect
- 빈으로 등록해야 하므로 @Component 추가도 필요하다.
@Component
@Aspect
public class PerfAspect {
@Around("execution(* me.gracenam..*.EventService.*(..))")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
- @Pointcut(표현식)
- 주요 표현식
- execution
- @annotation
- bean
- 포인트컷 조합
- &&
- ||
- !
- @Before
- @AfterReturning
- @AfterThrowing
- @Around