일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- REST HTTP API
- 배열 탐색
- ubuntu
- 함수형 인터페이스
- 스키마 설계
- Spring 예외처리
- custom exception
- O(log n)
- 코드스테이츠
- char to int
- N:N
- 탐욕 알고리즘
- JAVA 재귀함수
- Spring MVC
- RestControllerAdvice
- Java
- ubuntu 패스워드
- root passwd
- mapstruct
- ubuntu passwd
- 자료구조
- git workflow
- git 설정
- 스키마 디자인
- Spring
- 리눅스 사용권한
- AOP
- file i/o
- set-version
- http 응답코드
Archives
- Today
- Total
개발소설
AOP(Aspect Oriented Programming) 본문
AOP (Aspect Oriented Programming)
- 관점 지향 프로그래밍을 의미
- Core Concerns (핵심 관심사)에서 횡단 관심사(Cross-Cutting Concern)를 분리함으로써 모듈성을 증가시키는 프로그래밍 기법
- 여러 객체에 공통으로 적용할 수 있는 기능을 분리하여 반복 작업을 줄이고 핵심 기능 개발에만 집중 할 수 있다.
- 로깅, 트랜젝션, 보안 등
예를 들자면 어떠한 비즈니스 로직에 실행시간을 측정하는 코드를 만들어서 적용 했는데 이것이 유용해서
다른 비즈니스로직들에도 이것들을 적용하고 싶을떄 적용해야 하는 비즈니스 로직이 몇천개 혹은 몇만개라면?
그 로직들에 일일히 코드를 추가 하는것은 매우 비효율적 이기 때문에
횡단(공통)관심사로 만들어 핵심 로직을 수정하지 않고 공통 기능의 구현을 추가하는것이 좋다.
AOP를 사용하는 이유
- OOP(객체 지향 프로그래밍) 방식의 프로그래밍을 했을 때 여러 곳에서 공통적으로 사용되는 부가 기능의 중복 코드가 발생
- 중복되는 부가 기능에 수정 및 삭제가 필요하게 되면 사용되는 모든 곳에 수정 및 삭제 동작을 해줘야한다.
- 관심 지향 프로그래밍(AOP)은 OOP 방식의 불필요한 반복을 해결하기 위한 방법
- AOP는 OOP를 대체하는것이 아니라 보완하는것 이다.
AOP 용어
애스팩트(Aspect)
- 여러 객체에 공통으로 적용되는 기능
- 어드바이스 + 포인트컷을 모듈화하여 애플리케이션에 포함되는 횡단 기능
- 부가 기능을 정의한 코드인 어드바이스(Advice)와 어드바이스를 어디에 적용할지 결정하는 포인트컷(PointCut)을 합친 개념
조인포인트(join point)
- 조인 포인트는 추상적인 개념이고, AOP를 적용할 수 있는 지점을 의미
- 클래스 초기화, 객체 인스턴스화, 메소드 호출, 필드 접근, 예외 발생과 같은 애플리케이션 실행 흐름에서의 특정 포인트를 의미
- 애플리케이션에 새로운 동작을 추가하기 위해 조인포인트에 관심 코드(aspect code)를 추가할 수 있다.
- 횡단 관심은 조인포인트 전/후에 AOP에 의해 자동으로 추가
- 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한
- 스프링 AOP는 스프링 컨테이너가 관리할 수 있는 빈에만 aop를 적용
어드바이스(Advice)
- 조인포인트에서 수행되는 코드, 부가 기능에 해당
- Aspect를 언제 핵심 코드에 적용할 지를 정의
포인트컷(Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능
- ** 프록시란? 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아 주는것(대리인, 대리자)
- 클라이언트 -> 프록스 -> 타깃
위빙(Weaving)
- 포인트컷으로 결정한 타겟의 조인 포인트에 어드바이스를 적용
- Advice를 핵심 코드에 적용 하는것
- 3가지 적용 방법
- 컴파일 시점에 코드에 공통 기능 적용
- 클래스 로딩 시점에 바이트 코드에 공통 기능 적용
- 런타임 시점에 프록시 객체를 생성하여 공통 기능 적용 - 스프링 aop에서 사용
AOP 프록시(proxy)
- AOP 기능을 구현하기 위해 만든 프록시 객체
- 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시
타겟 (Target)
- 핵심 기능을 담고 있는 모듈로 부가기능을 부여할 대상
- Adivce를 받는 객체이고 포인트컷으로 결정
어드바이저(Advisor)
- 하나의 어드바이스와 하나의 포인트 컷으로 구성
- 스프링 AOP에서만 사용되는 특별한 용어
Advice 종류
- Before - 조인 포인트 실행 이전에 실행
- After returning - 조인 포인트가 정상 완료 후 실행
- After throwing - 메서드가 예외를 던지는 경우에 실행
- After (finally) - 조인 포인트의 동작(정상 또는 예외)과는 상관없이 실행
- Around - 메서드 실행 전 & 후, 예외 발생 시점에 공통 기능을 실행, ProceedingJoinPoint를 사용
pointcut 표현식
- AspectJ는 포인트컷을 편리하게 표현하기 위한 특별한 표현식을 제공
- 포인트컷에는 여러 지시자가 있지만 execution을 많이 사용하고 나머지는 자주 사용하지 않는다.
@Pointcut("execution(* start.aop.order..*(..))")
private void allOrder(){}
- excution( 접근제어자[생략가능] 리턴타입 패키지경로.클래스.메서드(파라미터)
- 리턴타입
- * : 모든 리턴 타입 허용
- void : 리턴타입이 void인 메서드 선택
- !void : 리턴타입이 void가 아닌 메서드 선택
- 패키지 경로
- •com.~~~.클래스명 : 정확하게 이 패키지만 선택
- com.~~~..클래스명 : com.~~~로 시작하는 모든 패키지
- com.~~~..impl.클래스명 : com.~~~.로 시작하고 impl로 끝나는 패키지 선택
- 클래스
- 패키지명.클래스명(OrderService) : 해당 클래스만 선택 (OrderService 클래스만 선택)
- 패키지명.*Service : 클래스이름이 Service로 끝나는 클래스만 선택
- 패키지명.OrderService+ : 해당클래스(OrderService)로부터 파생된 모든 자식 클래스 선택
- 인터페이스면 해당 인터페이스를 구현한 모든 클래스
- 메서드
- *(..) : 모든 메서드 선택 (기본)
- get*(..) : 메서드 이름이 get으로 시작하는 모든 메서드 선택
- 매개변수
- (..) : 매개변수 개수와 타입의 제약이 없음 (기본)
- (*) : 반드시 1개의 매개 변수를 가지는 메서드만 선택
- &&, ||, ! 를 사용하여 결합도 가능
- start.aop.order 하위 패키지의 메서드(조인포인트)와 클래스명이 Service로 끝나는 클래스의 메서드(조인포인트)
- 두개의 조건을 모두 만족하는 조인포인트를 설정할때 (&&)로 결합 아래 예제 확인
- start.aop.order 하위 패키지의 메서드(조인포인트)와 클래스명이 Service로 끝나는 클래스의 메서드(조인포인트)
- 리턴타입
@Pointcut("execution(* start.aop.order..*(..))")
private void allOrder(){}
@Pointcut("execution(* *..*Service.*(..))")
private void allService(){}
@Pointcut("allOrder() && allService()")
private void orderService() {}
AOP 애너테이션 실습
//OrderRepository.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@Slf4j
@Repository
public class OrderRepository {
public String save(String itemId) {
log.info("[orderRepository] 실행");
//저장 로직
if (itemId.equals("ex")) {
throw new IllegalStateException("예외 발생");
}
return "ok";
}
}
// OrderService.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void orderItem(String itemId) {
log.info("[orderService] 실행");
orderRepository.save(itemId);
}
}
// @Aspect로 공통관심사선언
// @Around (포인트컷 표현식)통해 포인트컷 지정
// start.aop.order를 포함한 하위패키지의 모든 메서드(조인 포인트)
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Slf4j
@Aspect
public class Aspect1 {
@Around("execution(* start.aop.order..*(..))")
public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("log -> {}", joinPoint.getSignature());
return joinPoint.proceed();
}
}
// @Pointcut으로 포인트컷 따로 분리
// @Around에서 포인트컷 시그니처(서명)로 접근
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Slf4j
@Aspect
public class Aspect2 {
@Pointcut("execution(* start.aop.order..*(..))")
private void allOrder(){}
@Around("allOrder()")
public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("log -> {}", joinPoint.getSignature());
return joinPoint.proceed();
}
}
// 포인트컷을 공용으로 사용하기위해 외부클래스로 만들기
// 외부에서 호출하기 위해 접근제어자를 public으로 할당
// @Around를 통해 해당클래스경로.포인트컷시그니처로 접근
// @Around("start.aop.order.aop.Pointcuts.allOrder()")
import org.aspectj.lang.annotation.Pointcut;
public class Pointcuts {
@Pointcut("execution(* start.aop.order..*(..))")
public void allOrder(){}
@Pointcut("execution(* *..*Service.*(..))")
public void allService(){}
//두가지 조인포인트에 모두 해당하는경우 포인트컷시그니처를 &&로 결합
@Pointcut("allOrder() && allService()")
public void orderAndService(){}
}
@Slf4j
@Aspect
public class Aspect4 {
@Around("start.aop.order.aop.Pointcuts.allOrder()")
public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("log -> {}", joinPoint.getSignature());
return joinPoint.proceed();
}
@Around("start.aop.order.aop.Pointcuts.orderAndService()") {
..
}
간단 정리
- AOP는 관점지향프로그래밍으로 공통기능과 핵심기능 두가지 관점으로 분리
- 핵심 기능 로직에 공통 기능을 횡단하도록 설계
- 공통기능을 핵심 로직 기능에서 분리함으로써 불필요한 반복작업(공통기능의 내용이 바뀐다거나)을 줄이고 재사용성을 높일수 있다.
- 조인포인트(Join Point) : AOP를 적용할 수 있는 지점을 의미 - Spring AOP는 메서드 실행 지점만 가능
- 어드바이스(Advice) : 조인포인트에서 수행되는 코드
- 포인트컷(Pointcut) : 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 애스팩트(Aspect) : 여러 객체에 공통으로 적용되는 기능, 어드바이스 + 포인트컷
'Spring Framework' 카테고리의 다른 글
DTO(Data Transfer Object) (0) | 2023.04.13 |
---|---|
SpringMVC - Controller (0) | 2023.04.12 |
Spring MVC (0) | 2023.04.12 |
DI - 스프링 컨테이너(Spring Container), 빈(bean) (0) | 2023.04.05 |
Spring Framework 기본 개념 (0) | 2023.04.02 |
Comments