AOP(Aspect Oriented Programming)

박찬미·2021년 3월 4일
0

Spring

목록 보기
6/6

AOP

중복되어 흩어져 있는 코드들을 한 곳으로 모으기



  • 중복 코드
class A{

    method a(){

        중복 코드1
        a
        중복 코드2

     }

    method b(){
    
        중복 코드1
        b
        중복 코드2

     }

    method c(){

        중복 코드1
        c
        중복 코드2

     }
}
  • 중복 코드 별도로 빼놓고, 나머지 모으기
class A{

    method a(){
        a
     }

    method b(){
        b
     }

    method c(){
        c
     }

    method p(JoinPoint point){

        중복코드1
        point.execute();
        중복코드2

    }
}

  • JoinPoint
    관심사와 비즈니스 로직이 결합되는 지점
    메소드가 실행되기 전 / 실행된 후
  • Pointcut
    관심사와 비즈니스 로직이 결합되는 지점을 결정하는 것

    구분Pointcut 설정
    @execution메소드를 기준
    @within특정한 타입(클래스) 기준
    this주어진 인터페이스를 구현한 객체 대상
    @args특정 파라미터를 가지는 대상들만 설정
    @annotation특정 어노테이션이 적용된 대상들만
  • Advice
    Aspect는 관심사를 의미
    Advice는 Aspect를 구현한 코드

    구분설명
    Before AdviceTarget의 JoinPoint를 호출하기 전에 실행되는 코드
    After Returning Advice모든 실행이 정상적으로 이루어진 후에 동작하는 코드
    After Throwing Advice예외가 발생한 뒤에 동작하는 코드
    After Advice정상적으로 실행되거나 예외가 발생했을 때 구분 없이 실행되는 코드
    Around Advice메소드의 실행 자체를 제어할 수 있는 가장 강력한 코드(직접 호출, 결과/예외 처리 가능)



1. AOP 구현 방법

  • 컴파일 이용
    A.java -> (AOP) -> A.class
    자바 파일에는 특정 코드가 없지만, 컴파일 한 코드에는 특정 코드가 들어간 것처럼 컴파일 해주는 컴파일러 이용(aspectj 제공)
  • 바이트코드 조작
    A.java -> A.class
    클래스 로더가 A라는 클래스 읽어와서 메모리에 올릴 때 조작, 클래스로더에 특별한 옵션을 붙인 것(바이트코드 조작)
    이는 메모리에 올라온 클래스와 로컬 클래스가 다르다.(aspectj 제공)

    소스코드 -> 컴파일 클래스 파일 -> 클래스 로딩 -> 메모리
    (클래스 로딩할 때 메모리상 클래스의 메소드에 특정 코드 들어가는 것)

  • 프록시 패턴
    스프링 AOP 사용방법
    디자인 패턴 중 하나를 이용해 AOP와 같은 효과를 낸다.
    프록시 패턴 설명

Real-World Analogy 핵심

기존의 코드 다루지 않고, 그 객체를 다른 객체로 바꾸는 방법



2. AOP 예제

  • pom.xml
- AspectJ 설정
	<properties>
		<java-version>1.8</java-version>
		<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
		<org.aspectj-version>1.9.4</org.aspectj-version>
		<org.slf4j-version>1.6.6</org.slf4j-version>
	</properties>
- lombok 디펜던시 추가
		<!-- lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.8</version>
		</dependency>
- AspectJ Weaver 디펜던시 추가
		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>
        
		<!-- AspectJ weaver -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

  • AOP 설정
    root-context.xml → namespaces → aop, context 추가(servlet-context.xml도 마찬가지)
	<context:component-scan base-package="com.wipia.study"></context:component-scan>
	
	<!-- @Aspect 어노테이션을 통해 어드바이스가 동작 하도록 -->
	<aop:aspectj-autoproxy />

  • Advice 작성
package com.wipia.study.aop;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Advice {
	
	/*
	 * Before : 클래스의 메소드 실행 전
	 * within : BoardController 클래스를 지정
	 */
	@Before("within (com.wipia.study.controller.BoardController)")
	public void beforeAdvice() {
		System.out.println("BoardController Before");
	}
	
	/*
	 * After : 메소드 실행 후
	 * execution : getBoardList 메소드 지정 * 로 모든 메소드 지정 가능
	 * 접근지정자 : 생략 가능 ex) public, private
	 * * : 변환 타입
	 * 
	 */
	@After("execution(* com.wipia.study.controller.BoardController.getBoardList(..))")
	public void afterAdvice() {
		System.out.println("after getBoardList");
	}
	
	/*
	 * AfterThrowing : 예외 발생 시
	 * 모든 클래스에서 메소드 호출 에러가 발생했을 때 동작
	 */
	@AfterThrowing(pointcut="execution(* com.wipia*..*.*(..))", throwing="e")
	public void afterThrowingAdvice(Exception e) {
		System.out.println("에러다 : "+e);
	}
	
	/*
	 * 모든 메소드 실행시 얼마나 걸리는지 시간 출력
	 */
	@Around("execution (* com.wipia..*.*(..))")
	public Object time(ProceedingJoinPoint pjp) {
		
		long start = System.currentTimeMillis();
		
		System.out.println("--- Target : "+pjp.getTarget());
		System.out.println("--- Parameter : "+Arrays.toString(pjp.getArgs()));
		
		Object result = null;
		
		try {
			result=pjp.proceed();
		}catch (Throwable e) {
			e.printStackTrace();
		}
		
		long end = System.currentTimeMillis();
		System.out.println("--- Time : "+(end-start));
		
		return result;
	}
	
}

@Aspect : 해당 클래스 객체가 Aspect를 구현한 것이라는 것을 나타내기 위해
@Component : AOP와 관계가 없지만 스프링에서 bean으로 인식하기 위해(전에 bean에서 설명함)


  • 결과




예제출처

0개의 댓글