Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
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
Tags
more
Archives
Today
Total
관리 메뉴

방카@Dev

[Spring]AOP(Aspect Oriented Programming)_스프링 핵심원리(고급편) 본문

BackEnd/Spring

[Spring]AOP(Aspect Oriented Programming)_스프링 핵심원리(고급편)

방카킴 2024. 6. 11. 15:36
 

Aspect Oriented Programming with Spring :: Spring Framework

Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enabl

docs.spring.io

 

 

스프링 핵심 원리 - 고급편 | 김영한 - 인프런

김영한 | 스프링의 핵심 원리와 고급 기술들을 깊이있게 학습하고, 스프링을 자신있게 사용할 수 있습니다., 핵심 디자인 패턴, 쓰레드 로컬, 스프링 AOP스프링의 3가지 핵심 고급 개념 이해하기

www.inflearn.com

#메이븐 설정파일(pom.xml)

<!-- AOP --> 
<dependency> 
    <groupId>org.aspectj</groupId> 
    <artifactId>aspectjweaver</artifactId> 
    <version>1.7.4</version> 
</dependency>

AOP란?

- 비즈니스 로직의 핵심 기능과 공통 기능(부가 기능)을 분리 시켜놓고, 공통 기능(횡단 관심사)을 필요로 하는 핵심 기능들에 사용하는 방법

 

 

 

1. 에스펙트(Aspect)

  • 부가 기능(어드바이스)과 부가 기능을 어디에 적용할지 선택(포인트 컷)하는 기능을 합해서 하나의 모듈로 만든 것
  • 부가기능과 해당 부가기능을 어디에 적용할지 정의한 것(ex. 로그 출력 기능을 모든 컨트롤러에 적용해라!)
  • 애플리케이션을 바라보는 관점을 하나하나의 기능에서 횡단 관심사(cross-cutting concerns) 관점으로 달리 보는 것
  • 에스펙트를 사용한 프로그래밍 방식을 관점 지향 프로그래밍(AOP)

 

2.어드바이스(Advice)  

   - ASPECT의 기능 자체(부가 기능 - e.g.로그 출력 기능)

  • 특정 조인 포인트에서 aspect에 의해 취해지는 부가 기능
  • around(주변), before(전), after(후)와 같은 다양한 종류의 어드바이스가 있음

3. 조인 포인트(Join Point)

  • Advice를 적용할 수 있는 위치
  • 메소드 실행, 생성자 호출, 필드 값 접근, static 메서드 접근 같은 프로그램 실행 중의 AOP 적용 가능 지점
  • aspectJ를 이용하여 컴파일 시점, 클래스 로딩 시점에 적용하는 AOP는 바이트코드를 조작하기 때문에 해당 기능을 모든 지점에 다 적용 가능
  • 프록시 방식을 사용하는 스프링 AOP는 메서드 실행 지점에만 AOP를 적용
  • 프록시는 메서드 오버라이딩 개념으로 동작
  • 프록시를 사용하는 스프링 AOP의 조인 포인트는 메서드 실행으로 제한

4. 포인트 컷(Pointcut)

- 조인 포인트 중 어드바이스가 적용될 위치를 선별하는 기

5. 타겟(Target)

- 어드바이스를 받는 객체, 포인트 컷으로 결정

6. 위빙(weaving)

- advice를 핵심 기능에 적용 하는 행위(원본 로직에 부가 기능 로직이 추가되는 것, 에스펙트와 실제 코드를 연결해서 붙이는 것)

- 포인트컷으로 결정한 타켓의 조인 포인트에 어드바이스를 적용하는 행위

# 위빙 적용 시점

(1) 컴파일 시점 : java 소스코드를 컴파일러를 사용해서 .class를 만드는 시점에 부가기능 로직 추가

  • 실제 대상 코드에 에스펙트를 통한 부가 기능 호출 코드 포함. AspectJ를 직접 사용

(2) 클래스 로딩 시점 : .class 파일을 JVM 내부의 클래스 로더 저장 전에 부가기능 로직 추가

  • 실제 대상 코드에 에스펙트를 통한 부가 기능 호출 코드 포함. AspectJ를 직접 사용

(3) 런타임 시점 : 컴파일 및 클래스 로더에 로딩하여 자바가 실행되고 난 다음 시점, 자바의 main 메서드가 이미 실행된 다음. 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있음.

  • 실제 대상 코드는 그대로 유지되고 대신 프록시를 통해 부가기능이 적용되므로 항상 프록시를 통해 부가 기능 사용
  • aop 기능을 구현하기 위해 만든 프록시 객체, 스프링에서 aop 프록시는 jdk 동적 프록시 또는 cglib 프록시 사용

* AspectJ 프레임워크

  • AOP의 대표적인 구현.
  • 횡단 관심사의 깔끔한 모듈화
  • 1)오류 검사 및 처리 2)동기화 3)성능 최적화(캐싱) 4)모니터링 및 로깅
  • 스프링은 AspectJ의 문법을 차용하고 프록시 방식의 AOP를 적용하기 때문에 AspectJ를 직접 사용하는 것이 아님

 

# 스프링에서 AOP 구현 방식


1) xml 스키마 기반의 AOP 구현

package com.kbfg.digi;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {
		AbstractApplicationContext ctx = new GenericXmlApplicationContext("classpath:applicationCTX.xml");
		
		Student student = ctx.getBean("student", Student.class);
		student.getStudentInfo();
		
		Worker worker = ctx.getBean("worker", Worker.class);
		worker.getWorkerInfo();
		
		ctx.close();
	}
}

 

<applicationCTX.xml>

- Namespaces에서 aop 선택

<?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.xsd">

	<bean id="logAop" class="com.kbfg.digi.LogAop" />

	<aop:config>
		<aop:aspect id="Logger" ref="logAop">
			<!-- LogAop-> 공통기능 -->
			<aop:pointcut id="publicM"
				expression="within(com.kbfg.digi.*)" />
			<!--aop:pointcut -> expression= "within(kr.soft.study.*) 이 범위에서 메서드 실행되면 
				전체가 pointcut이 된다. -->
			<aop:around pointcut-ref="publicM" method="loggerAop" />
		</aop:aspect>
		<aop:aspect id="Logger" ref="logAop">
			<aop:pointcut id="publicM"
				expression="within(com.kbfg.digi.*)" />
			<aop:before pointcut-ref="publicM" method="beforeAdvice" />
		</aop:aspect>
		<aop:aspect id="Logger" ref="logAop">
			<aop:pointcut id="publicM"
				expression="within(com.kbfg.digi.*)" />
			<aop:after-returning pointcut-ref="publicM"
				method="afterReturningAdvice" />
		</aop:aspect>
		<aop:aspect id="Logger" ref="logAop">
			<aop:pointcut id="publicM"
				expression="within(com.kbfg.digi.*)" />
			<aop:after-throwing pointcut-ref="publicM"
				method="afterThrowingAdvice" />
		</aop:aspect>
		<aop:aspect id="Logger" ref="logAop">
			<aop:pointcut id="publicM"
				expression="within(com.kbfg.digi.*)" />
			<aop:after pointcut-ref="publicM" method="afterAdvice" />
		</aop:aspect>
	</aop:config>

	<bean id="student" class="com.kbfg.digi.Student">
		<property name="name" value="홍길동" />
		<property name="age" value="10" />
		<property name="gradeNum" value="3" />
		<property name="classNum" value="5" />
	</bean>
	<bean id="worker" class="com.kbfg.digi.Worker">
		<property name="name" value="홍길순" />
		<property name="age" value="35" />
		<property name="job" value="개발자" />
	</bean>
</beans>
package com.kbfg.digi;

import org.aspectj.lang.ProceedingJoinPoint;

public class LogAop {
	
	public Object loggerAop(ProceedingJoinPoint joinpoint) throws Throwable {
		String signatureStr = joinpoint.getSignature().toShortString();
		System.out.println(signatureStr + "is start.");
		long st = System.currentTimeMillis();
		
		try {
			Object obj = joinpoint.proceed();
			return obj;
		} finally {
			long et = System.currentTimeMillis();
			System.out.println(signatureStr + " is finished.");
			System.out.println(signatureStr + " 경과시간 : "+ (et-st));
		}
	}
	
	public void beforeAdvice() {
		System.out.println("beforeAdvice()");
	}
	
	public void afterReturningAdvice() {
		System.out.println("afterReturningAdvice");
	}
	
	public void afterThrowingAdvice() {
		System.out.println("afterThrowingAdvice()");
	}
	
	public void afterAdvice() {
		System.out.println("afterAdvice()");
	}
}

2) @Aspect 어노테이션 기반의 aop 구현

<applicationCTX.xml>

- <aop:aspectj-autoproxy/> 태그 추가

<?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.xsd">

	<aop:aspectj-autoproxy/>     //추가
	<bean id="logAop" class="com.kbfg.digi.LogAop" />

	<bean id="student" class="com.kbfg.digi.Student">
		<property name="name" value="홍길동" />
		<property name="age" value="10" />
		<property name="gradeNum" value="3" />
		<property name="classNum" value="5" />
	</bean>
	<bean id="worker" class="com.kbfg.digi.Worker">
		<property name="name" value="홍길순" />
		<property name="age" value="35" />
		<property name="job" value="개발자" />
	</bean>
</beans>

 

package com.kbfg.digi;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LogAop {
	
//	@Pointcut("execution(public void get*(..))") //public void인 모든 get메소드
//	@Pointcut("execution(* com.kbfg.digi..*(..))") //com.kbfg.digi 패키지에 파라미터가 없는 모든 메서드
//	@Pointcut("execution(* com.kbfg.digi..*.*(..))") //com.kbfg.digi 패키지 & 하위 패키지에 파라미터가 없는 모든 메소드
	@Pointcut("execution(* com.kbfg.digi.Worker.*(..))") //com.kbfg.digi.Worker안의 모든 메소드
	
//	@Pointcut("within(com.kbfg.digi.*)") //kr.soft.study. 패키지 안에 있는 모든 메서드
//	@Pointcut("within(com.kbfg.digi..*)") //kr.soft.study. 패키지 및 하위 패키지 안의 모든 메서드
//	@Pointcut("within(com.kbfg.digi.Worker)") //kr.soft.study.Worker 안의 모든 메서드
	
//	@Pointcut("bean(student)") //bean - 특정 빈에 적용하겠다
//	@Pointcut("bean(*ker)") //~ker로 끝나는 빈에만 적용
	private void pointcutMethod() {}
	
	@Around("pointcutMethod()")
	public Object loggerAop(ProceedingJoinPoint joinpoint) throws Throwable {
		String signatureStr = joinpoint.getSignature().toShortString();
		System.out.println(signatureStr + " is start.");
		long st = System.currentTimeMillis();
		try {
			Object obj = joinpoint.proceed();
			return obj;
		} finally {
			long et = System.currentTimeMillis();
			System.out.println( signatureStr + " is finished.");
			System.out.println( signatureStr + " 경과시간 : " + (et - st));
		}		
	}
	
	@Before("within(com.kbfg.digi.*)")
	public void beforAdvice() {
		System.out.println("beforAdvice()");
	}
	
	@AfterReturning("within(com.kbfg.digi.*)")
	public void afterReturningAdvice() {
		System.out.println("afterReturningAdvice");
	}
	
	@AfterThrowing("within(com.kbfg.digi.*)")
	public void afterThrowingAdvice() {
		System.out.println("afterThrowingAdvice()");
	}
	
	@After("within(com.kbfg.digi.*)")
	public void afterAdvice() {
		System.out.println("afterAdvice()");
	}
	
}