study/Spring
13_spring_(AOP)
스파이크12
2020. 3. 17. 08:51
관점 지향 프로그래밍(Aspect Oriented Programming)
공통되는 부분을 따로 빼내어 필요한 시점에 해당코드를 추가해주는 기술
Advice : 공통되는 부분을 따로 빼내어 작성하는 메소드
JoinPoint : Advice를 적용될 수 있는 모든 관점(시점)
PointCut : JoinPoint 중 실제 Advice를 적용할 부분
Weaving : 그 시점에 공통 코드를 끼워 넣는 작업(런타임 시 위빙 (Spring)
Aspect : Advice + PointCut (동적) , 여러 객체에 공통으로 적용되는 기능을 분리하여 작성한 클래스
Proxy : 대상 객체를 직접 접근하지 못하게 '대리인'으로써 요청을 대신 받는 기술
@Before("pointcut") / After("pointcut") / Around("pointcut")
* 모든 어드바이스는 JoinPoint 타입의 파라미터를 첫번재 매개변수로 선언해야한다.
단, Around 어드바이스는 joinPoint의 하위 클래스인 ProceedingJoinPoint 타입의 파라미러를 필수로 선언해야한다
LogAdvice
@Component // Spring 컨테이너가 @Aspect가 적용된 객체를
// 제어(IOC)해야 하므로 Bean 으로 등록되어 있어야 함
@Aspect // 해당 클래스가 AOP에 사용될 것이라는 걸 명시
// Aspect = advice + pointcut
public class LogAdvice {
// Pointcut 지정 : advice가 적용될 부분 지정
// execution : 특정 객체(메소드)가 실행(호출) 되는 시점
// execution([접근제한자] [리턴타입] [클래스명] [메소드명] [파라미터])
// * : 모두
// .. : 이하 모두
// *Impl : 클래시명 마지막이 Impl인 클래스
// Aspect는 필드선언이 안됨 메소드로 작성
// 별도의 Pointcut을 지정하여 필요할때 호출하여 사용
// @Pointcut("execute(* com.kh.spring..*Impl.*(..))")
// public void implPointcut() {}
// before Advice
//@Before("execute(* com.kh.spring.*Impl.*(..))")
//@Before("implPointcut()")
@Before("CommonPointcut.implPointcut()")
public void startLog(){
System.out.println("[log] : 비즈니스 로직 시작");
}
// @After : 예외 발생 여부와 관계 없이 무조건 실행됨
// after advice
// @After("execution("* com.kh.spring.*Impl.*(..))")
@After("CommonPointcut.implPointcut()")
public void endLog() {
System.out.println("[log] : 비즈니스 로직 종료");
}
}
공용으로 사용될 Pointcut을 모아둔 클래스
// 공용으로 사용될 Pointcut을 모아둔 클래스
public class CommonPointcut {
@Pointcut("execution(* com.kh.spring.*Impl.*(..))")
public void implPointcut(){}
}
BeforeAdvice
@Component
@Aspect
public class BeforeAdvice {
@Before("CommonPointcut.implPointcut()")
public void beforeLog(JoinPoint jp) {
// jp.getTarget() : 대상 객체 반환
// jp.getSigniture() : 대상 객체 메소드 정보 반환
String className = jp.getTarget().getClass().getSimpleName();
String methodName = jp.getSignature().getName();
System.out.println("-------------------------------------------");
System.out.println("[전처리] : " + className + " - "
+ methodName + "() - start");
}
}
AfterAdvice
@Component // bean 등록
@Aspect // advice + pointcut
public class AfterAdvice {
@After("CommonPointcut.implPointcut()")
public void afterLog(JoinPoint jp) {
// jp.getTarget() : 대상 객체 반환
// jp.getSigniture() : 대상 메소드 반환
String className = jp.getTarget().getClass().getSimpleName();
String methodName = jp.getSignature().getName();
System.out.println("[후처리] : " + className + " - "
+ methodName +"() - end");
System.out.println("------------------------------------------------------------");
}
AroundAdvice
@Component // bean 등록
@Aspect // advice+pointcut
public class AroundAdvice {
// @Around = @Before + @After
// ProceedingJoinPoint.proceed() : 전, 후 처리 기준점 역할
// ProceedingJoinPoint는 JoinPoint를 상속받은 클래스
@Around("CommonPointcut.implePointcut()")
public Object aroundLogs(ProceedingJoinPoint jp) throws Throwable {
// 메소드 수행시간 체크
String methodName = jp.getSignature().getName();
// StopWatch : 스프링에서 제공하는 스톱워치 클래스
StopWatch sw = new StopWatch();
sw.start(); // 시간 측정 시작
// 여기까지가 Before
Object obj = jp.proceed();
// 여기이후가 After
sw.stop(); // 시간측정종료
System.out.println(methodName + "() 수행시간 : "
+ sw.getTotalTimeMillis() + "(ms)");
return obj;
}
}
AfterReturningAdvice (리턴된 데이터 중간에 가져오기)
@Component // bean 등록
@Aspect // advice + pointcut
@public class AfterReturningAdvice {
// login*(*) -> 메소드명이 login으로 시작하는 메소드이면서
// 매개변수(파라미터)가 한개인 메소드
// 리턴된 데이터 강탈
@AfterReturning(pointcut = "execution("* com.kh.spring..*Impl.login*(*))",
returning = "returnObj")
// 위에 선언한 returnObj 가 파라미터로 들어감
public void loginLog(JoinPoint jp, Object returnObj) {
// 접속자 IP 얻어오기
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
.currentRequestAttributes()).getRequest();
String ip = request.getRemoteAddr();
String logMsg = "[IP : " + ip + "] : ";
if(returnObj instanceof Member) {
Member member = (Member)returnObj;
if(member.getMemberId().equals("admin")) {
logMsg += "관리자 로그인";
} else {
logMsg += "ID : " + member.getMemberId() + "로그인";
}
System.out.println(logMsg);
}
}
}
AfterThrowingAdvice (중간에 에러잡기)
@Component
@Aspect
public class AfterThrowingAdvice {
@AfterThrowing(pointcut = "CommonPointcut.implPointcut()",
throwing = "exceptionObj")
public void exceptionLog(JoinPoint jp, Exception exceptionObj) {
// SyntaxErrorException
// -> "SQL 구문 에러"
String logMsg = "예외 발생 내용 : ";
if(exceptionObj instanceof IllegalArgumentException) {
logMsg += "부적합한 값 입력";
} else if(exceptionObj instanceof BadSqlGrammarException) {
logMsg += "SQL 문법 오류";
} else {
logMsg += "기타 예외 발생";
}
System.out.println(logMsg);
}
}