AOP

prana·2023년 12월 3일

JAVA

목록 보기
8/8
post-thumbnail

질문

  1. 299p - Boy.java와 Girl.java에서 초록색 부분이 사라진 이유는?

  • 횡단 관심사이기 때문
  1. 300p - Boy.java와 Girl.java에서 붉은색 부분이 추가된 이유는?
  • 스프링 AOP가 인터페이스 기반으로 작동하기 때문에 그 요건을 충족하기 위해서.
  • CGLiB 라이브러리를 사용해야 할 경우에는 코드를 변경할 수 없는 서드파티 모듈 안에 final로 선언
  1. 299p - Start.java에서 파란색 부분이 변경된 이유는?
  • 굵게 표시된 부분은 스프링 프레임워크를 적용하고 활용하는 부분이다.
  1. 300p - Person.java가 새롭게 나온 이유는 뭘까?
  • 스프링 AOP가 인터페이스 기반이기 때문이다.
  1. 300p - MyAspect.java가 들어온 이유는 뭘까?
  • Boy.java와 Girl.java에서 공통적으로 나타나는 횡단 관심사를 모두 삭제했지만, 결국 누군가는 횡단 관심사를 처리해야 한다. MyAspect.java가 횡단 관심사를 처리하게 된다.
  1. 300p 네번째 문단 첫번째줄 - 빈이 설정되는 이유는?
  • 객체의 생성과 의존성 주입을 스프링 프레임워크에 위임하기 위해서다.
  • 물론 스프링 프레임워크는 객체 생성뿐 아니라, 객체의 생명주기 전반에 걸쳐 빈의 생성에서 소멸까지 관리한다.
  • boy와 girl 빈은 AOP 적용 대상이기에 등록할 필요가 있고, myAspect 빈은 AOP의 Aspect이기에 등록할 필요가 있다.
  1. 300p - <aop:aspectj-autoproxy />는 뭘까?
  • 6.2절에서 설명했던 프록시 패턴을 이용해 횡단 관심사를 핵심 관심사에 주입하는 것이다.
  • 프록시 없이 호출
  • 프록시 이용해 호출
  1. 303p - Pointcut을 왜 Aspect 적용 위치 지정자라고 했을까?
  • 메서드 뿐만 아니라 속성 등에서도 Aspect를 적용할 수 있기 때문

내용

AOP - Aspect? 관점? 핵심 관심사? 횡단 관심사?

  • AOP : Aspect-Oriented Programming 의 약자-> 관점 지향 프로그래밍
  • 스프링 DI -> 의존성(new)에 대한 주입
  • 스프링 AOP -> 👉로직(code) 주입

  • 다수의 모듈에 공통적으로 나타나는 부분이 존재(로깅, 보안, 트랜잭션)하는 것을 횡단 관심사(cross-cutting concern)라고 한다.
  • 자주 보는 의사코드 (볼드체: 공통적으로 나타나는 코드, 횡단 관심사)
  • 밑줄: 핵심 관심사
  • 코드 = 핵심 관심사 + 횡단 관심사


  • Boy, Girl, Start 클래스

  • 스프링 AOP -> 👉로직(code) 주입, 들어갈 위치는?

일단 덤벼 보자 - 실전편

  • AOP 적용 전
// Boy.java
package org.AOP.aop001;

public class Boy {
    public void runSomething(){
        System.out.println("열쇠로 문을 열고 집에 들어간다.");
        try{
            System.out.println("컴퓨터로 게임을 한다.");
        }catch(Exception ex){
            if(ex.getMessage().equals("집에 불남")){
                System.out.println("119에 신고한다.");
            }
        }finally {
            System.out.println("소등하고 잔다.");
        }
        System.out.println("자물쇠를 잠그고 집을 나선다.");
    }
}
package org.AOP.aop001;

public class Girl {
    public void runSomething(){
        System.out.println("열쇠로 문을 열고 집에 들어간다.");
        try{
            System.out.println("요리를 한다.");
        }catch(Exception ex){
            if(ex.getMessage().equals("집에 불남")){
                System.out.println("119에 신고한다.");
            }
        }finally {
            System.out.println("소등하고 잔다.");
        }
        System.out.println("자물쇠를 잠그고 집을 나선다.");
    }
}
// Start.java
package org.AOP.aop001;

public class Start {
    public static void main(String[] args) {
        Boy romeo = new Boy();
        Girl juliet = new Girl();

        romeo.runSomething();
        juliet.runSomething();
    }
}


  • 횡단 관심사는 사라졌고, 오직 핵심 관심사만 남았다.
  • 4개의 파일로 분할해서 개발하는 수고를 해야 하지만, 개발과 유지보수 관점에서는 무척이나 편한 코드가 되었다.
  • AOP를 적용하면서, Boy.java에 단일 책임 원칙을 자연스럽게 적용하게 됐다.
@Before("execution(public void org.AOP.aop002.Boy.runSomething())") 

를 아래처럼 고쳐도 실행 가능하다.

@Before("execution(* runSomething())") // 대상 메서드 실행 전에 이 메서드를 실행하겠다는 의미


필자의 블로그 참고

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    <aop:aspectj-autoproxy />
    <bean id="myAspect" class="org.AOP.aop002.MyAspect"/>
    <bean id="boy" class="org.AOP.aop002.Boy"/>
    <bean id="girl" class="org.AOP.aop002.Girl"/>
</beans>
  • aop002.xml에서는 3개의 빈이 설정돼 있다.
  • 호출하는 쪽에서 romeo.runSomething() 메서드를 호출하면, 프록시가 그 요청을 받아 진짜 romeo 객체에게 요청을 전달한다.

runSomething() 메서드는 호출을 그냥 전달만 할까?

runSomething() 메서드는 주고받는 내용을 감시하거나 조작할 수 있다.

💡- 스프링 AOP는 프록시를 사용, 그런데 호출하는 쪽(romeo.runSomething() 메서드 호출)에서나 호출 당하는 쪽(romeo 객체), 그 누구도 프록시가 존재하는지조차 모른다. 오직 스프링 프레임워크만 프록시의 존재를 안다.

  • 버퍼, 캐시 서버 등도 존재 목적은 달라도, 모두 중간에서 가로채는 것이라는 동일한 일을 한다.

  • <aop:aspectj-autoproxy />는 스프링 프레임워크에게 AOP 프록시를 사용하라고 알려주는 지시자이다. 게다가 auto는 자동으로!

요약

  • 스프링 AOP는 인터페이스 기반이다.
  • 스프링 AOP는 프록시(proxy)기반이다.
  • 스프링 AOP는 런타임(runtime) 기반이다.

일단 덤벼 보자 - 용어편

@Before가 있듯, @After도 있다. 프록시가 개입할 수 있는 시점은 무려 5개나 된다.

PointCut - 자르는 지점? Aspect 적용 위치 지정자!

Pointcut 은 바로 * runSomething()

  • @Before("execution(* runSomething())")은 지금 선언하고 있는 메서드(public void before)를 * runSomething()가 실행되기 전(@Before)에 실행하라는 의미
  • Pointcut : 👉 횡단 관심사를 적용할 타깃 메서드를 선택하는 지시자(메서드 선택 필터)
    • 타킷 클래스의 타깃 메서드 지정자
public void aop002.Boy ~* runSomething()

JoinPoint - 연결점? 연결 가능한 지점!

  • Pointcut은 JoinPoint의 부분 집합

  • 스프링 AOP는 인터페이스를 기반으로 한다.

  • 인터페이스란 추상 메서드의 집합

    스프링 AOP는 메서드에만 적용 가능하다는 결론에 도달

  • Pointcut의 후보가 되는 모든 메서드들이 JoinPoint, 즉 Aspect 적용이 가능한 지점이 된다.

package org.AOP.aop002;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect // AOP에서 사용하겠다는 의미
public class MyAspect { 
    @Before("execution(* runSomething())") // 대상 메서드 실행 전에 이 메서드를 실행하겠다는 의미
  👉 public void before(JoinPoint joinPoint){
        // JoinPoint : @Before에서 선언된 메서드인 aop002.Boy.runSomething()을 의미한다.
        System.out.println("얼굴 인식 확인: 문을 개방하라");
        //System.out.println("열쇠로 문을 열고 집에 들어간다.");
    }
}
  • romeo.runSomething() 메서드를 호출한 상태
    -> JoinPoint : romeo 객체의 runSomething() 메서드
  • juliet.runSomething() 메서드를 호출한 상태
    -> JoinPoint: juliet 객체의 runSomething() 메서드
  • JoinPoint 파라미터를 이용하면 실행 시점에 실제 호출된 메서드가 무엇인지, 실제 호출된 메서드를 소유한 객체가 무엇인지, 호출된 메서드의 파라미터는 무엇인지 등의 정보를 확인할 수 있다.

정리

  • 광의의 JoinPoint란 Aspect 적용이 가능한 모든 지점
  • 협의의 JoinPoint란 호출된 객체의 메서드이다.

Advice - 조언? 언제, 무엇을!

  • pointcut에 언제, 무엇을 적용할지 정의한 메서드(로직)
    • 타깃 객체의 타깃 메서드에 적용될 부가 기능

Aspect - 관점? 측면? Advisor의 집합체!

  • Aspect = Advice들 + Pointcut 들
  • Advice 는 [언제(when), 무엇을(what)]
  • Pointcut은 [어디에(where)]
  • Aspect는 When + Where + What (언제, 어디에, 무엇을)

Advisor - 조언자? 어디서, 언제, 무엇을!

  • Advisor = 한 개의 Advice + 한 개의 Pointcut
  • Advisor는 스프링 AOP에서만 사용하는 용어, 다른 AOP 프레임워크에서는 사용하지 않으며, 스프링 버전이 올라가면서 이제는 쓰지 말라고 권고하는 기능이다.
  • Aspect가 나왔기 때문에, 하나의 Advisor를 사용할 필요가 없어졌기 때문이다.

일단 덤벼 보자 - POJO와 XML 기반 AOP

  • 어노테이션 없이 POJO와 XML 설정 기반으로 변경하면서 앞에서 설명한 개념 살펴보기
변경 전변경 후

AOP 기초 완성

  • After 어드바이스
  • JoinPoint 메서드를 실행한 후에 실행된다.
  • Advice를 만드는 클래스와 스프링 AOP 설정 두 곳이 변경된다.
  1. MyAspect 변경

  2. aop004.xml 추가


  • 어노테이션 기반

눈에 거슬리는 중복되는 내용

package org.AOP.aop006;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect { 
    @Pointcut("execution(* runSomething())")
    private void iampc(){
        // 여긴 무엇을 작성해도 의미가 없어요
    }

    @Before("iampc()")
    public void before(JoinPoint joinPoint){
        System.out.println("얼굴 인식 확인: 문을 개방하라");
    }

    @After("iampc()")
    public void lockDoor(JoinPoint joinPoint){
        System.out.println("주인님 나갔다: 어이 문 잠가!");
    }
}

  • 서비스 추상화란?
    • 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통의 인터페이스로 제어할 수 있게 한 것

  • 서비스 추상화를 해주면서 그것도 일관성 있는 방식을 제공 -> PSA(일관성 있는 서비스 추상화)

0개의 댓글