2장 스프링 코어(DI, AOP) - AOP
AOP (Aspect oriented programming)
횡단 관심사 (Cross-Cutting Concern) : 구현하고자 하는 비즈니스 로직과 다소 거리가 있으나 여러 모듈에 걸쳐 공통적이고 반복적으로 필요로 하는 처리 내용
대표적 횡단 관심사 : 보안, 로깅, 트랜잭션 관리, 모니터링, 캐시 처리, 예외 처리
횡단 관심사의 분리 (Separation of Cross-Cutting Concern) : 프로그램 안에서 횡단 관심사에 해당하는 부분을 분리해서 한 곳으로 모으는 것.
이를 실현하는 방법을 AOP라 한다.
AOP 개념
- 애스팩트(aspect) : AOP의 단위가 되는 횡단 관심사. 로그 출력, 예외 처리, 트랜잭션 관리 등과 같은 관심사
- 조인 포인트(join point) : 횡단 관심사가 실행될 지점이나 시점(메서드 실행이나 예외 발생 등). 스프링 프레임워크의 AOP 에서는 메서드 단위로 조인 포인트를 잡는다.
- 어드바이스(advice) : 특정 조인 포인트에서 실행되는 코드. 횡단 관심사를 실제로 구현해서 처리하는 부분.
- 포인트컷(pointcut) : 여러 조인 포인트 중 실제로 어드바이스를 적용할 곳을 선별하기 위한 표현식.
- 위빙(weaving) : 애플리케이션 코드의 적절한 시점에 애스펙트를 적용하는 것. 스프링 AOP는 기본적으로 실행 시점에 위빙.
- 타깃(target) : AOP 처리에 의해 처리 흐름에 변화가 생긴 객체.
스프링 프레임워크에서 사용 가능한 어드바이스
Before : 조인 포인트 전에 실행. 예외가 발생하는 경우 제외하고 항상 실행.
After Returning : 조인 포인트가 정상적으로 종료한 후 실행.
After Throwing : 조인 포인트에서 예외가 발생했을 때 실행.
After : 조인 포인트 완료 후 실행. 예외 발생이나 정상 종료 여부 상관 없이 실행.
Around : 조인 포인트 전후에 실행.
스프링 AOP
스프링 AOP에는 DI 컨테이너에서 관리하는 빈들을 타깃으로 어드바이스를 적용하는 기능이 있다. 조인 포인트에 어드바이스를 적용하는 방법은 프락시 객체를 만들어서 대체하는 방법을 쓴다.
어드바이스 적용 후 DI 컨테이너에서 빈을 꺼내 보면 원래 있던 빈 인스턴스가 아니라 프락시 형태로 어드바이스 기능이 덧입혀진 빈이 나온다.
스프링 AOP에는 AspectJ라는 AOP 프레임워크가 포함됨.
자바 기반 설정 방식에서 어드바이스 정의
Before
@Aspect
@Component
public class MethodStartLoggingAspect {
@Before("execution(* *..*ServiceImpl.*(..))")
public void startLog(JoinPoint jp) {
System.out.println("메서드 시작: " + jp.getSignature());
}
}
After returning
@Aspect
@Component
public class MethodNormalEndLoggingAspect {
@AfterReturning(value = "execution(* *..*ServiceImpl.*(..))", returning = "user")
public void endLog(JoinPoint jp, User user) {
System.out.println("메서드 정상 종료: " + jp.getSignature() + "반환값" + user);
}
}
@AfterReturning 애너테이션 returning 속성에 반환값을 받을 매개변수의 이름을 지정하여 정상적으로 종료한 메서드의 반환값을 구할 수 있다.
AfterThrowing
@Aspect
@Component
public class MethodExceptionEndLoggingAspect {
@AfterThrowing(value = "execution(* *..*ServiceImpl.*(..))", throwing = "e")
public void endLog(JoinPoint jp, RuntimeException e) {
System.out.println("메서드 비정상 종료: " + jp.getSignature());
e.printStackTrace();
}
}
@AfterThrowing 애너테이션 throwing 속성에 예외를 받을 매개변수의 이름을 지정하여 발생한 예외가 무엇인지 알 수 있다.
After throwing 어드바이스에서 예외를 다시 던져 전파할 수 있다.
Around
@Aspect
@Component
public class MethodLoggingAspect {
@Around("execution(* *..*ServiceImpl.*(..))")
public Object log(ProceedingJoinPoint jp) throws Throwable {
System.out.println("메서드 시작: " + jp.getSignature());
try {
Object result = jp.proceed();
System.out.println("메서드 정상 종료: " + jp.getSignature() + " 반환값=" + result);
return result;
} catch (Exception e) {
System.out.println("메서드 비정상 종료: " + jp.getSignature());
e.printStackTrace();
throw e;
}
}
}
Around 어드바이스는 메서드의 실행 전과 후의 처리는 물론, 포인트컷이 적용된 대상 메서드 자체도 실행할 수 있다.
포인트컷 표현식
메서드명으로 조인 포인트 선택
execution(* com.example.user.UserService.*(..))
com.example.user.UserService 클래스에서 임의의 메서드를 대상으로 한다.
execution(* com.example.user.UserService.find*(..))
com.example.user.UserService 클래스에서 이름이 find로 시작하는 메서드를 대상으로 한다.
execution(String com.example.user.UserService.*(..))
com.example.user.UserService 클래스에서 반환값의 타입이 String인 메서드를 대상으로 한다.
execution(* com.example.user.UserService.*(String, ..))
com.example.user.UserService 클래스에서 첫 번째 매개변수의 타입이 String인 메서드를 대상으로 한다.
타입으로 조인 포인트 선택
within(com.example.service..*)
임의의 클래스에 속한 임의의 메서드 대상. 임의의 클래스는 service 패키지나 이 패키지의 서브 패키지에 속한다.
within(com.example.password.PasswordEncoder+)
PasswordEncoder 인터페이스를 구현한 클래스의 메서드를 대상으로 한다. 단 PasswordEncoder 인터페이스는 com.example.user 패키지에 속한다.
그 밖의 방법으로 조인 포인트 선택
bean(*Service)
DI 컨테이너에 관리되는 빈 가운데 이름이 'Service'로 끝나는 빈의 메서드를 대상으로 한다
@annotation(com.example.annotation.TraceLog)
@TraceLog 애너테이션이 붙은 메서드를 대상으로 한다.
@within(com.example.annotation.TraceLog)
@TraceLog 애너테이션이 붙은 클래스의 메서드를 대상으로 한다.
이처럼 특정 기능 구현 후 그와 관련된 애너테이션을 만들어 둔 것이 있다면 @annotation 지시자나 @within 지시자를 활용하면 좋다.
네임드 포인트컷 활용
네임드 포인트컷 정의
@Component
@Aspect
public class NamedPointCuts {
@Pointcut("within(com.example.web..*)")
public void inWebLayer() {}
@Pointcut("within(com.example.domain..*)")
public void inDomainLayer() {}
}
네임드 포인트컷 활용
@Component
@Aspect
public class MethodLoggingAspect {
@Around("inDomainLayer()")
public Object log(ProceedingJoinPoint jp) throws Throwable {
...
}
}
&&, ||, ! 와 같은 논리 연산자들로 조합할 수도 있다.
@Around("inDomainLayer() || inWebLayer()")
어드바이스 대상 객체와 인수 정보 가져오기
@Around("execution(* *..*ServiceImpl.*(..))")
public Object log(JoinPoint jp) throws Throwable {
// 프락시가 입혀지기 전 원본 대상 객체 반환
Object targetObject = jp.getTarget();
// 프락시를 가져온다
Object thisObject = jp.getThis();
// 인수를 가져온다
Object[] args = jp.getArgs();
// ...
}
JointPoint 인터페이스 메서드 반환값이 Object 타입이므로 형 변환이 필요. 아래 방법은 type-safe
@Around("execution(* com.example.CalcService.*(com.example.CalcInput)) && target(service) && args(input)")
public Object log(CalcSerivce service, CalcInput input) throws Throwable {
// ...
}
스프링 프로젝트에서 활용되는 AOP 기능
트랜잭션 관리
@Transactional 애너테이션을 트랜잭션 관리가 필요한 메서드에 지정하면 복잡한 트랜잭션 관리를 스프링 프레임워크가 대신해준다.
스프링 프레임워크는 해당 메서드가 정상 종료한 것이 확인되면 트랜잭션을 commit 하고, 처리가 실패해서 예외가 발생한 것을 감지하면 트랜잭션을 rollback한다.
인가
스프링 시큐리티에서 제공하는 인가 기능을 AOP 형태로 적용.
권한 제어가 필요한 메서드에 @PreAuthorize 애너테이션을 지정하면 해당 메서드가 호출되기 전에 특정 인가 조건을 만족하는지 확인 가능.
캐싱
메서드에 @Cacheable 애너테이션을 지정하면 메서드의 매개변수 등을 키로 사용해 메서드의 실행 결과를 캐시로 관리할 수 있다.
비동기 처리
비동기 처리를 하고 싶은 메서드에 @Async 애너테이션을 붙여주고 반환값으로 CompletableFuture 타입의 값이나 DeferredResult 타입의 값을 반환하게 만들면 해당 메서드는 AOP 방식으로 별도의 스레드에서 실행될 수 있다.
재처리
스프링 Retry 프로젝트 활용. 메서드에 @Retryable 애너테이션을 붙여 해당 메서드가 정상 처리되지 않은 경우 원하는 조건을 만족할 때 까지 재처리.
프로퍼티 관리
@Value 애너테이션을 통해 별도로 분리된 프로퍼티 값을 소스코드에 주입해서 쓸 수 있다.
빈 정의 시 프로퍼티 활용
// application.properties
datasource.username=demo
datasource.password=pass
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig {
// ...
}
Datasource dataSource(@Value("${datasource.username}") String username, @Value("${datasource.password}") String password) {
BasicDataSource dataSource = new BasicDataSource();
datasource.setUsername = username;
datasource.setPassword = password;
return dataSource;
}
스프링 프레임워크의 프로퍼티 관리 기능은 파일명이 '.properties'로 끝나는 자바 프로퍼티 파일 뿐 아니라 JVM 시스템 파라미터, 환경 변수의 설정 값도 모두 일관된 방법으로 프로퍼티처럼 다룰 수 있다.
같은 이름의 프로퍼티가 있다면 다음 우선순위로 프로퍼티가 적용된다.
1. JVM 시스템 프로퍼티
2. 환경 변수
3. 프로퍼티 파일
Author And Source
이 문제에 관하여(2장 스프링 코어(DI, AOP) - AOP), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jasik15/2장-스프링-코어DI-AOP-AOP저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)