코딩항해기
[Spring] 관점 지향 프로그래밍 AOP (@어노테이션) 본문
xml 설정파일은 @어노테이션으로 대체 가능하므로 어노테이션으로 AOP 처리하는 법에 대해 정리한다.
AOP 어노테이션 종류 (@AspectJ 지원)
@Service (=@Component) | Bean 등록 (핵심관심과 함께 관리하기 위해 Service) |
@Aspect | Aspect 표기 |
@Pointcut | 포인트 컷 지정 |
@After | 핵심관심 전에 해당 메서드 실행 |
@AfterReturning | 핵심관심에서 반환값 발생 이후에 해당 메서드 실행 |
@AfterThrowing | 핵심관심에서 에러 발생 이후에 해당 메서드 실행 |
@Before | 핵심관심 이후로 해당 메서드 실행 |
@Around | 핵심관심 전 후로 해당 메서드 실행 |
환경설정 (xml)
어노테이션을 사용할 때에는 해당 어노테이션들을 인식할 수 있도록 context:componet-scan을 통해 base-package를 지정해야한다. 또한 aop 사용 중이라는 것을 알려주기 위해 aop: aspectj-autoproxy 태그를 작성해야한다. aop태그를 사용하기 때문에 루트 앨리먼트는 xml방식과 동일하다. (루트 컨테이너 xml applicationContext.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!--어노테이션 인식범위 지정-->
<context:component-scan base-package="com.koreait.app.biz.board" />
<context:component-scan base-package="com.koreait.app.biz.member" />
<context:component-scan base-package="com.koreait.app.biz.common" />
<!--AOP를 사용 중이라는 것을 안내설정-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
어노테이션 사용
@Service //bean 등록
@Aspect //Aspect 안내
@Slf4j //(logback사용 AOP와 관계 없음)
public class LogAdvice {
//포인트 컷 설정
@Pointcut("execution(* com.koreait.app.biz..*Impl.selectOne(..))")
public void dtoPointcut() {}; 참조 메서드
//공통 기능 (반환된 후 실행)
@AfterReturning(pointcut = "dtoPointcut()", returning = "dto")
public void dtoLogger(Object dto) {
String logMsg = null;
if(dto instanceof BoardDTO) { //반환된 값이 게시글 DTO일때
logMsg = "dto is BoardDTO: " + dto;
}
else if(dto instanceof MemberDTO) { //반환된 값이 멤버 DTO일때
logMsg = "dto is MemberDTO: " + dto;
}
log.info("AOP log: {}",logMsg);
}
}
기존 bean 태그가 @Service로 변경됐으며 이는 @Component로 작성해도 동일하다. 그러나 핵심관심이 Service 공간에 저장되고 공통기능은 핵심관심과 연관되므로 비슷한 공간에서 관리하기 위해 @Service어노테이션을 사용한다.
bean 등록을 마쳤다면 Pointcut 설정을 한다. Pointcut의 이름을 지정하기 위한 참조 메서드를 작성하고 해당 메서드 위에 Pointcut 어노테이션을 달아 기존 Pointcut 메서드 범위 설정을 작성한다.
Pointcut 설정을 완료했으면 실행할 공통 기능을 설정할 수 있다. 실행할 공통기능 메서드 위에 실행 조건을 담은 어노테이션을 작성하며 After Before와 같은 어노테이션은 설정이 pointcut 메서드명 지정 밖에 없으므로 pointcut= 안내 부분을 생략가능하지만, AfterReturning과 같은 경우는 returning 설정을 해야하므로 생략이 불가하다.
Pointcut 관리
이렇게 작성해도 정상 작동되지만 다른 Advice가 생기면 Pointcut을 재사용하더라도 다시 선언해야하는 불편함이 있고, Pointcut에 대한 응집도도 낮다.
이러한 문제를 해결하기 위해 별도의 클래스를 통해 Pointcut만 따로 관리할 수 있다. 이때 Pointcut 참조 메서드가 공통기능과 함께 있지 않으므로 앞에 클래스명을 명시해야한다.
@Service //bean 등록
@Aspect //Aspect 안내
@Slf4j //(logback사용 AOP와 관계 없음)
public class LogAdvice {
//공통 기능 (반환된 후 실행)
@AfterReturning(pointcut = "PointcutCommon.dtoPointcut()", returning = "dto") //Pointcut 클래스명 명시
public void dtoLogger(Object dto) {
String logMsg = null;
if(dto instanceof BoardDTO) { //반환된 값이 게시글 DTO일때
logMsg = "dto is BoardDTO: " + dto;
}
else if(dto instanceof MemberDTO) { //반환된 값이 멤버 DTO일때
logMsg = "dto is MemberDTO: " + dto;
}
log.info("AOP log: {}",logMsg);
}
}
Pointcut을 관리하는 클래스는 참조 메서드만 가지고 있기 때문에 해당 클래스를 bean으로 등록해 관리할 필요는 없지만 Aspect라는 것은 명시해야한다.
@Aspect
public class PointcutCommon {
@Pointcut("execution(* com.koreait.app.biz..*Impl.selectOne(..))")
public void dtoPointcut() {}
@Pointcut("execution(boolean com.koreait.app.biz..*Impl.*(..))")
public void cudPointcut() {}
}
++++
'Spring' 카테고리의 다른 글
[Spring] springframework 트랜잭션 설정 (0) | 2024.10.18 |
---|---|
[Spring] 템플릿 패턴 : JDBCTemplate 적용 (DAO 고도화) (0) | 2024.10.18 |
[Spring] AOP의 JoinPoint (0) | 2024.10.15 |
[Spring] Filter, Interceptor, AOP (0) | 2024.10.15 |
[Spring] 관점 지향 프로그래밍 AOP (xml) (0) | 2024.10.15 |