1. XML 기반의 POJO 클래스를 이용한 AOP 구현
부가기능을 제공하는 Advice 클래스를 작성한다.
XML 설정 파일에 <aop:config>를 이용해서 애스펙트를 설정한다. (어드바이스와 포인트컷을 설정함)
2. @Aspect 어노테이션을 이용한 AOP 구현
@Aspect 어노테이션을 이용해서 부가기능을 제공하는 Aspect 클래스를 작성한다.
이 때 Aspect 클래스는 어드바이스를 구현하는 메서드와 포인트컷을 포함한다.
XML 설정 파일에 <aop:aspectj-autoproxy />를 설정한다.
개요
만약 내가 작성한 모든 메서드의 실행 전후로 로그를 남기고 싶다면?
데이터베이스에 대한 쓰기 작업 전후의 트랜잭션 관리를 일일이 명시하지 않고 자동으로 하고 싶다면?
전통적인 클래스 관점의 OOP 세계에서는 깔끔하게 대응하기가 쉽지 않다.
답은 AOP(Aspect Oriented Programming)를 도입하는 것이다.
Spring 또한 AspectJ 스타일을 수용한 프록시 기반의 Spring AOP를 제공한다.
메서드 단위로만 적용 가능한 제한적인 AOP를 제공하지만 일반적인 상황에서 사용하는 데는 충분하다.
AOP 도입으로 로그 출력 기능 구현 (JDK Dynamic Proxy)
1. 스프링 @AOP를 사용하기 위해서는 다음과 같은 의존성을 추가해야 한다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 아래와 같이 @Aspect 어노테이션을 붙여 이 클래스가 Aspect를 나타내는 클래스라는 것을 명시하고 @Component를 붙여 스프링 빈으로 등록한다.
@Component
@Aspect
public class PerfAspect {
@Around("execution(* com.saelobi..*.EventService.*(..))")
public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
long begin = System.currentTimeMillis();
Object retVal = pjp.proceed(); // 메서드 호출 자체를 감쌈
System.out.println(System.currentTimeMillis() - begin);
return retVal;
}
}
@Around 어노테이션은 타겟 메서드를 감싸서 특정 Advice를 실행한다는 의미이다.
위 코드의 Advice는 타겟 메서드가 실행된 시간을 측정하기 위한 로직을 구현하였다.
execution( com.saelobi...EventService.*(..))가 의미하는 바는
com.saelobi 아래의 패키지 경로의 EventService 객체의 모든 메서드에 이 Aspect를 적용하겠다는 의미다.
3. target인 EventService는 Interface이다.
public interface EventService {
void createEvent();
void publishEvent();
void deleteEvent();
}
4. interface를 implements 하여, JDK dynamic proxy가 사용되며 오버라이딩 메서드들이 proxy를 사용한다.
@Component
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(1000);
} catch (InterruptedException e){
e.printStackTrace();;
}
System.out.println("Published an event");
}
public void deleteEvent() {
System.out.println("Delete an event");
}
}
5. 런타임 위빙 방식으로 실행되면, 프록시가 생성되어 로그 출력을 하게된다.
@Service
public class AppRunner implements ApplicationRunner {
@Autowired
EventService eventService;
@Override
public void run(ApplicationArguments args) throws Exception {
eventService.createEvent();
eventService.publishEvent();
eventService.deleteEvent();
}
}
결과
Created an event
1003
Published an event
1000
Delete an event
0
- 질문
AOP 표준인 AspectJ를 실무에서 자주 쓰나요? AspectJ 공부를 할 필요성이 있나요? spring AOP만 알아도 충분한가요?
런타임 위빙이 spring AOP 방식인데, AspectJ는 컴파일 타임 위빙 방식이 맞나요?
이번 AOP는 조사할 양이 너무 많아서 spring framework AOP 중심으로 정리하였습니다.
AspectJ 관련하여 한번 멘토링 주제를 진행하는 것도 나쁘지 않다 생각합니다~
JDK Dynamic Proxy / CGLIB Proxy 에 대해 지금 알고 있는 것 보다 더 자세히 알 필요가 있을까요? 개념은 이해를 했는데 실제 예제 소스를 보면 다양한 AOP 처리시 사용된 예제들은 계속 봐야 제 것으로 만들 수 있을 것 같습니다!
=프록시 관련해서도 멘토링 주제로 진행하는 것도 나쁘지 않다 생각합니다~
참고