1. 공통 코드의 분리 → 여러 메서드에 공통 코드를 추가해야 한다면?
class MyClass {
void syrian() { System.out.print("Syrian() is called "); }
void dwarf() { System.out.print("Dwarf() is called "); }
void geobil() { System.out.print("Geobil() is called "); }
}
이렇게 출력하는(메인기능) serian, dwarf, geobil이라는 세가지 메서드가 있을때 메서드의 앞뒤로 특정문구(부가기능)를 추가로 넣어주려면
class MyClass {
void syrian() {
System.out.println("[Before] ");
System.out.print("Syrian() is called ");
System.out.println("[After]");
}
void dwarf() {
System.out.println("[Before] ");
System.out.print("Dwarf() is called ");
System.out.println("[After]");
}
void geobil() {
System.out.println("[Before] ");
System.out.print("Geobil() is called ");
System.out.println("[After]");
}
}
이런식으로 메서드마다 아래위로 매번 출력문(부가기능)을 넣어줘야 한다. 늘 이런 작업을 할수는 없으니 추가할 출력문을 별도의 메서드로 빼서 여러 기능을 하나로 작업하고자 하는것이 AOP의 핵심(관심사 분리 → 분리된 기능을 동적으로 합쳐주는것)
리플렉션 API를 사용해서 구현 하는데 우선 클래스 분리를 하고
class MyAdvice {
void invoke(Method m , Object obj, Object... args) throws Exception {
// m : 호출할 메서드의 메타데이터를 가지고 있는 메서드 객체
// obj : 메서드를 호출할 객체
// args : 가변인수로 받을때
System.out.println("[Before] ");
m.invoke(obj, args); // → 이부분에 MyClass의 해당 메소드들을 호출한다(핵심기능)
System.out.println("[After]");
}
}
class MyClass {
void syrian() { System.out.println("Syrian() is called "); }
void dwarf() { System.out.println("Dwarf() is called "); }
void geobil() { System.out.println("Geobil() is called "); }
}
메인클래스에서 간단하게 리플렉션 API로 호출해보자
import java.lang.reflect.Method;
import java.util.regex.Pattern;
public class AopMain {
public static void main(String[] args) throws Exception {
MyAdvice myAdvice = new MyAdvice();
// 1. 객체생성
Class myClass = Class.forName("com.fastcampus.ch3.aop.MyClass");
// 2. 리플렉션 API를 사용해서 클래스 정보 가져오기
Object obj = myClass.newInstance();
// 3. 클래스 정보를 가지고 객체 생성
for(Method m : myClass.getDeclaredMethods()) {
// 4. 반복문을 통해 해당 클래스에 선언된 모든 메서드를 반환한다
myAdvice.invoke(m, obj, null);
// 5. obj객체에서 메서드(m)를 호출한다
}
}
}
1. 패턴에 따른 적용 : 메서드명에 a가 포함된 메서드에만 적용(syrian, dwarf) → MyAdvice 클래스 수정
class MyAdvice {
Pattern p = Pattern.compile(".*a.*"); // 패턴에 맞는 메서드에만 적용(패턴 : a가 포함)
boolean mathches(Method m) {
Matcher matcher = p.matcher(m.getName());
return matcher.matches();
// 메서드 명과 패턴의 일치 여부 판단
}
void invoke(Method m , Object obj, Object... args) throws Exception {
if(mathches(m))
System.out.println("[Before]");
m.invoke(obj, args);
if(mathches(m))
System.out.println("[After]");
// if문으로 해당 패턴에 맞는 메서드에만 출력
}
}
2. 애너테이션으로 적용하기 : @Transactional
class MyClass {
// 적용할 메서드에 @Transactional 선언
@Transactional
void syrian() { System.out.println("Syrian() is called "); }
void dwarf() { System.out.println("Dwarf() is called "); }
void geobil() { System.out.println("Geobil() is called "); }
}
class MyAdvice {
void invoke(Method m , Object obj, Object... args) throws Exception {
if(m.getAnnotation(Transactional.class) != null)
System.out.println("[Before]");
m.invoke(obj, args);
if(m.getAnnotation(Transactional.class) != null)
System.out.println("[After]");
// if문 : Method객체 m에서 @Transactional이 존재하는지 확인 후 참이면 실행(출력)
}
}
코드를 자동으로 추가한다면 어디에?(영역지정) 앞,뒤,양쪽(앞+뒤)에 지정 가능한데 위에서 if문의 위치로 설정할 수 있다