스프링 AOP

스프링 AOP

1. AOP소개

Aspect Oriented Programming을 줄여서 AOP를 바라보는 관점을 기준으로 프로그래밍하는 기법을 말한다. AOP를 구현하는 다양한 방법이 존재하지만, 기본적인 개념은 공통 관심 사항을 구현한 코드를 핵심 로직을 구현한 코드 안에 삽입 하는 것이다.

2020/03/18 - [스프링/예제로 배우는 스프링 입문] - 스프링 AOP

1.1 AOP 용어

용어 의미 Jointpoint Adivce를 적용 가능한 지점을 의미한다. 메서드 호출, 필드 값 변경 등이 해당된다. Pointcut JoinPoint의 부분 집합으로서 실제로 Advice가 적용되는 JoinPoint를 나타낸다 Advice 언제 공통 괸심 긴을 핵심 로직에 적용할 지를 정의하고 있다. Weaving Advice를 핵심 로직 코드에 적용하는 것을 weaving이라고 한다. Aspect 여러 객체에 공통으로 적용되는 기능을 Asspect라고 한다.

1.2 세가지 Weaving 방식

- 컴파일 시에 Weaving 하기 (AspectJ에서 사용하고 있는 방식이다.)

핵심 로직을 컴파일 할 때, 알맞은 위치에 공통 코드를 삽입하면, 컴파일 결과 AOP가 적용된 클래스 파일이 생성된 다.

- 클래스 로딩 시에 Weaving 하기

AOP 라이브러리는 JVM이 클래스를 로딩할 때 클래스 정보를 변경할 수 있는 에이전트를 제공한다. 이 에이전트 는 로딩한 클래스의 바이너리 정보를 변경하여 알맞은 위치에 공통 코드를 삽입한 새로운 클래스 바이너리 코드 를 사용하도록 한다. 즉, 원본 클래스 파일은 변경하지 않고 클래스를 로딩할때에 JVM이 변경된 바이트 코드를 사 용하도록 함으로써 AOP를 적용한다.

- 런타임 시에 Weaving 하기

소스 코드나 클래스 정보 자체를 변경하지 않는다. 대신, 프록시를 이용하여 AOP를 적용한다. 프록시 기반의 AOP 는 핵심 로직을 구현한 객체에 직접 접근하는 것이 아니라 중간에 프록시를 생성하여 프록시를 통해서 핵심 로직 을 구현한 객체에 접근하게 된다. 이때, 프록시는 핵심 로직을 실행하기 전 또는 후에 공통 기능을 적용하는 방식으 로 AOP를 구현하게 된다. 메서드가 호출될 때만 Adivice를 적용 할 수 있기 때문에 필드 값 변경과 같은 Joinpoint 에 대해서는 적용할 수 없는 한계가 있다.

2.스프링에서의 AOP

자체적으로 프록시 기반의 AOP를 지원하고 있다. 따라서 스프링 AOP는 메서드 호출 Joinpoint만을 지원한다.

스프링은 세가지 방법으로 AOP를 구현할수 있다.

- XML 스키마 기반의 POJO 클래스를 이용한 AOP구현

- AspectJ에서 정의한 @Aspect 애노테이션 기반의 AOP 구현

- 스프링 API를 이용한 AOP 구현

2.1 구현 가능한 Advice 종류

종류 설명 Before Advice 대상 객체의 메서드 호출전에 공통된 기능을 실행한다. After Returning Advice 대상 객체의 메서드가 익셉션 없이 실행된 이후에 공통 기능을 실행한다. After Throwing Advice 대상 객체의 메서드를 실행하는 도중 익셉션이 발생한 경우 공통 기능을 실행한다 After Advice 대상 객체의 메서드를 실행하는 도중에 익셉션이 발생한 여부에 상관없이 메서드 실행 후 공통 기능을 실행한다. Around Advice

대상 객체의 메서드 실행 전, 후 또는 익셉션 발생 시점에 공통 기능을 실행하는데 사용된다.

3. XML 스키마 기반 AOP

- 의존성 추가

org.springframework spring-aop 4.0.4.RELEASE org.aspectj aspectjweaver 1.7.4

- 클래스 구현

public class Profiler { public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { String signatureString = joinPoint.getSignature().toShortString(); System.out.println(signatureString +"시작"); long start = System.currentTimeMillis(); try { Object result = joinPoint.proceed(); return result; } finally { long finish = System.currentTimeMillis(); System.out.println(signatureString + "종료"); System.out.println(signatureString + "실핼시간:" + (finish - start) + "ms"); } } }

ProceedingJoinpoint를 이용해 Around Advice에 맞는 공통 기능을 구현 할 수 있다.

- 추가

public class MainQuickStart { public static void main(String[] args) { GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:acQuickStart.xml"); WriteArticleService writeArticleService = ctx.getBean("writeArticleService", WriteArticleService.class); writeArticleService.write(new NewArticleRequest("write", "title", "content")); MemberService memberService = ctx.getBean(MemberService.class); MemberRegRequest memberRegReq = new MemberRegRequest("id", "name", "pw"); memberService.regist(memberRegReq); ctx.close(); } }

중간중간에 코드가 생략된게 너무 많다ㅜㅜ;

이렇게 결과가 나온다. WriteArticleServiceImpl, MemberServiceImpl 클래스를 변경하지 않았음에도 trace()메서드 전후로 실행된 결과를 확인 할 수 있다. 공통된 기능을 하나의 클래스로 만들어서 다른 클래스에도 추가한다면 변경없이 공통 기능을 추가하거나 변경할 수 있다는 AOP만의 장점을 보여준다.

4. XML 스키마 기반의 POJO 클래스를 이용한 AOP구현

4.1 Aspect 설정

태그 설명 메서드 실행전에 적용되는 Advice를 정의한다. 메서드가 정상적으로 실행한 후에 적용되는 Advice를 정의한다. 메서드가 익셉션을 발생시킬 때 적용되는 Advice를 정의한다. try-catch 블록에서의 catch 블록과 유사하다 메서드가 정상적으로 실행되는지 또는 익셉션을 발생시키는지 여부에 상관없이 적용되는 Advice를 정의한다. try-catch-finally블록과 비슷하다 메서드 호출 이전, 이후, 익셉션 발생 등 모든 시점에 적용가능한 Advice를 정의한다.

5. @Aspect 애노테이션 기반 AOP

- @Aspect 애노테이션을 이용해서 Aspect 클래스를 구현한다. 이때 Aspect 클래스는 Advice를 구현한 메서드와 Pointcut을 포함한다.

- Xml 설정에서 를 설정한다. @Configuration 기반 자바 설정을 이용한다면

@EnableAspectAutoProxy를 설정한다.

@Aspect public class Profiler { @Pointcut("execution(public * com.test.chapter6..*(..))") private void profileTarget() { } @Around("profileTarget()") public Object trace(ProceedingJoinPoint joinPoint) throws Throwable { String signatureString = joinPoint.getSignature().toShortString(); System.out.println(signatureString +"시작"); long start = System.currentTimeMillis(); try { Object result = joinPoint.proceed(); return result; } finally { long finish = System.currentTimeMillis(); System.out.println(signatureString + "종료"); System.out.println(signatureString + "실핼시간:" + (finish - start) + "ms"); } } }

execution 명시자

excution(수식어 패턴 ?리턴타입패턴 클래스이름패턴?메서드이름 패턴(파라미터패턴))

수식어 : public , protected등이지만 public만 가능

* : 모든값 표현

.. : 0개이상

execution(public * com.test.chapter6..*(..))

수식어 패턴 : public

리턴타입 패턴 : 모든값

클래스이름 패턴 : com.test.chapter6을 포함된 클래스

메소드이름 패턴 : 모든값

파라미터패턴 : 0개이상

within 명시자 : 특정 타입에 속하는 메서드를 Pointcut으로 설정할때 사용된다.

bean 명시자 : 스프링 빈 이름을 이용하여 pointcut을 정의한다.

조합 : && ,|| 이용

from http://b-programmer.tistory.com/20 by ccl(A) rewrite - 2020-03-23 16:20:29

댓글

이 블로그의 인기 게시물

데이터 바인딩 추상화 - propertyEditor

[sts] spring boot groovy 적용 실행 하기

2020 LCK 롤챔스 Spring 경기 재개 및 일정