[Java Spring] AOP 개념 및 사용 방법(AspectJ, xml, annotation)
AOP (Aspect Oriented Programming), 관점 지향 프로그래밍
어떤 로직을 기준으로 핵심적인 관점(비즈니스 로직), 부가적인 관점을 나누어 그 관점을 기준으로 모듈화
- 요청(Request)에 대해 핵심 관심사항(Aspect)과 부가 관심사항으로 나눠 관점을 기준으로 프로그램을 구현하는 기법
- OOP: 사용자의 관점에서 필요한 핵심적인 비즈니스 로직을 구현하는데 있어 객체(클래스)를 모듈화함으로써 반복되는 코드를 줄임
- AOP: OOP의 개념에 더해, 어플리케이션 전체에 사용되는 부가기능(Aspect)들을 모듈화, 공통 기능(Corsscutting Concerns)관리를 더 효율적으로 가능하게 함 (개발, 운영 측면에서 OOP를 더욱 강력하게 만듦)
- 어플리케이션에서 계속 반복 사용되는 코드(Croscutting Concerns)를 Aspect로 모듈화하고 비즈니스 로직에서 분리하여 재사용하는 것이 핵심
1. 스프링 AOP 주요 개념
- Aspect: 여러 클래스에 공통적으로 구현되는 관심사(Concern)를 모듈화한 것(주로 부가기능)
- Advice: 어떤 일을 해야할지에 대한 것, 해야 할 일에 대한 정보를 가지고 있음(Around, Before, After 등 타입 존재)
- Target: Aspect가 가지고 있는 Advice가 적용되는 대상(클래스, 메서드 등).
- JointPoint: Advice가 적용될 위치, 끼어들 수 있는 지점(생성자 호출 전, 생성자 호출 시, 메소드 실행 시 등)
- PointCut: JointPoint의 상세한 스펙을 정의함으로써 구체적으로 Advice가 실행될 지점의 조건을 서술해놓은 것
- Weaving: Aspect를 대상 객체에 연결시켜 Advice 객체로 만드는 과정을 의미.
- AOP Proxy: AOP를 구현하기 위해 AOP 프레임워크에 의해 생성된 객체(JDK Proxy, CGLIB Proxy)
2. Spring AOP Proxy
- 비즈니스 로직이 구현된 Target 객체를 호출하면, Target이 아닌 Advice(부가 기능)가 적용된 Proxy 객체가 호출
- Spring AOP는 기본값으로 표준 JDK dynamic proxy 사용
- 인터페이스를 구현한 클래스가 아닌 경우 CGLIB proxy 사용
- Spring AOP는 Spring 컨테이너가 관리하는 Bean에만 적용 가능
3. AspectJ 통한 AOP 시작하기
3-1. AspectJ 라이브러리(의존성) 추가
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.8</version>
<scope>runtime</scope>
</dependency>
3-2. xml 방식
// 1. Aspect 선언하기
// 핵심 관심사항이 정리된 클래스 (spring/aop/xml/MyAspect.java)
public class MyAspect {
public void before() {
System.out.println("시험을 시작합니다.");
}
public void afterReturning() {
System.out.println("시험을 종료합니다.");
}
public void afterThrowing() {
System.out.println("부정행위로 퇴실처리 되었습니다.");
}
public void after() {
System.out.println("퇴실합니다.");
}
}
// 2. Aspect 클래스 bean 등록하기. (applicationContext.xml)
// Aspect을 적용할 클래스 역시 bean등록해주기.
<bean id="myAspect" class="spring.aop.xml" />
<bean id="midtermExam" class="spring.aop.MidtermExam" />
<!-- 3. PointCut 선언 (Spring AOP는 메서드 실행만 지원)
(applicationContext.xml에서 선언) -->
<aop:config> <!-- 어떤 aspect, pointCut이 적용될지 결정 -->
<!-- 포인트컷 선언시 조인포인트에 대한 표현식과 포인트 컷 이름 포함 필요 -->
<aop:pointcut expression="execution(public void spring.aop.*.doTest())" id="mypt" />
<aop:aspect ref="myAspect">
<!-- advice 타입에 따른 메서드 지정 -->
<aop:before method="before" pointcut-ref="mypt" />
<aop:after-returning method="afterReturning" pointcut-ref="mypt" />
<aop:after-throwing method="afterThrowing" pointcut-ref="mypt" />
<aop:after method="after" pointcut-ref="mypt" />
</aop:aspect>
</aop:config>
3-3. Annotation 방식
<!-- applicationContext.xml 설정 -->
<!-- 1. @AspectJ 활성화 --->
<aop:aspectj-autoproxy />
<!-- 2. bean 등록을 위한 Component scan 설정 -->
<context:component-scan base-package="spring.aop" />
// 3. Bean 등록 및 Aspect설정
@Component
@Aspect
public class MyAspect {
@Pointcut("execution(public String spring.aop.*.doTest())")
public void mypt(){
}
@Before("mypt()")
public void before() {
System.out.println("시험을 시작합니다.");
}
@AfterRetruning("mypt()")
public void afterReturning() {
System.out.println("시험을 종료합니다.");
}
@AfterThrowing("mypt()")
public void afterThrowing() {
System.out.println("부정행위로 퇴실처리 되었습니다.");
}
@After("mypt()")
public void after() {
System.out.println("퇴실합니다.");
}
}