article thumbnail image
Published 2022. 9. 30. 13:30

AOP(Aspect Oriented Programming)

 

관점 지향프로그래밍, 횡단 관심사, cross-cutting concerns

부가 기능(advice)을 동적으로 추가해주는 기술

메서드의 시작 또는 끝에 자동으로 코드(advice)를 추가

AopMain.java

 

package com.jcy.usedhunter;

import java.lang.reflect.Method;

public class AopMain {
	public static void main(String[] args) throws Exception {
		MyAdvice myAdvice = new MyAdvice();
		
		Class myClass = Class.forName("com.jcy.usedhunter.MyClass");
		Object obj = myClass.newInstance();
		
		for(Method m : myClass.getDeclaredMethods()) {
			myAdvice.invoke(m, obj, null);
	
		} 
	}
}
class MyAdvice{
	void invoke(Method m, Object obj, Object... args) throws Exception{
		System.out.println("[before]{");
		m.invoke(obj, args);
		System.out.println("}[after]");
	}
}

class MyClass{
	void aaa() {
		System.out.println("aaa() is called.");
	}
	void aaa2() {
		System.out.println("aaa2() is called.");
	}
	void aaa3() {
		System.out.println("aaa3() is called.");
	}
}
[before]{
aaa2() is called.
}[after]
[before]{
aaa() is called.
}[after]
[before]{
aaa3() is called.
}[after]

 

 

 

패턴 사용하기

package com.jcy.usedhunter;

import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AopMain {
	public static void main(String[] args) throws Exception {
		MyAdvice myAdvice = new MyAdvice();
		
		Class myClass = Class.forName("com.jcy.usedhunter.MyClass");
		Object obj = myClass.newInstance();
		
		for(Method m : myClass.getDeclaredMethods()) {
			myAdvice.invoke(m, obj, null);
	
		} 
	}
}
class MyAdvice{
	Pattern p = Pattern.compile("a.*");
	
	boolean matches(Method m) {
		Matcher matcher = p.matcher(m.getName());
		return matcher.matches();
	}
	void invoke(Method m, Object obj, Object... args) throws Exception{
		if (matches(m)) 
		System.out.println("[before]{");
		
		m.invoke(obj, args);
		
		if (matches(m)) 
		System.out.println("}[after]");
	}
}

class MyClass{
	void aaa() {
		System.out.println("aaa() is called.");
	}
	void aaa2() {
		System.out.println("aaa2() is called.");
	}
	void bbb() {
		System.out.println("bbb() is called.");
	}
}
[before]{
aaa() is called.
}[after]
[before]{
aaa2() is called.
}[after]
bbb() is called.

 

 

 

@Transactional 사용하기

 

package com.jcy.usedhunter;

import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.transaction.annotation.Transactional;

public class AopMain {
	public static void main(String[] args) throws Exception {
		MyAdvice myAdvice = new MyAdvice();
		
		Class myClass = Class.forName("com.jcy.usedhunter.MyClass");
		Object obj = myClass.newInstance();
		
		for(Method m : myClass.getDeclaredMethods()) {
			myAdvice.invoke(m, obj, null);
	
		} 
	}
}
class MyAdvice{
	Pattern p = Pattern.compile("a.*");
	
	boolean matches(Method m) {
		Matcher matcher = p.matcher(m.getName());
		return matcher.matches();
	}
	void invoke(Method m, Object obj, Object... args) throws Exception{
		if (m.getAnnotation(Transactional.class)!=null) 
		System.out.println("[before]{");
		
		m.invoke(obj, args);
		
		if (m.getAnnotation(Transactional.class)!=null)  
		System.out.println("}[after]");
	}
}

class MyClass{
	@Transactional
	void aaa() {
		System.out.println("aaa() is called.");
	}
	void aaa2() {
		System.out.println("aaa2() is called.");
	}
	void bbb() {
		System.out.println("bbb() is called.");
	}
}
aaa2() is called.
[before]{
aaa() is called.
}[after]
bbb() is called.

 

 

코드를 자동으로 추가하는 곳

 

앞 : Before Advice

뒤 : After Advice

앞뒤 : Around Advice

 

 

 

 

AOP 관련 용어

 

용어 설명
target advice 가 추가될 객체
advice target 에 동적으로 추가될 부가 기능(코드)
join point advice 가 추가(join) 될 대상(메서드)
pointcut join point 들을 정의한 패턴. ex : excution(*com.jcy.*.*(...))
proxy target 에 advice 가 동적으로 추가되어 생성된 객체
weaving target 에 advice 를 추가해서 proxy 를 생성하는 것

 

 

Advice의 종류

 

Advice의 설정은 XML 과 에너테이션, 두 가지 방법으로 가능

종류  Annotation 설명
around advice @Around 메서드의 시작과 끝 부분에 추가되는 부가 기능
before advice @Before 메서드의 시작 부분에 추가되는 부가 기능
after advice @After 메서드의 끝 부분에 추가되는 부가 기능
after returning @AfterReturning 예외가 발생하지 않았을 때, 실행되는 부가 기능
after throwing @AfterThrowing 예외가 발생 했을 때, 실행되는 부가 기능

 

 

pointcut expression

 

advice가 추가될 메서드를 지정하기 위한 패턴

excution(반환타입 패키지명.클래스.메서드명(매개변수 목록))

 

 

 

AspectJ

 

		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

 

Spring Aop

				<dependency>
				    <groupId>org.springframework</groupId>
				    <artifactId>spring-aop</artifactId>
				    <version>${org.springframework-version}</version>
				</dependency>

 

AspectJ Weaver

		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
		<dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjweaver</artifactId>
		    <version>1.9.7</version>
		    <scope>runtime</scope>
		</dependency>

 

 

AopMain2.java

package com.jcy.usedhunter.aop;


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

public class AopMain2 {

	public static void main(String[] args) {
		ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context_aop.xml");
		MyMath mm = (MyMath) ac.getBean("myMath");
		
		
		mm.add(3, 5);
		mm.multyply(3, 5);
		
	}

}

 

root.comtext_aop.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
		
	<aop:aspectj-autoproxy/>
	 <context:component-scan base-package="com.jcy.usedhunter.aop" />
	</beans>

 

LoggingAdvice.java

package com.jcy.usedhunter.aop;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAdvice {
	@Around("execution(* com.jcy.usedhunter.aop.MyMath.*(..))") // pointcut - 부가기능이 적용될 메서드의 패턴
	public Object methodCallLog(ProceedingJoinPoint pjp) throws Throwable {
		long start = System.currentTimeMillis();
		System.out.println("[start]" + pjp.getSignature().getName() + Arrays.toString(pjp.getArgs()));
		
		Object result = pjp.proceed(); // target 의 메서드를 호출
		
		System.out.println("result"+result);
		System.out.println("[end]"+(System.currentTimeMillis()-start)+"ms");
		return result;
		
	}

}
[start]add[3, 5]
result8
[end]15ms
[start]multyply[3, 5]
result15
[end]0ms

 

 

add 만 부가기능 추가하기

package com.jcy.usedhunter.aop;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAdvice {
	@Around("execution(* com.jcy.usedhunter.aop.MyMath.add*(..))") // pointcut - 부가기능이 적용될 메서드의 패턴
	public Object methodCallLog(ProceedingJoinPoint pjp) throws Throwable {
		long start = System.currentTimeMillis();
		System.out.println("[start]" + pjp.getSignature().getName() + Arrays.toString(pjp.getArgs()));
		
		Object result = pjp.proceed(); // target 의 메서드를 호출
		
		System.out.println("result"+result);
		System.out.println("[end]"+(System.currentTimeMillis()-start)+"ms");
		return result;
		
	}

}
package com.jcy.usedhunter.aop;


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

public class AopMain2 {

	public static void main(String[] args) {
		ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context_aop.xml");
		MyMath mm = (MyMath) ac.getBean("myMath");
		
		
		mm.add(3, 5);
		System.out.println("mm.multyply= "+mm.multyply(3, 5));
		
	}

}
[start]add[3, 5]
result8
[end]16ms
mm.multyply= 15

 

복사했습니다!