본문 바로가기
Programming/Spring

[spring] XML 기반 AOP 설정

by NAMP 2016. 4. 21.

[spring] XML 기반 AOP 설정

AOP 네임스페이스 선언

xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd

또는

aop namespace 체크

AOP 관련 엘리먼트

<aop:config>

<aop:pointcut>

XML 설정 파일에 여러 개 정의 할 수 있음
id 속성을 통해서 이름값을 명시
해당 이름으로 어드바이스 설정에서 참조함

<aop:aspect>

애스팩트는 포이트컷과 어드바이스를 결합한 개념
AOP에서 가장 중요한 핵심 개념

<aop:advisor>

직접 어드바이스를 정의하고자 하는 목적이 아니고 외부에서 정의한 어드바이스를 포인트컷과 연결할 목적으로 사용

어드바이스 설정

위치 선정을 잘 해야 한다.

핵심로직의 메소드가 있다. 할 일을 다하면 리턴한다. 예외가 발생할 수 있다.

public method 핵심로직(){
    `Before`
    …
    예외 발생 {
	`After throwing`
	} finally {
        `After`
	}
    
    ...
    return `After Returning`;
}

가로채기 Around

Advice 클래스 생성

구현

public class Advice {
    public void log(){
        System.out.println("==== log start ======");
        System.out.println("==== log end ======");
    }
}

login 이 핵심로직

@Override
    public UserVO login(String id, String pw) {
        UserVO vo = dao.login(id, pw);
        String msg = "";
        if (vo != null){
            msg = context.getMessage("login.success", new String[]{id}, Locale.KOREA); 
        }else{
            msg = context.getMessage("login.fail", new String[]{id}, Locale.KOREA);
        }
        System.out.println(msg);
        return vo;
    }

applicationContext 에 bean 추가

<bean id="advice" class="aop.Advice" />

aop namespace 체크

pointcut 생성

pointcut 생성

<aop:config>
    <aop:pointcut id="pc"
        expression="execution(* user.service.*Service.login(..))" />
</aop:config>

aspect 생성

    <bean id="advice" class="aop.Advice" />
    
    <aop:config>
        <aop:pointcut id="pc"
            expression="execution(* user.service.*Service.login(..))" />
        <aop:aspect ref="advice">
        </aop:aspect>            
    </aop:config>

before

aspect 로 엮는다.

    <bean id="advice" class="aop.Advice" />
    
    <aop:config>
        <aop:pointcut id="pc"
            expression="execution(* user.service.*Service.login(..))" />
        <aop:aspect ref="advice">
            <aop:before method="log" pointcut-ref="pc"/>
        </aop:aspect>            
    </aop:config>

위빙 된다 라고 표현한다.

함수 호출

UserService service = (UserService) context.getBean("service");
service.login("admin", "a1234");

출력 결과

==== log start ======
==== log end ======
UserDAO_JDBC
로그인 되었습니다. admin님

출력 위치를 변경한다.

<aop:after-returning method="log" pointcut-ref="pc"/>

출력결과

UserDAO_JDBC
로그인 되었습니다. admin님
==== log start ======
==== log end ======

예외가 발생하면 출력되지 않는다.

after

예외 발생해도 출력되도록 수정

<bean id="advice" class="aop.Advice" />
    
    <aop:config>
        <aop:pointcut id="pc"
            expression="execution(* user.service.*Service.login(..))" />
        <aop:aspect ref="advice">
            <aop:after method="log" pointcut-ref="pc"/>
        </aop:aspect>            
    </aop:config>

after-throwing

예외 발생시만 찍는 메소드 생성

    // 예외가 발생했을 경우에만 동작
    public void log_ex(Exception e){
        System.out.println("예외 발생 : " + e.getMessage());
    }

throwing 을 추가한다.

<aop:after-throwing method="log_ex" pointcut-ref="pc" throwing="e"/>

advice에 있는 이름과 일치해야 한다. e

부가기능에서 핵심로직의 정보를 출력하는 경우
joinpoint 를 사용

    public void log(JoinPoint jp){
        System.out.println("==== log start ======");
        System.out.println("이름: "+jp.getSignature().getName());  
        System.out.println("==== log end ======");
    }

실행결과

==== log start ======
이름: login
==== log end ======

joinpoint 는 항상 첫번째 파라미터로 한다.

public class Advice {
    public void log(JoinPoint jp, Object obj){
        System.out.println("==== log start ======");
        System.out.println("이름: "+jp.getSignature().getName());  
        System.out.println("결과값: "+ obj);  
        System.out.println("==== log end ======");
    }
    
    // 예외가 발생했을 경우에만 동작
    public void log_ex(Exception e){
        System.out.println("예외 발생 : " + e.getMessage());
    }
}

after-returning

리턴 값을 출력하도록 수정한다.

    <aop:config>
        <aop:pointcut id="pc"
            expression="execution(* user.service.*Service.login(..))" />
        <aop:aspect ref="advice">
            <aop:after-returning method="log" pointcut-ref="pc" returning="obj"/>            <aop:after-throwing method="log_ex" pointcut-ref="pc" throwing="e"/>
        </aop:aspect>            
    </aop:config>

obj 이름을 일치시킨다

출력 결과

UserDAO_JDBC
로그인 되었습니다. admin님
==== log start ======
이름: login
결과값: UserVO [userid=admin, username=관리자, userpwd=a1234, email=admin@multicampus.co.kr, phone=02-1234, address=서울 역삼]
==== log end ======

Around 어드바이스

Advice 클래스에 around 메소드 생성

public void around(){
        System.out.println("  == around start  == ");
        System.out.println("  == around end  == ");
    }

가로채기 기능을 한다.

<aop:around method="around" pointcut-ref="pc"/>

출력결과

  == around start  == 
  == around end  == 

원래의 메소드가 수행되지 않았다.

재호출 시켜줘야 핵심로직이 돌아간다.

ProceedingJoinPoint 가 반드시 필요하다.

가로챘기 때문에 결과값을 리턴하는 역할까지 해야 한다. 결과값을 가공할 수 있다.

    public Object around(ProceedingJoinPoint pp) throws Throwable{
        System.out.println("  == around start  == ");
        Object obj = pp.proceed();
        System.out.println("  == around end  == ");
        return obj;
    }

실행결과

  == around start  == 
UserDAO_JDBC
로그인 되었습니다. admin님
  == around end  == 

around 성능테스트에 사용가능 하다.

엮어주는 것이 aspect

AOP는 런타임에 결정된다. proxy 가 동작한다.

Advice 매개변수


export 프로젝트 : https://goo.gl/eFQkI4

'Programming > Spring' 카테고리의 다른 글

[spring] 다국어 지원  (0) 2016.04.22
[spring] Annotation 기반 AOP 설정  (0) 2016.04.22
[spring] AOP (Aspect Oriented Programming)  (0) 2016.04.21
[spring] Annotation 기반 설정  (0) 2016.04.20
[spring] DI 관련 Source 내용  (0) 2016.04.20

댓글