AOP 복습(Spring Framework STS4 동영상 강의)

Back-End/Spring 2019. 6. 22. 21:25
728x90
반응형


- AOP (Aspect Oriented Programming, 관점(관심) 지향적인 프로그래밍 ) -



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


2. Aspect (측면, 관점, 관심) : 핵심적인 비즈니스 로직은 아니지만 반드시 해야 하는 작업들

ex) 버스, 지하철로 출퇴근을 할 때 교통카드를 찍어야함, 목적지에 정시에 도착하는 것이 중요하고,

교통카드를 찍는 행위가 메인은 아님.


3. 관심의 분리 (Separation of Concerns)를 통해 핵심관점 (업무로직) + 

횡단관점(트랜잭션, 로그, 보안, 인증 처리 등) 으로 관심의 분리를 실현


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



  

  핵심관심과 횡단관심


  ex) 은행업무를 볼때 계좌이체, 입출금, 이자계산 등 중요하고 안할수가 없는 업무를 "핵심관심 (종단관심) " 이라고 한다.


  또한 이러한 업무들을 할때 필요한 "로깅" , "보안" , "트랜잭션" 등을 "횡단관심" 이라고 한다.





-AOP의 주요 용어-


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


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

(호출전, 호출후, 호출전후 3가지로 지원해줌)


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

(횡단관심에 있는 것들을 코딩을 덜 할 수 있도록 지원해 주는것)


4. Pointcuts : 실제로 Advice를 적용시킬 대상 method


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




-Advice의 종류-


1. Before : target method 호출 전에 적용


2. After : target method 호출 후에 적용


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




-AOP의 설정 방법-


1. pom.xml에 라이브러리를 추가함


AspectJ의 버전은 기본적으로 properties에 있는 <org.aspectj-version>의 버전을 따라간다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<properties>
        <java-version>1.8</java-version>
        <!-- 2019년 1월 현재 최신 버전 5.1.4, 에러가 날 경우 호환성을 위해 버전을 내려야 함 -->
        <org.springframework-version>5.1.4.RELEASE</org.springframework-version>
        <org.aspectj-version>1.9.2</org.aspectj-version>
        <org.slf4j-version>1.7.25</org.slf4j-version>
    </properties>
 
 
<!-- AspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${org.aspectj-version}</version>
        </dependency>
cs



2. servlet-context.xml의 Namespace에서 aop와 tx 추가 체크




추가를 하면 servlet-context.xml의 source쪽에 aop와 tx관련 소스가 추가되어 있다.

즉, xml 파일에서 aop와 tx관련 태그를 사용할 수 있다는 뜻이다.


tx : 트랜잭션 처리 관련된 태그

1
2
3
4
5
6
<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"
cs




3. servlet-context.xml에 aop 태그를 추가

1
2
<!-- aop의 설정을 통한 자동적인 Proxy 객체를 생성 -->
    <aop:aspectj-autoproxy />
cs



4. LogAdvice.java 만들기

   

로그 수집 작업을 하려면 모든 메소드에 해야한다.


로그 수집은 중요한 업무이지만 핵심적인 업무는 아니고 공통적인 업무에 해당


공통적인 업무는 모든 class의 모든 method에 작성하지 말고 Advice에 모아서 작성하고


세부적인 코드에서는 핵심업무에 집중하도록 처리




  시그니처(signature)란?


  객체가 선언하는 모든 연산은 연산의 이름, 매개변수로 받아들이는 객체들을, 연산의 시그니처라고 합니다. 




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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.example.spring02.aop;
 
import java.util.Arrays;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
 
import com.itextpdf.text.log.LoggerFactory;
 
import ch.qos.logback.classic.Logger;
 
//@Controller @Service @Repository
 
@Component    //스프링에서 관리하는 bean, aop는 위의 3개에 속하지 않기때문에 컴포넌트 어노테이션을 사용
@Aspect //스프링에서 관리하는 aop bean, aop를 사용하기 위해서는 2개의 어노테이션이 다 있어야 한다.
public class LogAdvice {
    
    private static final Logger logger
        = LoggerFactory.getLogger(LogAdvice.class);
    
    
    
//=============================중요==================================
    
    //포인트컷 - 실행시점, Around - 실행 전후
//Before, After, Around (어떤 요청이 오기 전에 실행될지, 후에 실행될지, 전후에 실행될지...)
        //컨트롤러, 서비스, dao의 모든 method 실행 전후에 logPring method가 호출됨
        //execution (리턴자료형 class.method(매개변수))
    
    
    //@Around( Around를 포인트 컷이라 한다. (언제 어느곳에서 실행될지 정함),
    //지금은 요청전에 실행하는걸로 정함
    
    
    //"execution(* com.example.spring02.controller..*Controller.*(..))" 
    // .이 2개 있는것은 하위패키지가 다 들어갈 수 있다는 의미, . 1개는 직속 디렉토리
    //즉, Controller로 끝나는 모든 메소드들을 말한다.
    // 밑에있는 코드는 Controller의 모든 메소드를 호출하면 실행되는 공통적인 코드이다.
    
    
    //+ " or execution(* com.example.spring02.service..*Impl.*(..))"
    // 마찬가지로 밑에있는 코드를 service.Impl에 있는 모든 메소드를 호출하면 실행되는 
    //공통적인 코드로 실행 시키고 싶을때 사용. 
    
    
    //+ " or execution(* com.example.spring02.model..dao.*Impl.*(..))")
    // 마찬가지로 밑에있는 코드를 dao.Impl에 있는 모든 메소드를 호출하면 실행되는 
        //공통적인 코드로 실행 시키고 싶을때 사용. 
        
    
//===================================================================    
    
    
    //모든 코드에서 로그를 출력하는 메소드
        public Object logPrint(ProceedingJoinPoint joinPoint)
            throws Throwable {
        long start = System.currentTimeMillis(); //시작할때의 현재시간을 start 변수에 저장
        
        //==========호출전===========================================
        
        Object result = joinPoint.proceed(); 
        // joinPoint.proceed(); 메소드를 호출해서 프록시 패턴을 result변수에 저장.
        // 즉 공통적인부분 (로그를 출력하는 부분)을 묶에서 result에 저장한다는 뜻.
        
        //만약 컨트롤러에서 login.check메소드가 호출한다고 가정했을때
        //호출되기 전에 먼저 위쪽에 있는 logPrint메소드가 먼저 실행되고,
        //login.check메소드가 다 실행되고 난 후 (호출후에는) 밑에 있는 부분이 실행이된다.
        //그러니까 Object result = joinPoint.proceed(); 를 기준으로 
        //'호출전' 과 '호출후' 로 나뉘어진다.
        
        //==========호출후===========================================
        //class name (호출한 클래스의 이름) 컨트롤러, 서비스, DAO인지 if문으로 판별해서 실행
        String type =
    joinPoint.getSignature().getDeclaringTypeName(); 
        
        // JoinPoint를 선언하고 있는 타입의 이름을 반환해서 그 값을 Signature객체를 반환할때 매개값으로 쓴다.
        // 
        
        
        String name = "";  //null값이면 오류가 발생하기 때문에 초기값을 공백으로 지정
        
        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: ";
        }
        
        //method name (메소드를 호출할때 로그가 찍히게 된다)
        logger.info(
                name + type + "." + joinPoint.getSignature().getName()+"()");
        
        //매개변수 (마찬가지로 매개변수를 사용할때 로그가 찍히게 된다.)
        logger.info(Arrays.toString(joinPoint.getArgs()));
        
        //핵심로직으로 이동
        long end = System.currentTimeMillis(); //끝날때의 현재시간을 end에 저장
        long time = end-start; //끝난시간에서 시작한시간을 빼면 실행시간을 계산할 수 있다.
        logger.info("실행시간:"+time);
        return result; //결과를 반환한다.
        }
}
 
cs



이제 실행을 하면 콘솔창에 각 클래스와 메소드가 실행될때 로그가 뜨게 된다.

728x90
반응형
: