라이브러리는 도서관 아닌가요

Spring xml 설정으로 proxy를 사용해 AOP 적용 본문

Java/AOP

Spring xml 설정으로 proxy를 사용해 AOP 적용

veryhi 2021. 12. 5. 12:26

 

 

 

메서드 invoke에서 다뤄지는 proxy 구현 대신,

 

xml 설정 파일을 이용해서 AOP를 적용할 수 있다.

 

(이전 invoke 관련 : https://verycrazy.tistory.com/31?category=1027221)

 

 

 

 


 

 

 

 

AOP를,

 

단순하게,

 

xml과 proxy만을 사용해서 설정할 때는,

 

setting.xml 파일에

 

타겟(클래스)과 핸들러만 설정해주면 된다.

 

 

 

 

<!-- 속성을 초기화해주는 Namespace p를 사용하기 위해 선언해주어야 함 -->

<bean id="target" class="패키지경로.myClass" p:x="1" p:y="2"></bean>

 

<!-- MethodInterceptor 인터페이스 구현 + invocation 객체 생성 후 .proceed()메서드 호출 + Exception 처리 -->

<bean id="logAroundPrint" class="패키지경로.LogPrint"/> <!-- Around 방식 -->

<!-- MethodBeforeAdvice 인터페이스 구현 + before 메서드 구현 -->

<bean id="logBeforePrint" class="패키지경로.LogPrint"/> <!-- Before 방식 -->

<!-- AfterReturningAdvice 인터페이스 구현 + afterReturning 메서드 구현 + returnValue 사용 가능 -->

<bean id="logAfterReturningPrint" class="패키지경로.LogPrint"/> <!-- After Returning 방식 -->

<!-- ThrowAdvice 인터페이스 구현 + 특정 메서드 구현 없음 ! (위의 메서드와 달리 고정된 인자가 없다.) -->

<!-- 대신 특정 Exception을 인자로 받아 처리를 수행한다. -->

<bean id="logAfterThrowingPrint" class="패키지경로.LogPrint"/> <!-- After 방식 -->

 

<!-- id의 proxy는 실제 그 proxy 역할을 하는 녀석 -->

<bean id="proxy" class="org.springframework.aop.framework.proxyFactoryBean">

       <!-- ref가 가리키는 target 메서드가 호출된다. -->

       <!-- property의 name은 setter명이다. 암묵적으로 앞에 set이 붙고 첫 글자 대문자로 변경 -->

       <property name="target" ref="target">

       <property name="interceptorNames"> <!-- 참고: 인터셉터는 복수로 들어간다, 아래처럼. -->

             <list>

                   <value>logAroundPrint</value> <!-- Around -->

                   <value>logBeforePrint</value> <!-- Before -->

                   <value>logAfterReturningPrint</value> <!-- logAfter -->

                   <value>logAfterThrowingPrint</value> <!-- AfterThrowing -->

             </list>

       </property>

</bean>

 

설정을 변경함으로써 proxy를 이용해

 

추가적인 비즈니스 로직을 빼거나 꽂을 수 있다.

 

java 코드에서 berfore 메서드 따위를 이용해 훨씬 간결하게 구현할 수도 있다.

 

 

 

 

 


 

 

 

 

 

위는 proxy로 날 것(?)을 구현한 것이고, 아래부터는 스프링의 weaving 방식을 이용해 advisor를 도입해보자.

 

잊지 말아야 할 기본 핵심: Proxy를 이용해서 별도로 수행하는 작업을 주입한다.

* 포인트컷(pointcut, 잘라내는(?) 지점)
: Proxy 내부에서 실행될 주 업무 이외의 로직이 자리할 위치. before나 after 등에 주 업무 외의 업무가 수행된다.

* 조인포인트(joinPoint, 합류 지점)
: 주 업무 로직인 모든 타겟 메서드들이 조인포인트가 될 수 있다.

* 위빙(weaving, 뜨개질)
: 필요한 업무 메서드(조인포인트)를 포인트컷(Proxy 내부에서 실행될 주 업무 로직이 들어갈 위치)에 주입

 

 

즉 여기서 구현할 핵심적인 내용은 특정한 메서드만이 weaving이 되도록 joinpoint를 지정하는 것이다.

 

 

 

 

 

<!-- 속성을 초기화해주는 Namespace p를 사용하기 위해 선언해주어야 함 -->

<bean id="target" class="패키지경로.myClass" p:x="1" p:y="2"></bean>

 

<!-- MethodInterceptor 인터페이스 구현 + invocation 객체 생성 후 .proceed()메서드 호출 + Exception 처리 -->

<bean id="logAroundPrint" class="패키지경로.LogPrint"/> <!-- Around 방식 -->

<!-- MethodBeforeAdvice 인터페이스 구현 + before 메서드 구현 -->

<bean id="logBeforePrint" class="패키지경로.LogPrint"/> <!-- Before 방식 -->

<!-- AfterReturningAdvice 인터페이스 구현 + afterReturning 메서드 구현 + returnValue 사용 가능 -->

<bean id="logAfterReturningPrint" class="패키지경로.LogPrint"/> <!-- After Returning 방식 -->

<!-- ThrowAdvice 인터페이스 구현 + 특정 메서드 구현 없음 ! (위의 메서드와 달리 고정된 인자가 없다.) -->

<!-- 대신 특정 Exception을 인자로 받아 처리를 수행한다. -->

<bean id="logAfterThrowingPrint" class="패키지경로.LogPrint"/> <!-- After 방식 -->

 

<bean id="proxy" class="org.springframework.aop.framework.proxyFactoryBean">

       <!-- property의 name은 setter명이다. 암묵적으로 앞에 set이 붙고 첫 글자 대문자로 변경 -->

       <!-- value 또는 ref를 사용하는데 target은 현재 클래스이므로 참조 변수(ref)를 사용한다. -->

       <property name="target" ref="target">

       <property name="interceptorNames"> <!-- 참고: 인터셉터는 복수로 들어간다. 아래처럼. -->

             <list>

                   <value>logAroundPrint</value> <!-- Around -->

                   <value>logBeforePrint</value> <!-- Before -->

                   <value>logAfterReturningPrint</value> <!-- logAfter -->

                   <value>logAfterThrowingPrint</value> <!-- AfterThrowing -->

             </list>

       </property>

</bean>

 

<!-- 특정 메서드를 기준으로 발생하는 포인트 컷 생성 -->

<bean id="myPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">

       <property name="mappedName" value="특정_메서드명"></property>       

</bean>

<!-- advice와 pointcut을 연결한 advisor 설정: 윗쪽에 미리 생성해놓은 Before advice를 가져온다. -->

<bean id="logBeforePrint" class="org.springframework.aop.support.DefaultPointcutAdvisor>

       <property name="advice" ref=""></property>

       <property name="pointcut" ref=""></property>

</bean>

<!-- advice와 pointcut을 연결한 advisor 설정: 윗쪽에 미리 생성해놓은 After advice를 가져온다. -->

<bean id="logAfterReturningPrint" class="org.springframework.aop.support.DefaultPointcutAdvisor>

       <property name="advice" ref=""></property>

       <property name="pointcut" ref=""></property>

</bean>

 

위에서 지정한 특정_메서드의 전후로 실행되는 pointcut 2개가 존재한다.

 

문제점: 딱 보인다. 코드의 중복. pointcut(혹은 advice) 하나당 advisor를 만들어야 하는 귀찮음이 존재한다.

 

 

 

 

 


 

 

 

 

 

Advisor 생성이 귀찮으니 뭔가 하나로 묶어서 사용할 수 있는 Advisor는 없을까?

 

있다 NameMatchMethodPointcutAdvisor

 

기존의 NameMatchMethodPointcut에서 Advisor를 결합한 녀석이다.

 

위에서 사용했던 코드 중 Pointcut과 하나의 Before Advisor를 생성하는 부분을 합쳐보자.

 

 

 

 

 

<!-- 특정 메서드를 기준으로 발생하는 포인트 컷 생성 -->

<bean id="myPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">

       <property name="mappedName" value="특정_메서드명"></property>       

</bean>

<!-- advice와 pointcut을 연결한 advisor 설정: 윗쪽에 미리 생성해놓은 Before advice를 가져온다. -->

<bean id="logBeforePrint" class="org.springframework.aop.support.DefaultPointcutAdvisor>

       <property name="advice" ref=""></property>

       <property name="pointcut" ref=""></property>

</bean>

 

 

...를 아래와 같이 변경한다.

 

 

<bean id="myPointCut" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

       <property name="advice" ref="logBeforePrint">

       <property name="mappedName" value="특정_메서드명"></property>       

</bean>

 

훨씬 간결해진다.

 

 

 

 

 

다음과 같이 메서드를 더 추가해줄 수도 있다.

 

<bean id="myPointCut" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

       <property name="advice" ref="logBeforePrint"></property>

       <property name="mappedName">

              <list>

                     <value>특정_메서드명1</value>

                     <value>특정_메서드명2</value>

                     <value>특정_메서드명3</value>

                     ...

              </list>

       </property>       

</bean>

 

 

 

 

 

 

위와 같이 메서드가 갈수록 많아질 수 있다. 이럴 때에는 정규식을 이용해 보다 편하게 등록할 수 있다.

 

RegexpMethodPointcutAdvisor를 적용해보자.

 

<bean id="myPointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

       <property name="advice" ref="logBeforePrint"></property>

       <property name="mappedName">

              <list>

                     <value>hello.*</value>

                     <value>.*my.*</value>

              </list>

       </property>       

</bean>

 

 

뭔가 알록달록하게 되었다.

Comments