Spring 프레임워크의 핵심 기능 중 하나인 AOP(Aspect-Oriented Programming)는 다음과 같은 기능을 제공합니다:
- 선언적 트랜잭션 관리
- 사용자 정의 관점(Aspect) 정의
AOP 주요 개념
- 횡단 관심사(Cross-cutting Concerns): 여러 모듈에 걸쳐 반복되는 공통 기능으로, 비즈니스 로직과는 분리되어야 하지만 반드시 처리해야 하는 부분입니다. 예: 로깅, 보안, 캐싱, 트랜잭션 등
- 관점(Aspect): 횡단 관심사를 모듈화한 객체로, 일반적으로 클래스 형태로 구현됩니다.
- 어드바이스(Advice): 관점에서 실제로 수행할 작업으로, 메서드 단위로 정의됩니다.
- 타겟(Target): 어드바이스가 적용될 대상 객체입니다.
- 프록시(Proxy): 타겟 객체에 어드바이스를 적용하여 생성된 객체입니다.
- 포인트컷(Pointcut): 어드바이스가 적용될 위치를 정의하는 표현식입니다.
- 조인포인트(Join Point): 포인트컷과 일치하는 실제 실행 지점입니다.
예제 준비 코드
public interface UserRepository {
void create();
void modify();
void remove();
void find();
}
public class UserRepositoryImpl implements UserRepository {
public void create() {
System.out.println("사용자 추가 완료");
}
public void modify() {
System.out.println("사용자 정보 수정");
}
public void remove() {
System.out.println("사용자 삭제 처리");
}
public void find() {
System.out.println("사용자 조회 결과");
}
}
방법 1: Spring API 인터페이스 활용
Spring에서 제공하는 표준 API를 사용하여 구현합니다.
public class BeforeExecutionLogger implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getSimpleName() + " 클래스의 " + method.getName() + " 메서드 시작");
}
}
public class AfterExecutionLogger implements AfterReturningAdvice {
public void afterReturning(Object result, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getSimpleName() + " 클래스의 " + method.getName() + " 메서드 종료. 반환값: " + result);
}
}
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userRepository" class="com.example.repository.UserRepositoryImpl"/>
<bean id="beforeLogger" class="com.example.logger.BeforeExecutionLogger"/>
<bean id="afterLogger" class="com.example.logger.AfterExecutionLogger"/>
<aop:config>
<aop:pointcut id="repositoryMethods" expression="execution(* com.example.repository.UserRepositoryImpl.*(..))"/>
<aop:advisor advice-ref="beforeLogger" pointcut-ref="repositoryMethods"/>
<aop:advisor advice-ref="afterLogger" pointcut-ref="repositoryMethods"/>
</aop:config>
</beans>
방법 2: 사용자 정의 AOP 구현
사용자가 직접 관점을 정의하여 구현합니다.
public class CustomAspectHandler {
public void preProcess() {
System.out.println("[사전 처리] ===================");
}
public void postProcess() {
System.out.println("[사후 처리] ===================");
}
}
<bean id="customAspect" class="com.example.aspect.CustomAspectHandler"/>
<aop:config>
<aop:aspect id="customAspectHandler" ref="customAspect">
<aop:pointcut id="targetMethods" expression="execution(* com.example.repository.UserRepositoryImpl.*(..))"/>
<aop:before method="preProcess" pointcut-ref="targetMethods"/>
<aop:after method="postProcess" pointcut-ref="targetMethods"/>
</aop:aspect>
</aop:config>
방법 3: 애노테이션 기반 구현
애노테이션을 사용하여 AOP를 설정합니다. 먼저 자동 프록시 설정이 필요합니다.
<aop:aspectj-autoproxy/>
<bean id="annotatedAspect" class="com.example.aspect.AnnotatedAspectHandler"/>
@Aspect
public class AnnotatedAspectHandler {
@Before("execution(* com.example.repository.UserRepositoryImpl.*(..))")
public void logBefore() {
System.out.println("[애노테이션 방식 - 시작]");
}
@After("execution(* com.example.repository.UserRepositoryImpl.*(..))")
public void logAfter() {
System.out.println("[애노테이션 방식 - 종료]");
}
}
참고: execution 표현식
execution 표현식은 포인트컷을 정의할 때 사용되며, 메서드 시그니처를 기반으로 적용 범위를 지정합니다.