관점 지향 프로그래밍 : 어떤 로직을 핵심적인 관점과 부가적인 관점으로 나누고 각각을 모듈화
흩어진 관심사(Crosscutting Concerns) : 다른 부분에서 계속 반복해서 쓰는 코드들
-> Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용
: Aspect를 어디에 적용할 것인지 정의
@Pointcut("execution(public void get*())")
public형의 반환값이 없는 get으로 시작하는 모든 메소드중 파라미터가 존재하지 않는 메소드들에게 적용
// * : 모든 값
//.. : 0개 이상 모든 값
@Pointcut("execution(* *(..))")
모든 접근제어자와 반환형
어떠한 경로의 클래스든 모두 적용
.. : 파라미터가 몇개가 존재하던지 상관없이 실행
@Pointcut("execution(* com.java.example.study())")
com.java.example class의 study() 메소드가 호출될 때 실행
@Pointcut("execution( com.java...*())")
.. : 해당 패키지를 포함한 모든 하위 패키지에 적용
: 패키지 내의 모든 메소드에 적용할 때 사용
@Pointcut("within(com.java.example.*)")
com.java.example 하위의 모든 클래스의 모든 메소드에 적용
: 해당 bean id를 가지고 있는 bean의 모든 메소드에 적용
@Pointcut(bean(example))
example이라는 bean id를 가진 bean의 모든 메소드에 적용
: 언제 핵심 로직에 반영할 것인지를 결정
@Advice("pointcut")
pointcut : 어떤 메소드가 실행될 때
advice : 언제 공통 코드를 실행 -> 메소드 전/후/...
: 메소드 실행 전
@Before("execution(* com.java.example.study())")
: 메소드 실행 후
@After("execution(* com.java.example.study())")
@Pointcut("execution(* com.java.example.study())")
private void pointcut() {}
//
@Before("pointcut()")
@After("pointcut()")
: 반환된 후
@AfterReturning(value = "pointcut()", returning = "returnValue")
returning 속성을 통해 메소드의 반환값 확인 가능
: 예외가 던져지는 시점
@AfterThrowing(value = "pointcut()", throwing = "exception")
throwing 속성을 통해 메소드의 exception 내용 확인 및 사용 가능
: 메소드가 호출되는 전 과정
@Component
@Aspect
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Around("execution(* com.java.example.study())")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 메소드를 실행
Object proceed = joinPoint.proceed();
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return proceed; // 결과 리턴
}
}
@Aspect : class가 부가기능 class임을 알려주는 annotation
@Component : spring bean으로 등록(Spring bean에만 aop를 적용 가능)
ProceedingJoinPoint interface
@LogExecutionTime
public void exampleMethod() {
...
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
@Target(ElementType.METHOD) : annotation을 메소드에 사용
@Retention(RetentionPolicy.RUNTIME) : annotation이 runtime까지 유지
@Component
@Aspect
public class LogAspect {
Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Around("@annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 메소드를 실행
Object proceed = joinPoint.proceed();
stopWatch.stop();
logger.info(stopWatch.prettyPrint());
return proceed; // 결과 리턴
}
}
@Service
public class TestEventService implements EventService {
@override
public void createEvent() {
System.out.println("create event");
}
@override
public void publishEvent() {
System.out.println("create event");
}
}
@Primary
@Service
public class ProxyTestEventService implements EventService {
@Autowired
TestEventService testEventService;
@override
public void createEvent() {
long begin = system.currentTimeMillis();
testEventService.createEvent();
system.out.println(system.currentTimeMillis()-begin);
}
@override
public void publishEvent() {
long begin = system.currentTimeMillis();
testEventService.createEvent();
system.out.println(system.currentTimeMillis()-begin);
}
}
@Primary : 우선순위 선정
-> client가 EventService를 통해 메서드를 호출하면 proxy 객체가 호출
@Service : 해당 클래스를 루트 컨테이너에 bean 객체로 생성