danbibibi
article thumbnail

AOP(Aspect Oriented Programming)

  • 관점 지향 프로그래밍
  • 어떤 로직을 기준으로 핵심 관심 사항공통(부가) 관심 사항으로 나누어 모듈화
    • 핵심 관심 사항 : 적용하고자 하는 핵심 비즈니스 로직
    • 공통(부가) 관심 사항 : 핵심 로직을 실행하기 위해 행해지는 부가적인 로직 ex) DB 연결, 로깅 등
  • 분리한 부가 기능을 Aspcet라는 독특한 모듈 형태로 만들어서 설계하고 개발 (공통 모듈을 손쉽게 적용 가능)
💡 한 마디로 AOP란?
부가기능을 Aspcet로 정의하여, 핵심기능에서 부가기능을 분리함으로써 
핵심기능을 설계하고 구현할 때 객체지향적인 가치를 지킬 수 있도록 도와주는 개념
(실무에서는 호불호가 갈린다고 한다)
💡 AOP 등장 배경
기존 OOP에서 공통관심사항을 여러 모듈에서 적용할 때,
중복된 코드를 양상 하는 한계를 해결하기 위해 등장!

 

 

Spring AOP 용어

Target

  • 핵심 기능을 담고 있는 모듈
  • 부가 기능을 부여할 대상

Aspect

  • 여러 객체에 공통으로 적용되는 공통 관심 사항
  • AOP의 기본 모듈
  • Aspect = Advice + Pointcut (Spring에서는 Adbisor라고도 한다!!)
  • Singleton 형태의 객체로 존재

Advice

  • Target에 제공할 부가기능을 담고 있는 모듈
  • 어느 시점에 어떤 공통관심사항(Aspect)를 적용할지 정의한 것
 Advice 종류   설명
 Before  대상 객체의 메소드가 실행되기 전에 실행
 After Returning  대상 객체의 메소드 실행이 정상적으로 끝난 뒤 실행
 After Throwing  대상 객체의 메소드 실행 중 예외가 발생한 경우 실행
 After  대상 객체의 메소드의 정상/예외 여부와 관계 없이 메소드 실행 종료 후 실행 (finally와 유사)
 Around  - 위의 4가지 Advice를 모두 구현할 수 있는 Advice  
 - 대상 객체의 메소드가 실행되기 전, 후 실행

JoinPoint

  • Aspect가 적용될 수 있는 지점 (method, field)
  • 즉, Pointcut이 될 수 있는 후보군들!!

Pointcut

  • 공통관심사항이 적용될 JoinPoint (선택된 JoinPoint)
  • Advice를 적용할 target의 method를 선별하는 정규 표현식

Weaving

  • 어떤 Adivce를 어떤 Pointcut(핵심사항)에 적용시킬 것인지에 대한 설정
  • 즉, Pointcut에 의해서 결정된 Target의 JoinPoint에 부가기능(Advice)를 삽입하는 과정
  • 핵심기능(Target)에 영향을 주지 않으면서 필요한 부가기능을 추가할 수 있도록 해주는 처리 과정

 

Pointcut 표현식

# public 메소드 실행
execution(public * *(..))

# 이름이 set으로 시작하는 모든 메소드 실행
execution(* set*(..))

# AccountService 인터페이스의 모든 메소드 실행
execution(* com.test.service.AccountService.*(..))

# service 패키지에 정의된 메소드 실행
execution(* com.test.service.*.*(..))

# 서비스 패키지와 하위 패키지의 모든 메소드 실행
execution(* com.test.service..*.*(..))

# 서비스 패키지 내의 모든 JoinPoint
within(com.test.service.*)

# 서비스 패키지 및 하위 패키지의 모든 JoinPoint
within(com.test.service..*)

# AccountService 인터페이스를 구현하는 프록시 개체의 모든 JoinPoint
this(com.test.service.AccountService)

# AccountService 인터페이스를 구현하는 대상 객체의 모든 JoinPoint
target(com.test.service.AccountService)

# 하나의 파라미터를 가지고 전달된 인자가 Serializable인 모든 JoinPoint
args(java.io.Serializable)

# 대상 객체가 @Transactional 어노테이션을 갖는 모든 JoinPoint
@target(org.springframework.transaction.annotation.Transactional)

# 대상 객체의 선언 타입이 @Transactional 어노테이션을 갖는 모든 JoinPoint
@within(org.springframework.transaction.annotation.Transactional)

# 실행 메소드가 @Transactional 어노테이션을 갖는 모든 JoinPoint
@annotation(org.springframework.transaction.annotation.Transactional)

# 단일 파라미터를 받고, 전달된 인자 타입이 @Classified 어노테이션을 갖는 모든 JoinPoint
@args(com.test.security.Classified)

# 'accountRepository' 빈
bean(accountRepository)

# 'accountRepository'를 제외한 모든 빈
!bean(accountRepository)

#  모든 빈
bean(*)

# 이름이 'account'로 시작되는 모든 빈
bean(account*)

# 이름이 'account'로 시작되는 모든 빈
bean(account*)

# 이름이 'Repository'로 끝나는 모든 빈
bean(*Repository)

# 이름이 'accouningt/'로 시작되는 모든 빈
bean(accouningt/*)

# 이름이 'dataSource'나 'DataSource'로 끝나는 모든 빈
bean(*dataSource) || bean(*DataSource)

 

Spring AOP 구현

먼저 AOP를 사용하기 위해 pom.xml에 dependency를 추가해준다!!

 

#pom.xml

# aspectjweaver
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8</version>
    <scope>runtime</scope>
</dependency>

# aspectjrt
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.8</version>
    # 여기는 scope 없음에 주의 !!
</dependency>

 

package com.test.aop;
import java.util.Random;
import org.springframework.stereotype.Component;

@Component
public class Driver implements Person{

	@Override
	public void doSomething() throws GasException {
		System.out.println("열심히 운전을 합니다.");
		 if(new Random().nextBoolean()) { // 예외 발생
			 throw new GasException();
		 }
	}
}

 

XML 이용

applicationContext.xml 파일 내에서 Aspect Class를 <bean>으로 등록해준다!

 

# applicationcontext.xml

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
			
		<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
		<bean class="com.test.aop.CarAspect" id="carAspect"></bean>
		<bean class="com.test.aop.Driver" id="driver"></bean>
		<aop:config>
			<aop:pointcut expression="execution(public void com.test.aop.*.doSomething())" id="mypt"/>
			<aop:aspect ref="carAspect">
				<aop:before method="before" pointcut-ref="mypt"/>
				<aop:after-returning method="after_rt" pointcut-ref="mypt"/>
				<aop:after-throwing method="after_th" pointcut-ref="mypt"/>
				<aop:after method="after" pointcut-ref="mypt"/>
			</aop:aspect>
		</aop:config> 

</beans>

 

@Aspect Annotation 이용

# applicationContext.xml

<?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"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
  
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <context:component-scan base-package="com.test.aop"/>

</beans>

 

다음과 같이 annotation을 이용해 Pointcut을 설정해준다!

 

// CarAspect.java

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 CarAspect {
	
	@Before("execution(public void com.test.aop.*.doSomething())")
	public void before() {
		System.out.println("차를 탑니다.");
	}
	
	@AfterReturning("execution(public void com.test.aop.*.doSomething())")
	public void after_rt() {
		System.out.println("주차를 합니다.");
	}
	
	@AfterThrowing("execution(public void com.test.aop.*.doSomething())")
	public void after_th() {
		System.out.println("주유소에 가서 기름을 넣습니다.");
	}
	
	@After("execution(public void com.test.aop.*.doSomething())")
	public void after() {
		System.out.println("차에서 내립니다.");
	}
}

 

'WEB > back-end' 카테고리의 다른 글

Spring MVC 실습  (0) 2023.04.24
Interceptor  (0) 2023.04.22
Spring  (0) 2023.04.21
JDBC를 이용한 DB 연결 (MySQL)  (0) 2023.03.24
DAO, DTO, Entity 란?  (0) 2023.03.22
profile

danbibibi

@danbibibi

꿈을 꾸는 시간은 멈춰 있는 것이 아냐 두려워하지 마 멈추지 마 푸른 꿈속으로