티스토리 뷰
초보 웹 개발자를 위한 스프링 5 프로그램 입문을 보고 복습 겸 개인 학습 정리입니다. (windows 기준)
AOP
Aspect Oriented Programming의 약자 (관점 지향 프로그래밍)
여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법
핵심 기능과 공통 기능의 구현을 분리하여 핵심 기능을 구현한 코드의 수정 없이 공통 기능을 적용할 수 있게 만들어 준다.
기본 개념은 핵심 기능에 공통 기능을 삽입하는 것.
핵심 기능의 코드를 수정하지 않으면서 공통 기능의 구현을 추가하는 것이 AOP
공통 기능을 Aspect라고 함
스프링 AOP는 프록시 객체를 자동으로 만들어준다.
3가지 방법
- 컴파일 시점에 코드에 공통 기능을 삽입하는 법
- 클래스 로딩 시점에 공통 기능을 삽입하는 법
- 런타임에 프록시 객체를 생성해서 공통 기능을 삽입하는 법 (스프링이 제공하는 방법)
AOP 주요 용어
용어 | 의미 |
Advice | 언제 공통 관심 기능을 핵심 로직에 적용할 지 정의 |
Joinpoint | Advice를 적용 가능한 지점을 의미 메서드 호출, 필드 값 변경 등이 Joinpoint에 해당 |
Pointcut | Joinpoint의 부분 집합 실제 Advice가 적용되는 Joinpoint를 나타낸다 |
Weaving | Advice를 핵심 로직 코드에 적용하는 것을 Weaving이라 함 |
Aspect | 여러 객체에 공통으로 적용되는 기능을 의미, 트랜젝션이나 보안 등이 좋은 예 |
Advice 종류
스프링은 프록시를 이용하여 메서드 호출 시점에 Aspect를 적용하기 때문에 구현 가능한 Advice 종류는 아래와 같다.
Before Advice, After Advice 등 이있지만 보통 Around Advice를 사용한다.
AroundAdvice - 익셉션 발생 여부와 상관 없이 대상 객체의 메서드 실행 후 공통 기능 실행
스프링 AOP구현
스프링 AOP는 프록시 객체를 자동으로 만들어준다.
스프링 AOP를 이용하여 공통 기능을 구현하고 적용하는 방법
- Aspect로 사용할 클래스에 @Aspect 애노테이션 붙이기
- @Pointcut 애노테이션으로 공통 기능을 적용할 Pointcut 정의 execution 명시자로 범위 설정
- 공통 기능을 구현한 메서드에 @Around 애노테이션 적용
※ @Around에도 execution 명시자를 적용할 수 있다.
예제 프로젝트 만들기 (메이븐 기준) 예제 코드
sp5-chap07 프로젝트 폴더 생성
프로젝트(sp5-chap07) 하위 폴더로 src/main/java 생성
sp5-chap07 폴더 내부에 pom.xml 생성
+ pom.xml 파일에 aspectjweaver dependency 추가
이클립스에서 sp5-chap07 폴더에 생성한 메이븐 프로젝트 import
→ sp5-chap07/src/main/java/config/AppCtx.java
package aspect;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
@Aspect // 스프링 AOP 적용할 클래스에 붙이기
public class ExeTimeAspect {
@Pointcut("execution(public * chap07..*(..))") // 공통 기능을 적용할 대상 설정
private void publicTarget() {}
// chap07 패키지와 그 하위 패키지에 위치한 public 메서드를 적용할 대상으로 설정했다는 의미
@Around("publicTarget()")
// ()에 들어있는 의미는 해당 메서드에 정의한 Pointcut에 공통 기능을 적용한다는 것을 의미
public Object measure(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.nanoTime();
try {
Object result = joinPoint.proceed();
return result;
} finally {
long finish = System.nanoTime();
Signature sig = joinPoint.getSignature();
System.out.printf("%s.%s(%s) 실행 시간 : %d ns\n",
joinPoint.getTarget().getClass().getSimpleName(),
sig.getName(),Arrays.toString(joinPoint.getArgs()),(finish - start));
}
}
}
→ sp5-chap07/src/main/java/main/MainAspect.java
package main;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import chap07.Calculator;
import config.AppCtx;
public class MainAspect {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(AppCtx.class);
Calculator cal = ctx.getBean("calculator", Calculator.class);
long fiveFact = cal.factorial(5);
System.out.println("cal.factorial(5) = "+ fiveFact);
System.out.println(cal.getClass().getName());
ctx.close();
}
}
[Run As] → [Java Application] 누르면 아래와 같이 출력되는데
RecCalculator.factorial([5]) 실행 시간 : 1683300 ns
→ ExeTimeAspect 클래스의 measure() 메서드가 출력
cal.factorial(5) = 120
→ MainAspect 클래스의 System.out.println("cal.factorial(5) = "+ fiveFact); 출력
jdk.proxy2.$Proxy18
→ MainAspect 클래스의 System.out.println(cal.getClass().getName()); 출력
(스프링이 런타임에 생성한 프록시 객체의 클래스 이름)
프록시 생성 방식
스프링은 AOP를 위한 프록시 객체를 생성할 때 실제 생성할 빈 객체가 인터페이스를 상속하면 인터페이스를 이용해서 프록시를 생성함
빈 객체가 인터페이스를 상속할 때 클래스를 이용해서 프록시를 생성하고자 할 때
@EnableAspectJAutoProxy의 proxyTargetClass 속성을 true로 지정하면 자바 클래스를 상속받아 프록시를 생성한다.
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppCtx {...
.. 생략 ..
}
execution 명시자
기본형식
execution(수식어 패턴? 리턴 타입 패턴 클래스 이름 패턴? 메서드 이름 패턴(파라미터패턴))
수식어 패턴은 생략이 가능 public, protected
※ 스프링 AOPsms public 메서드만 적용할 수 있다.
리턴 타입 패턴은 리턴 타입을 명시
클래스 이름 패턴? 메서드 이름 패턴은 클래스 이름 및 메서드 이름을 패턴으로 명시
파라미터패턴은 매칭될 파라미터에 대해 명시
각 패턴은 ' * '을 이용하여 모든 값을 표현
'..'(점 두 개)를 이용하여 0개 이상이라는 의미를 표현할 수 있다.
예시
예 | 설명 |
execution(public void set*(..)) | 리턴 타입 void 메서드 이름 set으로 시작 파라미터가 0개 이상인 메서드 호출 |
execution(*chap07.*.*()) | chap07 패키지 타입에 속한 파라미터가 없는 모든 메서드 호출 |
execution(*chap07..*.*(..)) | chap07 패키지 및 하위 패키지에 있는 파라미터가 0개 이상인 메서드 호출 |
execution(Long chap07.Calculator.factorial(..)) | 리턴타입이 Long Calculator 타입의 factorial() 메서드 호출 |
execution(* get*(*)) | 이름이 get으로 시작 파라미터가 한 개인 메서드 호출 |
execution(* get*(*, *)) | 이름이 get으로 시작 파라미터가 두 개인 메서드 호출 |
execution( * read*(lnteger, ..)) | 메서드 이름이 read로 시작 첫번째 파라미터 타입이 Integer이며 (int) 한 개 이상 파라미터를 갖는 메서드 호출 |
'study > Spring' 카테고리의 다른 글
chap 08 - DB 연동 jdbcTemplate 쿼리 실행 (0) | 2023.01.18 |
---|---|
chap 08 - DB 연동 DataSource 설정 (1) | 2023.01.13 |
chap 06 - 빈 라이프사이클 & 범위 (0) | 2023.01.09 |
chap 05 - 컴포넌트 스캔 (0) | 2023.01.05 |
chap 04 - 의존 자동 주입 (0) | 2023.01.04 |
- Total
- Today
- Yesterday
- git공부
- 면접을 위한 CS 전공 지식 노트
- Spring
- path variable
- git
- git 공부
- 회원 로그인
- Python
- Magazine K
- 환경 변수 설정
- 회원가입
- 디자인 패턴
- 웹페이지
- django
- musma
- django-environ
- Django tutorial
- Java
- authenticate()
- 혼자 공부하는 파이썬
- 암호화
- 검색 결과 내 페이지네이션
- API
- 배열
- 톰캣
- 한글 형태소 분석기
- error: failed to push some refs to 'https://github.com/
- 커맨드 객체
- python3
- django.contrib.auth
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |