5.1 Introduction
- Aspect Oriented Programming (AOP)는 프로그램 구조에 대한 다른 관점을 제공함으로써 OOP를 보완함
- OOP의 modularity 주요 단위: class
- AOP의 modularity 주요 단위: aspect
- aspect는 여러 타입과 객체에 걸친 트랜잭션 관리와 같은 문제를 모듈화하는 것을 가능하게 함
- AOP는 Spring의 주요 요소 중 하나임.
- Spring IoC 컨테이너가 AOP에 의존성을 가지지 않지만,
- AOP는 괜찮은 middleware 솔루션을 제공하면서 Spring Ioc를 보완함
- 읽고있는 문서가 Spring 5.0.0.RELEASE 버전인데, 이 문서에서는 Spring 2.0 AOP에 대해서 다룬다고 함
- AOP 기능
- 선언적인 enterprise service를 제공함 (e.g. declarative transaction managemet)
- custom aspect 구현 가능
5.1.1. AOP concepts
- 용어 정리: Spring-specific한 용어가 아님
- Aspect: 여러 class들을 걸쳐 있는 문제를 모듈화한 것
- ex) 트랜잭션 관리
- Spring AOP에서 aspect는 일반 클래스 (schema-based approach) 또는 @Aspect 어노테이션이 붙은 일반 클래스를 사용해서 구현됨
- Join point: 프로그램을 실행 중인 지점
- ex) 메서드 실행, 예외 처리
- Spring AOP에서 join point는 항상 메서드 실행을 나타냄
- Advice: 특정 Join point에서 Aspect에 의해 수행되는 action
- 종류: around, before, after
- Spring을 포함한 많은 AOP 프레임워크에서 advice를 interceptor로 모델화하고, join point 근처에서 interceptor들의 chain을 유지함
- Pointcut: join point와 매치되는 predicate
- Advice는 pointcut expression과 관련이 있음.
- Advice는 pointcut에 매치되는 join point에 실행함 (ex. 특정 이름의 메서드를 실행)
- join point의 개념은 AOP의 중심이고, Spring은 디폴트로 AspectJ pointcut expression 언어를 사용함
- Advice는 pointcut expression과 관련이 있음.
- Introduction: 타입을 대신하여 추가적인 메서드나 필드를 선언하는 것
- Spring AOP는 새로운 인터페이스와 관련된 구현체를 advised object에 도입하는 것을 도움
- ex) bean이 IsModified라는 interface를 구현하게 하기 위해 introduction을 사용할 수 있음. 이것은 caching을 단순화하게 함.
- AspectJ에서는 inter-type declaration으로도 알려져있음
- Target Object (advised object): 하나 이상의 aspect에 의해 advised되는 객체
- Spring AOP는 runtime proxy를 사용해서 구현되기 때문에, 이 객체는 항상 proxied 객체임
- AOP proxy: aspect contract(타겟 메서드 실행 등...)를 구현하기 위해 AOP에 의해 만들어진 객체
- Spring 프레임워크에서 JDK 동적 proxy 또는 CGLIB proxy를 사용함
- Weaving: 타겟 객체를 만들기 위해 aspect와 다른 application 객체를 연결하는 것
- complie time (AspectJ 컴파일러를 사용해서), load time 또는 runtime에 실행될 수 있음
- 다른 pure한 Java AOP 프레임워크와 달리 Spring AOP는 런타임에 weaving을 수행함
- Aspect: 여러 class들을 걸쳐 있는 문제를 모듈화한 것
- Types of advice
- Before advice: join point 전에 실행되는 advice
- 예외를 발생하지 않는 이상, join point로 진행되는 실행 흐름을 막을 수 없음
- After returning advice: join point가 평범하게 끝난 후 실행되는 advice
- ex) 예외 발생 없이 메서드가 끝났을 때
- After throwing advice: 예외가 발생해서 메서드가 종료되면 실행되는 advice
- After (finally) advice: join point가 어떻게 종료되는지와 상관없이 실행되는 advice
- Around advice: 메서드 발생처럼 join point를 감싸고 있는 advice
- 가장 강력한 advice
- method 발생 이전과 이후에 custom behavior를 수행할 수 있음
- 원래의 return 값을 리턴하거나 예외를 발생시켜서, join point로 이동할지 아니면 advised method 실행으로 바로 이동할지 선택해야함
- Before advice: join point 전에 실행되는 advice
- Around Advice: 가장 일반적인 advice
- Spring AOP가 모든 범위의 advice type을 제공하기 때문에 가장 덜 강력한 advice type을 사용하는 것을 ㅜ천함
- ex) 메서드의 리턴 값으로 캐시를 업데이트만 하려고 할때는, around advice보다 after returning advice를 사용해서 구현하는 것이 낫다.
- 가장 구체적인 advice type을 사용하는 코드가 에러가 발생할 가능성이 적다.
- Spring 2.0에서, 모든 advice parameter는 정적으로 타입화되었다.
- Object arrays 대신 적절한 타입의 advice 파라미터를 사용하기 위해서이다.
- join point의 개념은 interception만 제공하는 예전 기술과 AOP를 구분하는 AOP의 주요 요소이다.
- pointcut은 advice가 객체지향 hierarchy를 독립적으로 타겟화하는 것을 가능하게 한다.
- ex) declarative transaction 관리를 제공하는 around advice는 여러 객체들에 걸쳐있는 메서드의 집합(e.g. service layer의 모든 business operation)에 적용될 수 있다.
5.1.2. Spring AOP capabilities and goals
- Spring AOP는 pure한 Java로 구현된다. 특별한 컴파일 절차 이런거 필요없다. Spring AOP는 class loader hierarchy를 제어할 필요가 없기 때문에, 서블릿 컨테이너나 application 서버에 사용하는 것이 적절하다.
- Spring AOP는 메서드 실행 join point만 지원한다. Field interception은 구현되지 않는다. field access를 advise하고 join point를 업데이트할 필요가 있다면, AspectJ와 같은 언어를 참고해라.
- Spring AOP의 AOP 접근은 다른 대다수의 AOP 프레임워크와 다르다.
- Spring AOP의 목적은 가장 완벽한 AOP 구현체를 제공하는 것이 아니다.
- enterprise aplication에서의 일반적인 문제를 해겨하는 것을 돕기 위해 AOP 구현과 Spring IoC 사이의 밀접한 통합을 제공한다.
- 따라서 Spring AOP는 Spring IoC 컨테이너와 일반적으로 결합해서 사용된다.
- Aspect는 일반적인 bean 정의 syntax를 사용해서 구성된다. (다른 AOP 구현체와 다른점!)
- Spring AOP에서 쉽게 할 수 없는 것이 있다.
- fine-grained object를 advise하는 일 (일반적으로, domain objects)
- AspectJ는 이러한 경우 사용하면 좋다.
- 그런데 웬만해서는 Spring AOP는 대부분 문제를 잘 해결한다.
- Spring AOP, AspectJ는 경쟁관계가 아닌 보완관계
5.1.3. AOP Proxies
- Spring AOP는 AOP proxy로 기본적으로 표준 JDK dynamic proxy를 사용함.
- 모든 interface가 proxy화되는 것을 가능하게 함
- Spring AOP는 CGLIB proxy를 사용할 수 있음.
- interface말고도 class를 proxy화하는 것은 필수적임
- 객체가 interface를 구현하지 않는다면 CGLIB가 기본적으로 사용된다.
- 일반적으로 class를 사용하는 것보다 interface를 사용해서 프로그래밍을 하는 것이 더 좋기 때문에, class들은 웬만하면 하나 이상의 interface를 구현하고 있을 것임
- 다음과 같은 상황에 CGLIB의 사용을 강제하는 것이 가능함
- 인터페이스에 정의되지 않은 메서드를 advise하는 경우
- proxied 객체를 concrete type으로써의 메서드로 pass하는 경우
- Spring AOP는 proxy 기반이다.
5.6. Proxing mechanisms
5.6.1. Understanding AOP proxies
- Spring AOP는 proxy 기반이다.
첫번째 상황) plain-vanila, unproxied, straight object reference
public class SimplePojo implements Pojo {
public void foo() {
// 'this' reference에 대한 직접 호출
this.bar();
}
public void bar() {
// some logic...
}
}
인스턴스에서 메서드를 실행시키면, 해당 인스턴스에서 메서드가 직접 호출됨
두번째 상황) client code가 proxy일 때
public class Main {
public static void main(String[] args) {
Pojo pojo = new SimplePojo();
// this is a direct method call on the 'pojo' reference
pojo.foo();
}
}
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
- 여기서 주목해야할 점은 Main 클래스의 main 메서드는 proxy로의 reference를 가진다는 것이다.
- 객체 reference의 메서드 호출은 proxy에 대한 호출을 뜻한다.
- 이와 같은 proxy는 특정한 메서드 호출에 관련된 모든 interceptors(advices)를 위임할 수 있다.
- 하지만 호출이 마지막으로 타겟 객체에 도달한다면 (이 경우에 SimplePojo reference)
- this.bar(), this.foo()와 같이 자체적으로 발생할 수 있는 모든 메서드 호출은 proxy가 아니라 객체 reference(this)에 대해 호출됨
- self-invocation은 method invocation과 관련된 advice가 실행 기회를 얻게 하지 않음
- 해결책) self-invocation이 일어나지 않도록 코드를 refactor하기
세번째 상황) client code와 Spring AOP를 묶기
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
- 좋지 않은 방법이다.
- class 자체가 AOP context에서 사용되고 있음을 인지하게 함. proxy가 생성될 때 추가적인 configuration을 요구함
public class Main {
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.adddInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
factory.setExposeProxy(true);
Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();
}
}
- AspectJ는 proxy 기반 AOP 프레임워크가 아니기 때문에 self-invocation 문제가 없음
Core Technologies
In the scenario above, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do you
docs.spring.io
'백엔드 > Spring' 카테고리의 다른 글
build.gradle와 build 과정 살펴보기 (0) | 2023.04.07 |
---|---|
Docker(도커) (0) | 2023.03.31 |
1. The IoC container (0) | 2023.03.15 |
JPA와 연관관계 (with MySQL) (1) (0) | 2022.10.20 |
JWT 토큰을 이용한 로그인 구현 (1) (0) | 2022.10.13 |
댓글