Programming/SPRING

[SPRING] AOP(Aspect Oriented Programming)

너굴위 2023. 11. 16. 17:50
728x90
반응형

DAO database 연결 GUI(4)에 있음

 

DI
   객체 조립기
      Spring Bean Configuration - xml
      Spring Boot
      pojo + annotation  표기법
   
   lifecycle
      전처리와 후처리
   
   * 의존성 자동주입
- AOP 구현법
DI
AspectJ + DI
Annoitation
약결합 구조


어드바이스가 어디에서 동작할지 지정

import확인 필수

< aop:around => 전처리, 후처리 모두 다 적용

 

 

AOP(Aspect Oriented Programming)

- 여러 객체 공통으로 적용할 수 있는 기능을 분리해서 재사용성을 높여주는 프로그래밍 기법

- 핵심기능과 공통기능의 구현을 분리함으로써 핵심기능을 구현한 코드의 수정 없이 공통기능을 적용할 수 있게 만들어줌

- 핵심기능에 공통 기능을 삽입한다. 즉 핵심 기능의 코드를 수정하지 않으면서 공통 기능의 구현을 추가하는 것

더보기

< 핵심 기능에 공통 기능을 삽입하는 방법 >

1. 컴파일 시점에 코드에 공통 기능을 삽입하는 방법

2. 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입하는 방법

3. 런타임에 프록시 객체를 생성해서 공통 기능을 삽입하는 방법

 

* 1, 2번은 Spring AOP에서는 지원하지 않으며 AspectJ와 같이 AOP 전용 도구를 사용해서 적용할 수 있다.

<!--AspectJ Maven pom.xml에 dependency 추가-->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.9.20.1</version>

		</dependency> 
		
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.20.1</version>
		</dependency>

 

스프링 AOP 구현

  • Aspect로 사용할 클래스에 @Aspect 어노테이션을 붙인다
  • @Pointcut 어노테이션으로 공통기능을 적용할 Pointcut을 정의한다.
  • 공통 기능을 구현한 메서드에 @Around 어노테이션을 적용한다.

AOP 어노테이션 알아보기

@Aspect

- @Aspect 어노테이션을 붙여 공통 기능을 제공하는 클래스를 만들면, 프록시는 스프링 프레임워크가 알아서 만든다.

- @Aspect 어노테이션을 적용한 클래스는 Advice와 Pointcut을 함께 제공한다.

 

  @Pointcut

   - 공통 기능을 적용할 대상을 설정

@Before("${pattern}")

- 지정한 패턴에 해당하는 메소드가 실행되기 전에 동작

- 해당 어노테이션이 붙은 메소드의 반환 값은 void여야 한다.

@After("${pattern}")

- 지정한 패턴에 해당하는 메소드가 실행된 후에 동작

- 해당 어노테이션이 붙은 메소드의 반환 값은 void여야 한다.

@Around("${pattern}")

- 지정된 패턴에 해당하는 메소드의 실행되기 전, 실행된 후 모두에서 동작

- 해당 어노테이션이 붙은 메소드의 반환 값은 Object여야 한다. 


execution  명시자 표현식

- Aspect를 적용할 위치를 지정할 때 사용 (예제에서는 Around어노테이션에 사용하였다)

    execution 명시자의 기본 형식

    execution(수식어패턴? 리턴타입패턴 클래스이름패턴?메서드이름패턴(파라미터패턴))

- 각 패턴은 * 을 이용하여 모든 값을 표현할 수 있다. 또한 '..'을 이용하여 0개 이상이라는 의미를 표현할 수 있다.

 

 

EX 설명
execution(public void set*(..)) - 리턴 타입 void
- 메서드 이름 set으로 시작
- 파라미터가 0개 이상인 메서드 호출
- 파라미터 부분에 '..'을 사용 => 파라미터가 0개 이상
execution(* chap07.*.*()) - chap07 패키지의 타입에 속한 파라미터가 없는 모든 메서드 호출
execution(* chap07..*.*(..)) - chap07 패기지 및 하위 패키지에 있음 (패키지 이름뒤에 ..를 사용해 해당 패키지 혹은 하위 패키지를 표현할 수 있다)

- 파라미터가 0개 이상
execution(Long chap07.Calculator.factorial(..)) - return 타입이 Long인 Calculator 타입의 factorial() 메서드 호출
execution(* get*(*,*)) - 이름이 get으로 시작하고 파라미터가 2개인 메서드 호출
   (* 한 개 붙이면 파라미터가 1개인 메서드 호출)
execution(* read*(Integer, ..)) - 메서드 이름이 read로 시작하고, 첫 번째 파라미터 타입이 정수형(Integer)이며, 한 개 이상(..로 표현)의 파라미터를 갖는 메서드 호출

 

 

Main 코드

//MainEx.java
package com.exam.aop03;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;

import com.exam.aop03.config.BeanConfig;
import com.exam.aop03.model.Action;

public class MainEx {

	public static void main(String[] args) {
			
		//annotation으로 선언
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BeanConfig.class);
				
		//제네릭으로 사용
//		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(new FileSystemResource("./src/com/exam/aop03/context.xml"));
			
			
		Action action = ctx.getBean("writeAction", Action.class);
			
		action.execute();
			
		ctx.close();
	}

}

Advice 코드

//BasicAdvice1.java
package com.exam.aop03.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class BasicAdvice1 {
	
	//이름이 execute이며 파라미터가 없는 메서드 호출
	//execution(* exe*())  => 이름이 exe로 시작하고 파라미터가 없는 메서드 호출
	@Around("execution(* execute())")
	public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
		
		System.out.println("전처리 구간1");
		
		Object rtnObj = joinPoint.proceed();
		
		System.out.println("후처리 구간1");
		
		return rtnObj;
		
	}
}

 

//BasicAdvice2.java
package com.exam.aop03.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class BasicAdvice2 {
	
	@Around("execution(* execute())")
	public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
		
		System.out.println("전처리 구간2");
		
		Object rtnObj = joinPoint.proceed();
		
		System.out.println("후처리 구간2");
		
		return rtnObj;
		
	}
}

 

//BasicAdvice3.java
package com.exam.aop03.advice;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BasicAdvice3 {
	
	//어노테이션을 통한 전처리/후처리 삽입(실행)
	@Before("execution(* execute())")
	public void before() throws Throwable{
		System.out.println("전처리 실행");
		
	}
	
	@After("execution(* execute())")
	public void after() throws Throwable{
		System.out.println("후처리 실행");
	}
}

Config코드

//BeanConfig.java
package com.exam.aop03.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.exam.aop03.advice.BasicAdvice1;
import com.exam.aop03.advice.BasicAdvice2;
import com.exam.aop03.advice.BasicAdvice3;
import com.exam.aop03.model.WriteAction;

@EnableAspectJAutoProxy
public class BeanConfig {

	@Bean
	public WriteAction writeAction() {
		WriteAction writeAction = new WriteAction();
		writeAction.setWriter("손수빈");
		return writeAction;
	}
	
//	@Bean
//	public BasicAdvice1 basicAdvice1() {
//		return new BasicAdvice1();
//	}
//	
//	
//	@Bean
//	public BasicAdvice2 basicAdvice2() {
//		return new BasicAdvice2();
//	}
	
	@Bean
	public BasicAdvice3 basicAdvice3() {
		return new BasicAdvice3();
	}
}

Model코드

//Action.java
package com.exam.aop03.model;

public interface Action {
	void execute();
}
//WriteAction.java
package com.exam.aop03.model;

public class WriteAction implements Action {
	private String writer;	
	
	public WriteAction() {
		// TODO Auto-generated constructor stub
		System.out.println("WriteAction() 생성자 호출");
	}
	
	public void setWriter(String writer) {
		this.writer = writer;
	}
		
	// 핵심 기능
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		System.out.println("execute() 시작");
		System.out.println("Hello" + writer);
		System.out.println("execute() 끝");
	}
	
}

<!--context.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"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
	">
	
	<!-- AOP 객체 생성 -->
    <!-- 어노테이션을 사용하지 않고 bean을 context.xml에서 정의하여 사용할 때 작성함-->
	<bean id="basicAdvice1" class="com.exam.aop03.advice.BasicAdvice1" />
	<bean id="basicAdvice2" class="com.exam.aop03.advice.BasicAdvice2" />
	
	<bean name="writeAction" class="com.exam.aop03.model.WriteAction" >
		<property name="writer">
			<value>홍길동</value>
		</property>
	</bean>
	
    
    
	<!-- AOP 환경설정 -->
    <!-- autoproxy로 어노테이션이 붙은 곳을 자동으로 처리 할 수 있도록 함-->
	<aop:aspectj-autoproxy />
	
</beans>

@Before과 @After 어노테이션을 통해 전처리 후처리 실행 결과


 

 

 

728x90
반응형