이론에 맞춰 AOP를 자바 코드로만 구현해본다. 다음과 같이 패키지와 클래스를 생성하고
entity 패키지도 복사해오면서 aop로만 변경해준다. 이 때 Annotation은 제거했고 메소드들을 result를 거쳐 반환할 수 있도록 수정했다.
먼저 AOP 개념이 적용되지 않는다면 메소드 내에 주석처리 된 부분이 그대로 들어가 있을 것이다. 이제 Cross-cutting Concern을 분리해 연결해본다.
package spring.aop.entity;
public class NewlecExam implements Exam {
private int kor;
private int eng;
private int math;
private int com;
public NewlecExam() {
// TODO Auto-generated constructor stub
}
public NewlecExam(int kor, int eng, int math, int com) {
super();
this.kor = kor;
this.eng = eng;
this.math = math;
this.com = com;
}
public int getKor() {
return kor;
}
public void setKor(int kor) {
this.kor = kor;
}
public int getEng() {
return eng;
}
public void setEng(int eng) {
this.eng = eng;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getCom() {
return com;
}
public void setCom(int com) {
this.com = com;
}
@Override
public int total() {
// long start = System.currentTimeMillis();
int result = kor+eng+math+com;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//
// long end = System.currentTimeMillis();
//
// String message = (end - start) + "ms 소요 ";
// System.out.println(message);
return result;
}
@Override
public float avg() {
float result = total()/4.0f;
return result;
}
@Override
public String toString() {
return "NewlecExam [kor=" + kor + ", eng=" + eng + ", math=" + math + ", com=" + com + "]";
}
}
다음과 같이 프락시를 통해 연결이 된 모습을 볼 수 있다. 프락시 객체는 새 인스턴스를 만들 때 Core Concern의 클래스로더와 인터페이스들의 클래스, InvocationHandler를 필요로 한다. 아래는 익명 InvocationHandler를 생성한 것이며 invoke 메소드에서 부 업무 로직이 차지한 것을 알 수 있다. 대신 그 사이 어디서 Core Concern에 접근할지를 알려주는 result가 method.invoke를 통해 실행될 것임을 암시한다.
package spring.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import spring.aop.entity.Exam;
import spring.aop.entity.NewlecExam;
public class Program {
public static void main(String[] args) {
Exam exam = new NewlecExam(1, 1, 1, 1);
Exam proxy = (Exam) Proxy.newProxyInstance(NewlecExam.class.getClassLoader(),
new Class[] {Exam.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(exam, args);
long end = System.currentTimeMillis();
String message = (end - start) + "ms 소요 ";
System.out.println(message);
return result;
}
});
System.out.println("total is " + proxy.total());
System.out.println("avg is " + proxy.avg());
}
}
생성된 프락시 인스턴스는 Object 타입이기 떄문에 이를 형변환해서 사용한다