여러 메서드에 공통 코드를 추가해야 한다면?
class MyClass {
//핵심기능과 부가기능이 같이 들어 있음.
//관심사(핵심, 부가)를 나눠야 함.
void aaa() {
System.out.println("[befor]{");
System.out.println("aaa() is called.");
System.out.println("}[after]");
}
void aaa2() {
System.out.println("[befor]{");
System.out.println("aaa2() is called.");
System.out.println("}[after]");
}
void bbb() {
System.out.println("[befor]{");
System.out.println("bbb() is called.");
System.out.println("}[after]");
}
}
//부가기능 분리
class MyAdvice {
void invoke(Method m, Object obj, Object... args) throws Exception {
System.out.println("[befor]{");
m.invoke(obj, args); //메서드 호출
System.out.println("}[after]");
}
}
//주기능 분리
//중복코드 제거
class MyClass2 {
void aaa() {
System.out.println("aaa() is called.");
}
void aaa2() {
System.out.println("aaa2() is called.");
}
void bbb() {
System.out.println("bbb() is called.");
}
}
맨 앞, 맨 끝.
중간에는 추가 할 수 없다.
Before Advice
After Advice
Around Advice(Before + After)
관점 지향 프로그래밍.
부가 기능(advice)을 동적으로 추가해주는 기술.
(동적 추가 : 코드가 실행되는 과정에서 자동으로 추가)
메서드의 시작 또는 끝에 자동으로 코드(advice)를 추가.
target : advice가 추가딜 객체.
advice : target에 동적으로 추가될 부가 기능(코드)
join point : advice가 추가(join)될 대상(메서드)
pointcut : join point들을 정의한 패턴.(execution( com.fastcampus..*(...))
proxy : target에 advice가 동적으로 추가되어 생성된 객체.
wweaving : target에 advice를 추가해ㅓㅅ proxy를 생성하는 것.
(OOP, AOP는 모두 변경에 유리한 코드를 만들기 위해 분리 하는 것)
Advice의 설정은 XML과 애너테이션, 두 가지 방법으로 가능
around advice : @Around, 메서드의 시작과 끝 부분에 추가
before advice : @Before, 메서드의 시작 부분에 추가
after advice : @After, 메서드의 끝 부분에 추가
after returning : @AfterReturning, 예외 발생X시 실행되는 부가 기능
after throwing : @AfterThrowing, 예외 발생시 실행되는 부가 기능
advice가 추가될 메서드를 지정하기 위한 패턴
execution(반환타입 패키지명.클래스명.메서드명(매개변수 목록))
//@Order(1), @Order(2) ...
public class LoggingAdvice
@Around("execution(* com.fastcampus.ch3.aop.*.*(..))")
//ProceedingJointPoint pjp //pjp는 메서드의 모든 정보
public Object methodCallLog(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("<<[start] "
+ pjp.getSignature().getName() + Arrays.deepToString(pjp.getArgs()));
Object result = pjp.proceed(); //메서드 호출
System.out.println("result = " + result);
System.out.println("[end]>> "+ (System.currentTimeMillis() - start+"ms");
return result; //메서드 호출결과를 반환
}
}
package com.fastcampus.ch3.aop;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AopMain {
public static void main(String[] args) throws Exception {
MyAdvice myAdvice = new MyAdvice();
Class myClass = Class.forName("com.fastcampus.ch3.aop.MyClass");
Object obj = myClass.newInstance();
for(Method m : myClass.getDeclaredMethods()){
myAdvice.invoke(m, obj, null);
}
}
}
class MyAdvice {
Pattern p = Pattern.compile("a.*");
boolean matches(Method m) {
Matcher matcher = p.matcher(m.getName());
return matcher.matches();
}
void invoke(Method m, Object obj, Object... args) throws Exception {
if (m.getAnnotation(Transactional.class) != null)
System.out.println("[before]{");
m.invoke(obj, args); ///aaa(), aaa2(), bbb() 호출가능
if(m.getAnnotation(Transactional.class) != null)
System.out.println("}[after]");
}
}
class MyClass {
@Transactional
void aaa(){
System.out.println("aaa() is called");
}
void aaa2(){
System.out.println("aaa2() is called");
}
void bbb(){
System.out.println("bbb() is called");
}
}
package com.fastcampus.ch3.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
public class AopMain2 {
public static void main(String[] args) {
ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context_aop.xml");
MyMath mm = (MyMath) ac.getBean("myMath");
mm.add(3, 5);
mm.add(1, 2, 3);
System.out.println("mm.multiply(3, 5) = " + mm.multiply(3, 5));
}
}
package com.fastcampus.ch3.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Aspect
public class LoggingAdvice {
@Around("execution(* com.fastcampus.ch3.aop.MyMath.add*(..))") //pointcut - 부가기능이 적용될 메서드의 패턴
public Object methodCallLog(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("<<[start] "+pjp.getSignature().getName()+ Arrays.toString(pjp.getArgs()));
Object result = pjp.proceed(); //target의 메서드를 호출
System.out.println("result = " + result);
System.out.println("[end]>> "+(System.currentTimeMillis() - start)+"ms");
return result;
}
}