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
'Spring > Ch3. Spring DI, AOP' 카테고리의 다른 글
Spring - 서비스 계층의 분리와 @Transactional 2 (0) | 2022.10.01 |
---|---|
Spring - 서비스 계층의 분리와 @Transactional (0) | 2022.09.30 |
Spring - Transaction (0) | 2022.09.29 |
Spring DI - Spring DI 흉내내기 (0) | 2022.09.25 |
Spring - Spring DI(1) (0) | 2022.09.13 |