나를 기록하다
article thumbnail
반응형

관점 지향 프로그래밍(AOP)

AOP(Aspect Oriented Programming)

  • 어플리케이션 로직에는 핵심 기능과 부가 기능이 존재
  • 핵심 기능: 객체가 제공하는 고유의 기능
  • 부가 기능: 핵심 기능을 보조하기 위한 기능(시간 측정, 로그 추적, 트랜잭션 관리 등)

예시 그림

  • OOP에서 모듈화의 핵심 단위는 클래스, AOP에서 모듈화의 단위는 Aspect
  • Aspect는 여러 타입과 객체에 거쳐서 사용되는 기능(Cross-Cutting, 트랜잭션 관리 등)의 모듈화
  • AOP는 OOP를 대체하는 것이 아닌 보조하는 것이 목적

 

AOP 용어

  • Target: 핵심 기능을 담고 있는 객체 → 부가기능을 부여할 대상
  • Aspect
    • 여러 클래스에 공통적으로 적용되는 공통 관심 사항(AOP의 기본 모듈)
    • Advice + Point Cut
  • Join Point
    • Advice가 적용될 수 있는 위치(메서드 실행, 생성자 호출 등)
    • Spring AOP에서는 메서드 실행 지점으로 제한
  • Point Cut
    • Join Point 중에서 Advice를 적용하기 위한 조건 서술
    • Aspect는 지정한 Point Cutd에 일치하는 모든 Join Point에서 실행
  • Advice
    • 부가 기능, 특정 Join Point에서 취해지는 행동
    • Around, Before, After 등의 타입이 존재
  • Weaving
    • Point Cut으로 결정한 Target의 Join Point에 Advice를 적용하는 것
    • 컴파일 시점, 클래스 로딩 시점, 런타임 시점에서 수행 가능하나 Spring AOP는 런타임에 수행
  • AOP Proxy
    • AOP를 구현하기 위해 AOP Framework에 의해 생성된 객체
    • Spring AOP는 JDK dynamic proxy 또는 CGLIB proxy 사용

 

Point Cut 표현식

  • 포인트컷은 관심 조인 포인트를 결정하므로 어드바이스가 실행되는 시기를 제어할 수 있다.
  • 포인트컷 표현식은 포인트컷 지시자(PCD; Pointcut Designator)로 시작한다.
표현식
execution([접근제어자] 반환타입 [선언타입] 메서드명(파라미터))
대괄호([]): 옵션
  • * 사용 가능

포인트컷 지시자 종류

  • execution : 메서드 실행 조인 포인트를 매칭 한다. 스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다.
  • within : 특정 타입 내의 조인 포인트를 매칭한다.
  • args : 인자가 주어진 타입의 인스턴스인 조인 포인트
  • this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
  • target : Target 객체(스프링 AOP 프록시가 가리키는 실제 대상)를 대상으로 하는 조인 포인트
  • @target : 실행 객체의 클래스에 주어진 타입의 어노테이션이 있는 조인 포인트
  • @within : 주어진 어노테이션이 있는 타입 내 조인 포인트
  • @annotation : 메서드가 주어진 어노테이션을 가지고 있는 조인 포인트를 매칭
  • @args : 전달된 실제 인수의 런타임 타입이 주어진 타입의 어노테이션을 갖는 조인 포인트
  • bean : 스프링 전용 포인트컷 지시자로 빈의 이름으로 포인트컷을 지정
// 모든 공개 메서드 실행
execution(public * *(..))

// set 다음 이름으로 시작하는 모든 메서드 실행
execution(* set*(..))

// AccountService 인터페이스에 의해 정의된 모든 메서드의 실행
execution(* com.xyz.service.AccountService.*(..))

// service 패키지에 정의된 메서드 실행
execution(* com.xyz.service.*.*(..))

// 서비스 패키지 또는 해당 하위 패키지 중 하나에 정의된 메서드 실행
execution(* com.xyz.service..*.*(..))

// 서비스 패키지 내의 모든 조인 포인트
within(com.xyz.service.*)

// 서비스 패키지 또는 하위 패키지 중 하나 내의 모든 조인 포인트
within(com.xyz.service..*)

// AccountService 프록시가 인터페이스를 구현하는 모든 조인 포인트
this(com.xyz.service.AccountService)

// AccountService 대상 객체가 인터페이스를 구현하는 모든 조인 포인트
target(com.xyz.service.AccountService)

// 단일 매개변수를 사용하고 런타임에 전달된 인수가 Serializable과 같은 모든 조인 포인트
args(java.io.Serializable)

// 대상 객체에 @Transactional 애너테이션이 있는 모든 조인 포인트
@target(org.springframework.transaction.annotation.Transactional)

// 실행 메서드에 @Transactional 애너테이션이 있는 조인 포인트
@annotation(org.springframework.transaction.annotation.Transactional)

// 단일 매개 변수를 사용하고 전달된 인수의 런타임 유형이 @Classified 애너테이션을 갖는 조인 포인트
@args(com.xyz.security.Classified)

// tradeService 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(tradeService)

// 와일드 표현식 *Service 라는 이름을 가진 스프링 빈의 모든 조인 포인트
bean(*Service)
출처: https://ittrue.tistory.com/233 [IT is True:티스토리]

프록시(Proxy)

프록시란?

  • 대리인
  • 프록시 서버는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램을 가리킴

Client - Proxy Server - Server

[예시] 프로그래머와 교육생

public class Programmer {
	//필드는 과감히 생략
	
	//프로그래머의 일상
	public void coding() {
		System.out.println("컴퓨터를 부팅한다."); //이전에 수행해야할 사항
		try {			
			System.out.println("열심히 코드를 작성한다."); //핵심 관심 사항
			if(new Random().nextBoolean()) {
				throw new OuchException();
			}
			System.out.println("Git에 Push 한다."); //이상없이 마무리가 되었을 때 수행해야할 사항
		} catch(OuchException e) {
			System.out.println("조퇴를 한다."); //예외발생
		} finally {			
			System.out.println("보람찬 하루를 마무리한다."); //모든 과정이 마무리가 되었을 때 수행해야할 사항
		}
	}
}
public class Student {
	//필드는 과감히 생략
	
	//교육생의 일상
	public void coding() {
		System.out.println("컴퓨터를 부팅한다."); //이전에 수행해야할 사항
		try {			
			System.out.println("열심히 공부를 한다."); //핵심 관심 사항
			if(new Random().nextBoolean()) {
				throw new OuchException();
			}
			System.out.println("Git에 Push 한다."); //이상없이 마무리가 되었을 때 수행해야할 사항
		} catch(OuchException e) {
			System.out.println("조퇴를 한다."); //예외발생
		} finally {			
			System.out.println("보람찬 하루를 마무리한다."); //모든 과정이 마무리가 되었을 때 수행해야할 사항
		}
	}
}
public class OuchException extends RuntimeException {
	
	private static final long serialVersionUID = 1L;
	
	public void handleException() {
		System.out.println("병원가자~");
	}
}

 

Programmer와 Student의 공통점을 뽑아내자.

public interface Person {
	public void coding();
}

 

프록시 객체로 만들고, 생성자 주입을 통해서 Programmer 또는 Student를 주입할 수 있도록 구성하자.

public class PersonProxy implements Person {

	private Person person;
	
	public void setPerson(Person person) {
		this.person = person;
	}
	
	@Override
	public void coding() {
		System.out.println("컴퓨터를 부팅한다."); //이전에 수행해야할 사항
		try {			
			person.coding();//핵심 관심 사항
			if(new Random().nextBoolean()) {
				throw new OuchException();
			}
			System.out.println("Git에 Push 한다."); //이상없이 마무리가 되었을 때 수행해야할 사항
		} catch(OuchException e) {
			e.handleException();
			System.out.println("조퇴를 한다."); //예외발생
		} finally {			
			System.out.println("보람찬 하루를 마무리한다."); //모든 과정이 마무리가 되었을 때 수행해야할 사항
		}
	}

}

 

이렇게  PersonProxy를 작성하면, Programmer과 Student에는 coding 메서드를 오버라이드하고, 공통적인 역할을 제외한 각각의 역할만 가지고 있으면 된다. 마지막으로 테스트 코드는 아래와 같이 작성해볼 수 있다.

public class Test {
	public static void main(String[] args) {
		Programmer p = new Programmer();
		
		PersonProxy px = new PersonProxy();
		px.setPerson(p);
		
		px.coding();
	}
}

Spring AOP

용어 정리

  • before - target 메서드 호출 이전
  • after - target 메서드 호출 이후, java exception 문장의  finally와 같이 도작
  • after returning - target 메서드 정상 동작 후
  • after throwing - target 메서드 에러 발생 후
  • around - target 메서드의 실행 시기, 방법, 실행 여부를 결정

Spring AOP Proxy

  • 실제 기능이 구현된 Target 객체를 호출하면, target이 호출되는 것이 아니라 advice가 적용된 Proxy 객체가 호출됨

Spring AOP Proxy

  • Spring AOP는 기본값으로 표준 JDK dynamic proxy를 사용
  • 인터페이스를 구현한 클래스가 아닌 경우 CGLIB 프록시를 사용

JDK Proxy와 CGLIB Proxy

자세한 구현 과정은 JDK Dynamic Proxy를 참조하길 바란다.

 

반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...