Dev/Spring
[Spring] AOP(Aspected-Oriented Programming)
ssyoni
2022. 2. 5. 23:14
반응형
AOP(Aspected-Oriented Programming)
- 스프링과 별개로 방법론이다.
- OOP는 사용자 관점에서의 주 업무 로직을 위주로 구현하는 것. 반면 AOP는 주 업무 로직을 구현함에 있어서 개발자나 운영자 및 관리자가 필요에 따라서 코드를 끼워 넣게 되는 부가적인 기능(ex. 로그 처리, 트렌젝션 등...)을 모듈화 하는 것.
- 즉, 사용자 관점, 개발자 관점, 운영자 관점, 등등..여러 관점들을 고려해서 로직을 분리하고 모듈화 하는 방법론이기 때문에 Aspected 관점 지향적 프로그램이 이라고 한다.
Concern - Primary(Core) Concern & Cross-cutting concern
- primary(Core) Concern : 핵심 로직
- Cross-cutting Concern : 핵심로직을 위아래로 감싸고 있는 부가적인 공통 로직
AOP 구현 방식
- 프록시로 위임과 부가 작업을 통해 AOP를 구현한다.
Proxy
- 프록시란? 기존 코드에 영향을 주지 않고 타깃의 기능을 확장하고 접근방법을 제어하는 방법
- 프록시의 역할
- 타깃으로 요청을 위임
- 부가기능을 수행
- 프록시를 만들면 번거로운 이유
- 타깃의 인터페이스를 구현하고 위임하는 작업이 번거롭다. 부가기능이 필요 없는 메소드까지도 구현해서 일일이 위임해줘야 하는 작업이 필요하다. → 작업량이 많아질수록 부담스러워짐. 또한 타깃의 메소드가 변경되면 함께 수정해줘야 하는 번거로움이 있음.
- 부가기능 코드가 중복될 경우가 많다. EX) 커밋, 롤백 하는 트랜젝션일 경우 DB를 사용하는 모든 기능에 트랜잭션이 들어간다.
⇒ 다이나믹 프록시를 활용한 단점 보완
Dynamic Proxy
- 프록시팩토리에 의해 런타임 시 다이내믹하게 만들어지는 오브젝트.
- 프록시팩토리에게 인터페이스 정보를 제공해주면 해당 인터페이스를 구현한 클래스의 오브젝트를 자동으로 만들어준다
- 인터페이스를 기반으로 Proxy를 생성해주는 방식
- 부가기능은 InvocationHandler를 구현한 오프젝트에 담는다.
- InvocationHandler는 다음과 같은 메서드 한 개만 가진 인터페이스이다.
public Object invoke(Object proxy, Method method, Object[] args)
- 순수 자바로 proxy 만들기
// 타깃 클래스 package noums.aop.entity; public class ExamImpl implements Exam { private int kor; private int eng; private int math; public ExamImpl() { // TODO Auto-generated constructor stub } public ExamImpl(int kor, int eng, int math) { this.kor = kor; this.eng = eng; this.math = math; } 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; } @Override public int total() { // 부가로직 // long start = System.currentTimeMillis(); // 핵심로직 int result = kor+eng+math; // 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() / 3.0f; return result; } }
package noums.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import noums.aop.entity.Exam; import noums.aop.entity.ExamImpl; public class Project { public static void main(String[] args) { Exam exam = new ExamImpl(1,1,1); // 타깃은 인터페이스를 통해 접근하는 습관을 들이자 //자바에서 제공 proxy // loader : 실제 로드할 객체 , // interfaces : 핵심로직이 구현한 인터페이스. 핵심로직이 여러개의 인터페이스를 구현하고있을 수 있기 때문에 배열로 선언해준다. // h : 부가기능을 꽂을 수 있는 부분 Exam proxyExam = (Exam) Proxy.newProxyInstance(Exam.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); // args : exam 에서 구현하고있는 메서드들을 파라미터로 가져옴 // 부가기능 long end = System.currentTimeMillis(); String message = (end-start) + "ms 시간 소요 "; System.out.println(message); return result; } } ); System.out.println("total is "+ proxyExam.avg()); } }
- Exam인터페이스 제공
- 프록시팩토리에게 나이내픽 프록시 만들어달라고 요청
- Exam인터페이스의 모든 메소드를 구현한 오브젝트 생성
- InvokationHandler 인터페이스를 구현한 오브젝트를 제공 → 다이내믹 프록시가 받은 모든 요청을 InvokationHandler 의 invoke() 메소드로 보내줌.
- Exam인터페이스의 메소드가 아무리 많아도 invoke 메소드 하나로 처리할 수 있다.
Sprnig AOP
스프링은 프록시 기반의 AOP 기능을 제공한다. 런타임 시점에 타겟 클래스에 대한 부가기능을 실행해줄 프록시 빈을 생성해준다. (자동 프록시 생성기)
용어
- Aspect : 공통 코드를 모듈화 한 것
- Target : Aspect 가 적용되는 곳
- Advice : 순수하게 부가기능만 실제로 구현된 부분. 부가기능 구현체
- Advice 종류
- Before
- After returnning
- After throwing : 예외가 발생된 후 동작
- Around : 메소드의 실행 자체를 제어할 수 있다.
- Advice 종류
- Joint point : Advice 가 Target에 적용되는 시점
- Point cut : Joint point의 상세 스펙을 정의한 것
- pointcut 종류
execution(@execution) | 메소드를 기준으로 Pointcut을 설정 |
within(@within) | 특정한 타입(클래스)을 기준으로 Pointcut을 설정 |
this | 주어진 인터페이스를 구현한 객체를 대상으로 Pointcut을 설정 |
args(@args) | 특정한 파라미터를 가지는 대상들만을 PointCut으로 설정 |
@annotation | 특정한 어노테이션이 적용된 대상들만을 Pointcut으로 설정 |
@Aspect
@Component
public class MenuCheckAspect {
@Around("execution(public * noums.study.pizzahouse.controller.OrderController.*(..))")
public Object menuCheck(ProceedingJoinPoint joinPoint) throws Throwable {
Object proceed = null;
System.out.println(">>>> MenuCheckAspect start ...");
try {
proceed = joinPoint.proceed(); // 다름 로직 수행
}
} catch (Exception e) {
}
System.out.println(">>>> MenuCheckAspect end ...");
return proceed;
}
}
출처 - newlecture 유튜브 강의
http://ldg.pe.kr/framework_reference/spring/ver2.x/html/aop.html
반응형