Programming/Java

[Java Spring] AOP 개념 및 사용 방법(AspectJ, xml, annotation)

erinh 2022. 10. 30. 18:12
반응형

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("퇴실합니다.");
  }
}
반응형