hyeonga_code

Spring_AOP 심화 실습_@Aspect 어노테이션 활용한 다양한 Advice 적용 본문

Spring

Spring_AOP 심화 실습_@Aspect 어노테이션 활용한 다양한 Advice 적용

hyeonga 2024. 1. 9. 09:59
반응형


    - 포인트 컷 클래스 작성하기
        - @Pointcut 어노테이션
            - Pointcut을 정의하는 AspectJ 표현식의 값을 가집니다.
            - 적용한 메소드는 void 리턴 타입을 가집니다.
            - Advice 관련 어노테이션에서 해당 메소드 이름을 이요하여 pointcut을 사용할 수 있습니다.

- sts.spring.common > PublicPointcut.java 클래스 생성
=====

1
2
3
4
5
6
7
8
9
10
package sts.spring.common;
 
import org.aspectj.lang.annotation.Pointcut;
 
public class PublicPointcut {
    
    @Pointcut("execution(public * sts.spring..*Impl.*(..))")
    public void publicMethod() {}
}
 



    - 로그인 서비스를 제공하는 Aspect 클래스를 작성합니다.
- sts.spring.common > LoggingAsepct.java 클래스 생성
=====

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
package sts.spring.common;
 
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.core.annotation.Order;
 
@Aspect
@Order(1)
public class LoggingAspect {
    
    @Before("PublicPoint.publicMethod()")
    public void before() {
        System.out.println("LogAs>> Before method");
    }
    
    @AfterReturning(pointcut = "sts.spring.common.PublicPointcut.publicMethod()", returning="ret")
    public void afterReturning(Object ret) {
        System.out.println("LogAs>> After method [return value : " + ret + "]");
    }
    
    @AfterThrowing(pointcut = "sts.spring.common.PublicPointcut.publicMethod()", throwing="ex")
    public void afterThrowing(Throwable ex) {
        System.out.println("LogAs>> During method exception [ exception : " + ex.getClass().getName() + "]");
    }
    
    @After("sts.spring.common.PublicPointcut.publicMethod()")
    public void afterFinally() {
        System.out.println("LogAs>> Complete method");
    }
}
 





    - 캐시 서비스를 제공하는 Aspect 클래스를 작성합니다.
- sts.spring.common > ArticleCacheAspect.java 클래스 생성
=====

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
45
46
47
48
49
50
51
package sts.spring.common;
 
import java.util.HashMap;
import java.util.Map;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
 
import sts.spring.board.vo.ArticleVO;
 
@Aspect
public class ArticleCacheAspect implements Ordered {
    
    // 결과를 캐싱하기 위해 사용합니다.
    private Map<Integer, ArticleVO> cache = new HashMap<Integer, ArticleVO>();
    
    // *..ReadArticleService 패키지 내의 모든 public 메소드에 적용됩니다.
    @Around("execution(public * *..ReadArticleService.*(..))")
    public ArticleVO cache(ProceedingJoinPoint joinPoint) throws Throwable{
        
        // 첫 번째 매개 변수를 추출하여 id 로 사용합니다.
        Integer id = (Integer) joinPoint.getArgs()[0];
        
        // id를 사용하여 게시물 데이터를 찾아옵니다.
        ArticleVO article = cache.get(id);
        
        // 게시물이 있는 경우 캐시 데이터에 추가하고 데이터를 반환합니다.
        if(article != null) {
            System.out.println("_ArticleCacheAspect : Article[" + id + "]");
            return article;
        }
        
        // 게시물이 없는 경우 원래 메소드를 실행하여 결과를 ret에 저장합니다.
        ArticleVO ret = (ArticleVO) joinPoint.proceed();
        
        // 실행된 경우 게시물 데이터를 캐시에 추가하여 반환합니다.
        if(ret != null) {
            cache.put(id, ret);
            System.out.println("_ArticleCacheAspect : Article[" + id + "]");
        }
        return ret;
    }
    
    @Override
    public int getOrder() {
        return 2;
    }
}
 



    - 회원 정보를 변경하고 추적 서비스를 제공하는 Aspect 클래스를 작성하기
- sts.spring.common > UpdateMemberInfoTraceAspect.java 클래스 생성
=====

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package sts.spring.common;
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
 
import sts.spring.member.vo.UpdateInfo;
 
@Aspect
@Order(3)
public class UpdateMemberInfoTraceAspect {
 
    @AfterReturning(pointcut = "args(memberId, info)", returning="result", argNames="result, memberId, info")
    public void traceReturn(JoinPoint joinPoint, boolean result, String memberId, UpdateInfo info) {
        System.out.println("__TraceAspect Return___________");
        System.out.println("  - result : " + result);
        System.out.println("  - memberId : " + memberId);
        System.out.println("  - Updateinfo : " + info);
    }
}
 



- applicaionContextFour.xml 파일 생성
=====

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
 
    <aop:aspectj-autoproxy />
    <bean id="traceAspect" class="sts.spring.common.UpdateMemberInfoTraceAspect" />
    <bean id="cacheAspect" class="sts.spring.common.ArticleCacheAspect" />
    <bean id="loggingAspect" class="sts.spring.common.LoggingAspect" />
    <bean id="readArticleService" class="sts.spring.board.service.ReadArticleServiceImpl" />
    <bean id="memberService" class="sts.spring.member.service.MemberServiceImpl" />
 
</beans>
 



    - 테스트 클래스를 작성합니다.
- sts.spring.board.controller > MainFour.java 클래스 생성
====

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package sts.spring.board.controller;
 
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import sts.spring.board.service.ReadArticleService;
import sts.spring.board.vo.ArticleNotFoundException;
import sts.spring.board.vo.ArticleVO;
import sts.spring.member.vo.MemberService;
import sts.spring.member.vo.UpdateInfo;
 
public class MainFour {
    public static void main(String[] args) {
        
        String[] configLocations = new String[] { "applicationContextFour.xml" };
        
        AbstractApplicationContext context = new ClassPathXmlApplicationContext(configLocations);
        
        ReadArticleService readArticleService = context.getBean("readArticleService", ReadArticleService.class);
    
        try {
            
            // 1번글을 얻어와 캐시에 저장합니다.
            ArticleVO article1 = readArticleService.getArticleAndIncreaseReadCount(1);
            
            // 캐시에 저장되어 있던 1번 글을 가져옵니다.
            ArticleVO article2 = readArticleService.getArticleAndIncreaseReadCount(1);
            
            // 값이 같은 경우 : 캐시에 넣은 값을 가져오는 것입니다.
            System.out.println("article1 == article2 : " + (article1 == article2));
            
            // 0번 글은 얻어올 수 없으므로 예외 처리를 확인할 수 있습니다.
            readArticleService.getArticleAndIncreaseReadCount(0);
                        
        } catch(ArticleNotFoundException e) { }
    
        MemberService memberService = context.getBean("memberService", MemberService.class);
        memberService.update("name01"new UpdateInfo());
        
        context.close();
            /*
                LogAs>> Before method
                _ArticleCacheAspect : Article[1]
                LogAs>> After method [return value : sts.spring.board.vo.ArticleVO@6c2ed0cd]
                LogAs>> Complete method
                LogAs>> Before method
                _ArticleCacheAspect : Article[1]
                LogAs>> After method [return value : sts.spring.board.vo.ArticleVO@6c2ed0cd]
                LogAs>> Complete method
                article1 == article2 : true
                LogAs>> Before method
                LogAs>> During method exception [ exception : sts.spring.board.vo.ArticleNotFoundException]
                LogAs>> Complete method
                LogAs>> Before method
                + MemberServiceImpl.update()
                __TraceAspect Return___________
                  - result : true
                  - memberId : name01
                  - Updateinfo : sts.spring.member.vo.UpdateInfo@36a5cabc
                LogAs>> After method [return value : true]
                LogAs>> Complete method
             */
    }
}    
 

 

반응형