관리자 글쓰기

본 내용은 혼자 공부하기 위하여 다른 포스트를 보면서 저에게 필요한 부분과 궁금했던 부분을 추가하여 게시하는 곳입니다.

궁금한 것에 대한 것은 모르는 것이 많겠지만 함께 알아보도록 노력하겠습니다.


참조 게시 포스트 : http://addio3305.tistory.com/


------------------------------------------------------------------------------------------------------------------------------------------

1. AOP(Aspect-Oriented-Programming)란?

1.1 간단 개념

- Application을 다양한 관점(핵심과 횡단 관심사)으로 분해하여 객체 지향에서 추구하는 모듈화를 더욱 잘 지원하도록 하는 프로그래밍 기법이다.

- 위의 그림처럼 계좌 이체, 입출금, 이자 계산과 같은 핵심 관심 모듈과 로깅, 보안 등인 횡단 관심 모듈로 나누어 핵심 관심 모듈에서 필요한 횡단 관심 모듈을 자동으로 소스코드가 삽입되도록 하는 것이다.


1.2 주요 개념


 용 어

의 미 

 Aspect

 여러 객체에 공통으로 적용되는 횡단 관심 모듈을 정의 

 Joinpoint

 Advice를 적용 가능한 지점으로 메서드 호출, 필드 값 변경이 해당. Spring에서는 메서드 호출만 지원 

 Advice

 '언제' '어떤' 공통 관심 기능을 핵심 관심 모듈에 적용할 지를 정의

 Weaving

 Advice를 핵심 관심 모듈에 적용하는 것을 정의

 Pointcut

 횡단 관심 모듈이 적용될 메소드를 선정하는 방법으로 스프링에서는 정규 표현식이나 AspectJ의 문법을 통해 정의


1.3 Advice의 종류


 종 류

설 명 

 Before Advice

 대상 객체의 메서드 호출 전 실행

 After Returning Advice

 대상 객체가 익셉션 없이 실행된 후 실행

 After Throwing Advice

 대상 객체가 익셉션이 발생한 경우 실행

 After Advice

 대상 객체의 익셉션과 상관 없이 실행 - finally의 기능과 비슷함

 Around Advice

 대상 객체의 메서드 호출 전, 후 또는 익셉션 발생 시점에 실행


2. AOP 적용하기
2.1 pom.xml
<org.aspectj-version>1.7.3</org.aspectj-version>
<!-- AspectJ -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
		
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
          
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>${org.aspectj-version}</version>
</dependency>
        
<!-- cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

pom.xml에 property와 dependency를 각각 선언해주자.


2.2 LoggerAspect

com.mycompany.myapp.common.logger 패키지에 LoggerAspect 클래스를 생성한다. 해당 클래스는 어떤 계층의 어떤 메서드가 실행 되었는지 로그를 이용하여 보여주기 위함이다.

@Aspect
public class LoggerAspect {
    protected Log log = LogFactory.getLog(LoggerAspect.class);
    static String name = "";
    static String type = "";
     
    @Around("execution(* com..controller.*Controller.*(..)) or execution(* com..service.*Impl.*(..)) or execution(* com..dao.*DAO.*(..))")
    public Object logPrint(ProceedingJoinPoint joinPoint) throws Throwable {
        type = joinPoint.getSignature().getDeclaringTypeName();
         
        if (type.indexOf("Controller") > -1) {
            name = "Controller  \t:  ";
        }
        else if (type.indexOf("Service") > -1) {
            name = "ServiceImpl  \t:  ";
        }
        else if (type.indexOf("DAO") > -1) {
            name = "DAO  \t\t:  ";
        }
        log.debug(name + type + "." + joinPoint.getSignature().getName() + "()");
        return joinPoint.proceed();
    }
}

- 1줄 

Aspect 어노테이션을 선언하여 해당 클래스가 Aspect Bean 객체임을 선언한다.


- 7줄

Advice를 적용하는 부분으로 @Around를 보아 위에서 살펴본 Around Advice임을 알 수 있다. 그리고 execution 부분부터는 이제 Pointcut으로 execution 명시자 표현식을 사용하였다. 

execution 명시자 표현식은 

execution(수식어패턴? 리턴타입패턴 클래스이름패턴?메서드이름패턴(파라미터패턴))         의 형식을 사용한다.

위에 표현식을 살펴보면 execution(* com..controller.*Controller.*(..)) 으로 선언되었는데 수식어패턴은 생략되었고 리턴 타입으로는 모든타입을 사용하기에 *로 선언되었다. 다음 com.. 부분은 com 패키지 및 하위 패키지를 뜻하고 그중 controller. 선언으로 controller 패키지만을 지정하였다. 다음 *Controller.* 부분은 마지막이 Controller로 끝나는 클래스의 모든 메서드를 뜻한다. 다음 (..) 부분은 모든 파라미터 패턴을 말한다. 

총 정리하자면 com 패키지 및 하위 패키지 중 controller 패키지에 속하며 Contoller로 끝나는 클래스의 모든 메서드를 호출한다는 의미이다. service부분이나 dao 부분은 각자 해석해보길 바란다.




2.3 XML

2.3.1 action-servlet.xml

밑의 해당 부분을 변경 및 추가해 넣는다.

<beans xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="com.mycompany.myapp" use-default-filters="false" >
    	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
      
    <aop:aspectj-autoproxy/>
    <bean id="loggerAspect" class="com.mycompany.myapp.common.logger.LoggerAspect"/>
          

- 12~14줄

component-scan을 선언해주면 어노테이션을 자동 검색하여 따로 추가하지 않아도 사용할 수 있게 해주는 것인데, use-default-filters 부분은 @Controller, @Service 와 같은 어노테이션을 자동 검색하지 않겠다는 뜻이다.

다음 include-filter 부분을 통해 Controller 어노테이션을 검색 할 수 있게 선언하여 주었다.


2.3.2 context-common.xml

밑의 해당 부분을 변경 및 추가해 넣는다.

<beans xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
                        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<context:component-scan base-package="com.mycompany.myapp">
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
	<aop:aspectj-autoproxy/>
	<bean id="loggerAspect" class="com.mycompany.myapp.common.logger.LoggerAspect"/>

- action-servlet.xml 과 동일하지만 15줄의 exclude-filter로 작성되어 있음을 주의하라. exclude로 선언되어 Controller를 제외한 다른 다른 클래스를 검색하라는 뜻이다. 18,19줄의 경우 동일하게 또 선언 된 이유는 루트 컨텍스트와 서블렛 컨텍스트가 다르기 때문이다. 


3. 테스트

- 위를 잘 따라왔으면 아무런 페이지를 호출할 시 콘솔창에서 다음과 같은 로그를 볼 수 있다.



따로 Controller, ServiceImpl, DAO 클래스들을 수정하지 않고 해당 계층들을 불러오면서 로그를 출력하게 만들었다.