본문 바로가기
Spring/study

spring 13강 AOP의 개요, 로그수집 예제

by avvin 2019. 6. 27.

spring 13강 AOP의 개요, 로그수집 예제


OOP(Object Oriented Programming, 객체지향프로그래밍)을 보완하는 확장적인 개념


Aspect(측면, 관점, 관심)

핵심적인 비즈니스 로직은 아니지만 반드시 해야하는 작업들


관심의 분리(Separation of Concern)를 통해 핵심Aspect(Business Logic) + 횡단 관점(트랜잭션, 로그, 보안, 인증 처리 등) 으로

Aspect의 분리를 실현



장점 : 중복 코드 제거, 효율적인 유지 보수, 높은 생산성, 재활용성 극대화, 변화 수용의 용이 



- Aspect : 공통 관심사(로깅, 보안, 트랜잭션 등)


- Join Points : method를 호출하는 시점, 예외가 발생하는 시점 등과 같이 특정 작업이 실행되는 시점을 의미함


- Advice : Join Points에서 실행되어야 하는 코드 (실제로 AOP 기능을 구현한 객체)


- Before : target method 호출 전에 적용

- After : target method 호출 후에 적용

- Around : target method 호출 이전과 이후 모두 적용 ( 가장 광범위하게 사용됨 )



- Pointcuts : 실제로 Adivce를 적용시킬 대상 method


- Proxy : Advice가 적용되었을 때 만들어지는 객체




AOP 실습 예제 ) 


사용자가 메시지를 남기면 포인트 10 증가

메시지를 읽으면 포인트 5 증가

.aop  : MessageAdvice.java/ LogAdvice.java  <<이번 포스팅에서는 로그수입 예제만을 다룸


.controller.message : MessageController.java


.model.message.dto : UserDTO.java / MessageDTO.java


.model.message.dao : MessageDAO/Impl  /  PointDAO/Impl


.service.message : MessageService/Impl





0. pom.xml에 aspect 관련 라이브러리 추가


1
2
3
4
5
6
        <!-- AspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${org.aspectj-version}</version>
        </dependency>
cs



1. servlet-context의 namespace에서 aop와 tx 체크


해당 문서에서 aop와 tx관련 태그 사용 가능


servlet-context.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
cs


2. aop 태그 코드 추가


1
2
<!-- aop의 설정으로 Proxy 객체를 자동으로 생성 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
cs


<aop:aspectj-autoproxy/>





3. AOP기능을 지원할 Adivce 클래스 작성 : LogAdvice.java


로그수집같이 매번 진행해야하지만 부수적인 일들을 Aspect 클래스에서 맡아 처리해준다.



AOP기능을 지원할 클래스 내의 aspect 메서드 상단에 @Around / @Before / @After

@Around는 괄호 안 조건에 해당되는 메서드들의 실행 전, 후에 실행,

@Before는 실행 전,

@After는 실행 후에 Aspect메서드가 실행된다.

1
2
3
@Around("execution(* com.example.spring02test.controller..*Controller.*(..))"
            + " or execution(* com.example.spring02test.service..*Impl.*(..))"
            + " or execution(* com.wxample.spring02test,model..dao.*Impl.*(..))")
cs


위 어노테이션을 사용하면 해당되는 메서드 옆에 아래와같은 기호가 뜬다.





LogAdvice.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
package com.example.spring02test.aop;
 
import java.util.Arrays;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
 
 
@Component //Aspect 클래스는 스프링 bean이어야한다.
@Aspect // 공통적인 업무를 지원하는 bean / 스프링에서 지원하는 AOP bean
public class LogAdvice {
    //로깅을 위한 변수
    private static final Logger logger
    = org.slf4j.LoggerFactory.getLogger(LogAdvice.class);
    
    //- Around : target method 호출 이전과 이후 모두 적용 ( 가장 광범위하게 사용됨 )
    @Around("execution(* com.example.spring02test.controller..*Controller.*(..))"
            + " or execution(* com.example.spring02test.service..*Impl.*(..))"
            + " or execution(* com.wxample.spring02test,model..dao.*Impl.*(..))")
    public Object logPrint(ProceedingJoinPoint joinPoint)throws Throwable {
        
        //이 메서드를 호출한 시간 
        long start = System.currentTimeMillis();
        
        //이 joinPoint.proceed()코드를 기준으로 
        //이상의 코드가 실행 전
        Object result = joinPoint.proceed(); //★핵심업무 실행
        //이하의 코드가 핵심업무 실행 후
        
        //호출한 클래스 이름
        //컨트롤러인지, 서비스인지, DAO인지 
        String type = joinPoint.getSignature().getDeclaringTypeName();
        String name="";
        if(type.indexOf("Controller"> -1) {
            name =" Controller \t:";
        }else if (type.indexOf("Service"> -1) {
            name = "ServiceImpl \t:";
        }else if(type.indexOf("DAO"> -1) {
            name = "DaoImpl \t:";
        }
     호출 되는 메서드 이름
        logger.info(name+type+"."+joinPoint.getSignature().getName() + "()");
        
        //method에 전달되는 매개변수들
        logger.info(Arrays.toString(joinPoint.getArgs()));
        
        //이 메서드를 실행이 끝나는 시간
        long end = System.currentTimeMillis();
        //이 메서드가 호출되고 끝나는데에 걸리는 시간
        long time = end - start; 
        logger.info("실행시간 : " + time);
        
        return result;
    }
    
}
 
cs