개키우는개발자 : )

AOP애플리케이션 작성(2) 본문

JAVA/Spring Framework

AOP애플리케이션 작성(2)

DOGvelopers 2019. 2. 8. 15:50
반응형

Spring Framework AOP애플리케이션 작성(2)



학습 목표


  • Aspect 클래스 선언 및 설정
  • Aspect 클래스 구현
  • Aspect 클래스 테스트


1.Aspect 클래스 선언 및 설정


1-1 Spring AOP의 구현방식


XML 기반의 POJO 클래스를 이용한 AOP 구현

- 부가기능을 제공하는 Advice 클래스를 작성한다.

- XML 설정 파일에 <aop:config>를 이용해서 애스펙트를 설정한다.

(즉, 어드바이스와 포인트컷을 설정함)


@Aspect 어노테이션을 이용한 AOP 구현

@Aspect 어노테이션을 이용해서 부가기능을 제공하는 Aspect 클래스를 작성한다. 이때 Aspect 클래스는 어드바이스를 구현하는 메서드와 포인트컷을 포함한다.


- XML 설정 파일에 <aop:aspectj-autoproxy />를 설정한다.



1-2 @Aspect 어노테이션


- Aspect 클래스 선언할 때 @Aspect 어노테이션을 사용한다.


- AspectJ 5버전에 새롭게 추가된 어노테이션이다.


- @Aspect 어노테이션을 이용할 경우 XML 설정 파일에 어드바이스와 포인트컷을 설정하는 것이 아니라 클래스 내부에 정의할 수 있다.


- <aop:aspectj-autoproxy> 태그를 설정파일에 추가하면 @Aspect 어노테이션이 적용된 Bean을 Aspect로 사용 가능하다.


1-3 Aspect 클래스 정보


- 클래스명 : LoggingAspect.java


- 클래스 기능 : 이 Aspect 클래스는 4가지 유형의 어드바이스와 포인트컷을 설정하여 타겟 객체의 파라미터와 리턴값, 예외 발생 시 예외 메시지를 출력하는 기능을 제공


- Advice 유형 : Before, AfterReturning, AfterThrowing, After


- 구현 메서드명 : before(JoinPoint joinPoint) 

afterReturning(JoinPoint joinPoint , Object ret)

afterThrowing(JoinPoint joinPoint , Throwable ex)

afterFinally(JoinPoint joinPoint)


1-4 Aspect 클래스 선언 및 설정


- 클래스 선언부에 @Aspect 어노테이션을 정의한다.


- 이 클래스를 애스펙트로 사용하려면 Bean으로 등록해야 하므로 @Component 어노테이션도 함께 정의한다.


- XML 설정파일에 <aop:aspectj-autoproxy /> 선언한다. 이 선언은 Bean으로 등록된 클래스 중에서 

@Aspect가 선언된 클래스를 모두 애스펙트로 자동 등록 해주는 역할을 한다.



New -> package -> myspring.aop.annot 패키지 생성


myspring.aop.annot 패키지 하위 -> New -> class -> LoggingAspect 클래스 파일 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package myspring.aop.annot;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
 
@Component
@Aspect
public class LoggingAspect {
    
    @Before("execution(public * myspring..*(..))")
    public void before(JoinPoint joinPoint) {
        String signatureString = joinPoint.getSignature().getName();
        System.out.println("@Before [" + signatureString + "] 메서드 실행 전처리 수행");
        for(Object arg:joinPoint.getArgs()) {
            System.out.println("@Before [" + signatureString + "] 아규먼트" + arg);
        }
    }
    
    @AfterReturning(pointcut="execution(public * myspring.user.service.*.*(..))",returning="ret")
    public void afterReturning(JoinPoint joinPoint, Object ret) {
        String signatureString = joinPoint.getSignature().getName();
        System.out.println("@AfterReturning [" + signatureString + "] 메서드 실행 후처리 수행");
        System.out.println("@AfterReturning [" + signatureString + "] 리턴값 = "+ret);
    }
    
    @AfterThrowing(pointcut="execution(* *..UserService*.*(..))",throwing="ex")
    public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
        String signatureString = joinPoint.getSignature().getName();
        System.out.println("@AfterThrowing [" + signatureString + "] 메서드 실행 중 예외 발생");
        System.out.println("@AfterThrowing [" + signatureString + "] 예외 = "+ex);
    }
    
    @After("execution(* *..*.*User(..))")
    public void afterFinally(JoinPoint joinPoint) {
        String signatureString = joinPoint.getSignature().getName();
        System.out.println("@After [" + signatureString + "] 메서드 실행 완료");
    }
}
 
cs


New -> spring -> Spring Bean Configuration File -> annot.xml 생성


New -> spring 검색New -> spring 검색


annot.xml 생성annot.xml 생성


xml namespace 설정xml namespace 설정


1
2
3
4
5
6
7
8
9
10
11
<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
 
    <!-- 어노테이션이 선언된 클래스들을 스캔하기 위한 설정 -->
    <context:component-scan base-package="myspring.di.annot" />
</beans>
 
cs



2.Aspect 클래스 구현


2-1 Advice를 정의하는 어노테이션


- Advice를 정의하기 위하여 아래와 같은 어노테이션을 제공한다.

- @Before("pointcut")

- 타겟 객체의 메서드가 실행되기 전에 호출되는 어드바이스

- JoinPoint를 통해 파라미터 정보를 참조할 수 있다.

- @After("pointcut")

- 타겟 객체의 메서드가 정상 종료 됐을 때와 예외가 발생했을 때 모두 호출되는 어드바이스

- 리턴값이나 예외를 직접 전달받을 수는 없다.

- @Around("pointcut")

- 타겟객체의 메서드가 호출되는 전 과정을 모두 담을 수 있는 가장 강력한 기능을 가진 어드바이스

- @AfterReturning(pointcut="",returning="")

- 타겟 객체의 메서드가 정상적으로 실행을 마친 후에 호출되는 어드바이스

- 리턴값을 참조할 때는 returning 속성에 리턴값을 저장할 변수 이름을 지정해야 한다.

- @AfterThrowing(pointcut="",throwing="")

- 타겟 객체의 메서드가 예외가 발생하면 호출되는 어드바이스

- 발생된 예외를 참조할 때는 throwing 속성에 발생한 예외를 저장할 변수 이름을 지정해야 한다.



2-2 Before 어드바이스

- @Before 어드바이스를 이용해서 실행되는 타겟 객체의 메서드명과 파라미터를 출력하는 어드바이스다.

- 아래의 before 메서드는 myspring 패키지 또는 그 하위 패키지에 있는 모든 public 메서드가 호출되기 이전에 호출된다.

1
2
3
4
5
6
7
8
@Before("execution(public * myspring..*(..))")
public void before(JoinPoint joinPoint) {
    String signatureString = joinPoint.getSignature().getName();
    System.out.println("@Before [" + signatureString + "] 메서드 실행 전처리 수행");
    for(Object arg:joinPoint.getArgs()) {
        System.out.println("@Before [" + signatureString + "] 아규먼트" + arg);
    }
}
cs


2-3 AfterReturning 어드바이스


- @AfterReturning 어드바이스를 이용해서 실행되는 타겟 객체의 메서드명과 리턴값을 출력하는 어드바이스이다.


- 아래의 afterReturning 메서드는 myspring.user.service 패키지 하위에 있는 모든 public 메서드가 정상 종료된 이후에 호출된다.


- 리턴값을 참조할 때는 returning 속성을 이용해서 리턴 값을 담을 변수 이름을 지정해야 한다.


1
2
3
4
5
6
@AfterReturning(pointcut="execution(public * myspring.user.service.*.*(..))",returning="ret")
public void afterReturning(JoinPoint joinPoint, Object ret) {
    String signatureString = joinPoint.getSignature().getName();
    System.out.println("@AfterReturning [" + signatureString + "] 메서드 실행 후처리 수행");
    System.out.println("@AfterReturning [" + signatureString + "] 리턴값 = "+ret);
}
cs


2-4 AfterThrowing 어드바이스


- @AfterThrowing 어드바이스를 이용해서 실행되는 타겟 객체의 메서드명과 예외 메시지를 출력하는 어드바이스이다.


- 아래의 afterThrowing 메서드는 클래스명이 UserService로 시작되는 클래스에 속한 모든 메서드가 예외가 발생된 이후에 호출된다.


- 발생된 예외를 참조할 때는 throwing 속성을 이용해서 예외 객체를 담을 변수 이름을 지정해야 한다.


1
2
3
4
5
6
@AfterThrowing(pointcut="execution(* *..UserService*.*(..))",throwing="ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
    String signatureString = joinPoint.getSignature().getName();
    System.out.println("@AfterThrowing [" + signatureString + "] 메서드 실행 중 예외 발생");
    System.out.println("@AfterThrowing [" + signatureString + "] 예외 = "+ex);
}
cs



2-5 After 어드바이스


- @After 어드바이스를 이용해서 실행되는 타겟 객체의 메서드명을 출력하는 어드바이스이다.


- 아래의 afterFinally 메서드는 메서드명이 User로 끝나는 메서드들이 정상 종료됐을 때와 예외가 발생했을 때 모두 호출된다.


- 반드시 반환해야 하는 리소스가 있거나 메서드 실행 결과를 항상 로그로 남겨야 하는 경우에 사용할 수 있다.

하지만 리턴 값 이나 예외를 직접 전달받을 수는 없다.


1
2
3
4
5
@After("execution(* *..*.*User(..))")
public void afterFinally(JoinPoint joinPoint) {
    String signatureString = joinPoint.getSignature().getName();
    System.out.println("@After [" + signatureString + "] 메서드 실행 완료");
}
cs



3.Aspect 클래스 테스트


3-1 Aspect 클래스 테스트(정상)


- UserService Bean의 getUser 메서드를 호출하면, Advice가 적용된 것을 확인해 볼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
UserService.getUser(..)시작
///////////////////advice 시작//////////////////////
@Before [getUser] 메서드 실행 전처리 수행
@Before [getUser] 아규먼트user1
@Before [read] 메서드 실행 전처리 수행
@Before [read] 아규먼트user1
@After [getUser] 메서드 실행 완료
@AfterReturning [getUser] 메서드 실행 후처리 수행
@AfterReturning [getUser] 리턴값 = {"userid":"user1","name":"name1","gender":"여","city":"city1"}
///////////////////advice 종료//////////////////////
UserService.getUser(..)종료
UserService.getUser(..)실행 시간 : 242 ms
{"userid":"user1","name":"name1","gender":"여","city":"city1"}
cs


3-2 Aspect 클래스 테스트(예외발생)


- UserService Bean의 getUser 메서드가 예외 발생 시, Advice가 적용된 것을 확인해 볼 수 있다.


1
2
3
4
5
6
7
UserService.updateUser(..)시작
@Before [updateUser] 메서드 실행 전처리 수행
@After [updateUser] 메서드 실행 완료
@AfterThrowing [updateUser] 메서드 실행 중 예외 발생
@AfterThrowing [updateUser] 예외 = java.lang.StackOverflowError
UserService.updateUser(..)종료
UserService.updateUser(..)실행 시간 : 3 ms
cs


3-3 Aspect 클래스 테스트(회원수정)


1
2
3
4
5
6
7
8
@Test
public void updateUserTest() {
    service.updateUser(new UserVO("user1","name4","남","city4"));
        
    for(UserVO user:service.getUserList()) {
        System.out.println(user);
    }
}
cs




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
UserService.updateUser(..)시작
@Before [updateUser] 메서드 실행 전처리 수행
@Before [updateUser] 아규먼트{"userid":"user1","name":"name4","gender":"남","city":"city4"}
@Before [update] 메서드 실행 전처리 수행
@Before [update] 아규먼트{"userid":"user1","name":"name4","gender":"남","city":"city4"}
수정된 UserId = user1
@After [updateUser] 메서드 실행 완료
@AfterReturning [updateUser] 메서드 실행 후처리 수행
@AfterReturning [updateUser] 리턴값 = 1
UserService.updateUser(..)종료
UserService.updateUser(..)실행 시간 : 72 ms
UserService.getUserList()시작
@Before [getUserList] 메서드 실행 전처리 수행
@Before [readAll] 메서드 실행 전처리 수행
@AfterReturning [getUserList] 메서드 실행 후처리 수행
@AfterReturning [getUserList] 리턴값 = [{"userid":"user1","name":"name4","gender":"남","city":"city4"}, {"userid":"user2","name":"name2","gender":"여","city":"서울"}, {"userid":"user3","name":"name3","gender":"여","city":"city3"}, {"userid":"ㄴㄴㄴ","name":"ㄴㄴㄴ","gender":"남","city":"서울"}]
UserService.getUserList()종료
UserService.getUserList()실행 시간 : 8 ms
{"userid":"user1","name":"name4","gender":"남","city":"city4"}
{"userid":"user2","name":"name2","gender":"여","city":"서울"}
{"userid":"user3","name":"name3","gender":"여","city":"city3"}
{"userid":"ㄴㄴㄴ","name":"ㄴㄴㄴ","gender":"남","city":"서울"}
cs


완성된 프로젝트 코드를 다운받으실수 있습니다.

https://dog-developers.tistory.com/27

반응형

'JAVA > Spring Framework' 카테고리의 다른 글

MyBatis(마이바티스) 애플리케이션 작성(1)  (0) 2019.02.09
MyBatis(마이바티스) 개요  (2) 2019.02.08
AOP애플리케이션 작성(1)  (0) 2019.02.08
AOP 개요  (0) 2019.02.08
Spring Framework JDBC 환경설정  (0) 2019.02.07
Comments