AOP(Aspect-oriented Programming)은 OOP(Object-oriented Programming)를 보완하는 수단, 흩어진 Aspect를 모듈화할 수 있는 기법.
흩어진 관심사 (Crosscutting Concerns)
Concerns(관심사) : 여러 클래스와 메소드들 중 비슷한 코드
3개의 클래스와 색으로 배치된 Concerns

🎈AOP를 적용

Aspect(색 띠)에 해당하는 기능을 어디(A, B, C)에 적용해야 되나✨AOP 주요 개념✨ (AOP 관련된 용어가 어려워 포기하는 사람이 많다... 그렇게 생각하지말자)
Aspect : 묶은 것 = 모듈(하얀 박스)Target : 적용이 되는 대상(Class A, B, C)(녹색 박스)Advice : 해야할 일들(색 띠)Join point : 합류점, 메소드 실행 시점(등 그 외 여러 지점)에 Advice를 끼워 넣어라.Pointcut : Join point의 SubSet, 어디에 적용 해야하는지 (회색 박스 안 A,B,C)AOP 구현체
🧨AOP 적용 방법
스프링 AOP 특징
What is 프록시 패턴❓
Why? (기존 코드 변경 없이) 접근 제어 또는 부가 기능 추가

Client는 인터페이스 타입으로 Porxy 객체를 사용하고 프록시 객체는 원 타겟 객체를 참조하고 있다.
예제를 통한 프록시 패턴 확인
main\java\me\jinmin\AppRunner : Client
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService;
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
main\java\me\jinmin\EventService : interface Subject
package me.jinmin;
public interface EventService {
void createEvent();
void publishEvent();
void deleteEvent();
}
main\java\me\jinmin\SimpleEventService : Real Subject(=구현체)
package me.jinmin;
import org.springframework.stereotype.Service;
@Service
public class SimpleEventService implements EventService{
@Override
public void createEvent() {
//long begin = System.currentTimeMillis();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Created an event!");
//System.out.println(System.currentTimeMillis() - begin);
}
@Override
public void publishEvent() {
//long begin = System.currentTimeMillis();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Published an event!");
//System.out.println(System.currentTimeMillis() - begin);
}
@Override
public void deleteEvent(){
System.out.println("Delete an event!");
}
}
결과
//주석 포함 실행
print:
Created an event!
1002
Published an event!
2001
Delete an event!
❗기존 코드를 변경하지 않고 성능을 측정하자.(❤프록시 패턴으로) ⇒ main\java\me\jinmin\ProxySimpleEventService
package me.jinmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@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);
}
@Override
public void deleteEvent() {
simpleEventService.deleteEvent();
}
}
//주석들을 지우고
print:
Created an event!
1005
Published an event!
2006
Delete an event!
프록시 패턴의 문제점
스프링 AOP
AbstractAutoProxyCreator implements BeanPostProcessor의존성 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
main\java\me\jinmin\ProxySimpleEventService 삭제.
두 가지 정보의 필요성
Advice)PointCut)main\java\me\jinmin\PerfAspect
(이 외 모든 클래스는 위의 내용과 동일)
package me.jinmin;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class PerfAspect {
@Around("execution(* me.jinmin.EventService.*(..))") //PointCut을 새로 정의할 수 있고 어디에 적용할지 정의할 수 있다.
public Object logPerf(ProceedingJoinPoint pip) throws Throwable { // pip : Advice가 적용되는 대상(여기서는 SimpleEventService의 두 메소드)
long begin = System.currentTimeMillis();
Object retVal = pip.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
메소드 호출 전, 후 무언가를 할 수 있다. (에러를 발견해 처리할 수도) ⇒ @Around = PointCut 정의(현재는 execution을 사용, 밑에 문제점에서는 @annotation을 사용)
결과
print:
Created an event!
1077
Published an event!
2008
Delete an event!
1
문제점 : deleteEvent()에는 적용하고 싶지 않다. → Annotation(@Retension)을 통해 해결❗(주의 : @Retension을 클래스 레벨로 설정)
\main\java\me\jinmin\PerfLogging
package me.jinmin;
import java.lang.annotation.*;
/**
* If, use this annnotation, log the performance.
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface PerLogging {
}
@Retension 에서 RetensionPoicy : 이 애노테이션 정보를 얼마나 유지할 것인가.@Retention(RetentionPolicy.SOURCE : 컴파일하면 정보가 사라진다.@Retention(RetentionPolicy.RUNTIME : 굳이 설정할 필요 없다.@Retention(RetentionPolicy.CLASS : Default \main\java\me\jinmin\PerfAspect
package me.jinmin;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class PerfAspect {
@Around("@annotation(PerLogging)") //애노테이션 처리
public Object logPerf(ProceedingJoinPoint pip) throws Throwable { // pip : Advice가 적용되는 대상(여기서는 SimpleEventService의 두 메소드)
long begin = System.currentTimeMillis();
Object retVal = pip.proceed();
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
\main\java\me\jinmin\SimpleEventService
package me.jinmin;
import org.springframework.stereotype.Service;
@Service
public class SimpleEventService implements EventService{
@PerLogging
@Override
public void createEvent() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Created an event!");
}
@PerLogging
@Override
public void publishEvent() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Published an event!");
}
@Override
public void deleteEvent(){
System.out.println("Delete an event!");
}
}
결과
print:
Created an event!
1056
Published an event!
2007
Delete an event!
🎈포인트컷 정의
@PointCut(표현식) ⇒ @Aroundexecution@annotationbean@Around("bean(simpleEventService") 는 빈에 해당하는 모든 메소들에 적용🎈어드바이스 정의
@Before : 구현체의 메소드 실행 이전에 @Before로 정의한 메소드들이 먼저 실행@AfterReturning@AfterThrowing@Arond : 강력.