Spring AOP 완벽 가이드: XML과 애너테이션 기반 구현

Spring AOP 개념과 핵심 용어

Spring AOP는 관점 지향 프로그래밍으로, 로깅, 트랜잭션, 보안 같은 공통 관심사를 비즈니스 로직에서 분리하여 모듈화와 재사용성을 높입니다. 주요 용어는 아래와 같습니다.

  • Join Point (연결점): 향상(advice)을 적용할 수 있는 지점, 보통 메서드 호출을 의미합니다.
  • Pointcut (포인트컷): 실제로 향상을 적용할 메서드입니다. 예를 들어 UserService의 save()와 delete() 메서드가 포인트컷이 됩니다.
  • Advice (조언): 포인트컷에 실행될 공통 기능입니다. 예를 들어 LogUtils의 printLog() 메서드가 advice입니다.
  • Aspect (관점): 포인트컷과 advice를 결합한 모듈입니다. 로깅, 트랜잭션 등이 aspect입니다.
  • Target (대상 객체): advice가 적용될 원본 객체입니다. 예를 들어 UserService 객체가 target입니다.
  • Weaving (위빙): advice를 target에 적용하는 과정입니다. 컴파일, 로드타임, 런타임에 수행됩니다.
  • Proxy (프록시): advice가 적용된 후 생성된 객체로, target을 감싸서 호출을 가로챕니다.

XML 기반 AOP 설정

Spring의 IOC 컨테이너가 자동으로 프록시를 생성하므로, 개발자는 연결점을 선택하고 aspect를 정의한 뒤 XML에 설정만 추가하면 됩니다.

AOP XML 요소

요소설명
<aop:config>AOP 설정의 루트 요소
<aop:aspect>aspect 정의
<aop:pointcut>포인트컷 정의
<aop:before>메서드 호출 전 실행되는 advice
<aop:after>메서드 종료 후 실행되는 advice (finally)
<aop:after-returning>메서드 정상 반환 후 실행
<aop:around>메서드 호출 전후를 감싸는 advice

XML AOP 예제

UserDao 인터페이스

public interface UserDao {
    void insert();
    void delete();
    void update();
    void select();
}

UserDaoImpl 클래스

public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("사용자 정보 추가");
    }
    @Override
    public void delete() {
        System.out.println("사용자 정보 삭제");
    }
    @Override
    public void update() {
        System.out.println("사용자 정보 수정");
    }
    @Override
    public void select() {
        System.out.println("사용자 정보 조회");
    }
}

XmlAdvice 클래스 (aspect 구현)

public class XmlAdvice {
    public void before(JoinPoint jp) {
        System.out.println("[전] 대상: " + jp.getTarget() + ", 메서드: " + jp.getSignature().getName());
    }
    public void afterReturning(JoinPoint jp) {
        System.out.println("[반환 후] 메서드: " + jp.getSignature().getName());
    }
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[주변] 시작");
        Object result = pjp.proceed();
        System.out.println("[주변] 종료");
        return result;
    }
    public void afterThrowing() {
        System.out.println("[예외] 발생!");
    }
    public void after() {
        System.out.println("[후] finally 실행");
    }
}

applicationContext-xml.xml 설정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <aop:aspectj-autoproxy/>
    <context:component-scan base-package="com.xml"/>
    <bean id="userDao" class="com.xml.UserDaoImpl"/>
    <bean id="xmlAdvice" class="com.xml.XmlAdvice"/>
    <aop:config>
        <aop:pointcut id="allMethods" expression="execution(* com.xml.UserDaoImpl.*(..))"/>
        <aop:aspect ref="xmlAdvice">
            <aop:before method="before" pointcut-ref="allMethods"/>
            <aop:after-returning method="afterReturning" pointcut-ref="allMethods"/>
            <aop:around method="around" pointcut-ref="allMethods"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="allMethods"/>
            <aop:after method="after" pointcut-ref="allMethods"/>
        </aop:aspect>
    </aop:config>
</beans>

테스트 클래스

public class TestXmlAop {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-xml.xml");
        UserDao dao = ctx.getBean("userDao", UserDao.class);
        dao.delete();
        System.out.println();
        dao.insert();
        System.out.println();
        dao.select();
        System.out.println();
        dao.update();
    }
}

애너테이션 기반 AOP 설정

Spring은 자바 애너테이션을 통해 간결하게 AOP를 구성할 수 있습니다.

주요 애너테이션

애너테이션설명
@Aspect해당 클래스를 aspect로 선언
@Pointcut포인트컷 표현식 정의
@Before메서드 실행 전 advice
@After메서드 실행 후 advice (finally)
@Around메서드 실행 전후를 감싸는 advice
@AfterReturning메서드 정상 반환 후 advice
@AfterThrowing메서드 예외 발생 후 advice

애너테이션 예제

UserDaoImpl 클래스 (@Component 추가)

@Component("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void insert() {
        System.out.println("사용자 정보 추가");
    }
    @Override
    public void delete() {
        System.out.println("사용자 정보 삭제");
    }
    @Override
    public void update() {
        System.out.println("사용자 정보 수정");
    }
    @Override
    public void select() {
        System.out.println("사용자 정보 조회");
    }
}

AnnoAdvice 클래스 (애너테이션 기반 aspect)

@Aspect
public class AnnoAdvice {
    @Pointcut("execution(* com.xml.UserDaoImpl.*(..))")
    public void pointcut() {}
    @Before("pointcut()")
    public void before(JoinPoint jp) {
        System.out.println("[전] 대상: " + jp.getTarget() + ", 메서드: " + jp.getSignature().getName());
    }
    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint jp) {
        System.out.println("[반환 후] 메서드: " + jp.getSignature().getName());
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[주변] 시작");
        Object result = pjp.proceed();
        System.out.println("[주변] 종료");
        return result;
    }
    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("[예외] 발생!");
    }
    @After("pointcut()")
    public void after() {
        System.out.println("[후] finally 실행");
    }
}

applicationContext.xml 설정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <aop:aspectj-autoproxy/>
    <context:component-scan base-package="com.xml"/>
    <bean id="annoAdvice" class="com.xml.AnnoAdvice"/>
</beans>

테스트 클래스

public class TestAnnotationAop {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao dao = ctx.getBean("userDao", UserDao.class);
        dao.delete();
        System.out.println();
        dao.insert();
        System.out.println();
        dao.select();
        System.out.println();
        dao.update();
    }
}

태그: Spring AOP 어노테이션 XML Pointcut

5월 24일 07:47에 게시됨