AOP 란? 모든 영역에 공통적으로 적용되는 코딩이다.
주어진 조건에 따라서 특정 클래스 메서드에 적용한다.
횡단 관심사 : 모든 코딩에 공통적으로 적용되는 코딩
크로스커팅 관심사(cross-cutting concerns)는 다른 관심사에 영향을 미치는 프로그램의 애스펙트이다.
조인 포인트 : 어디다가 적용할지 EX) 짝수 클래스 홀수 클래스
어드바이스 : 조인 포인트에 적용할 코드
포인트 컷 : 언제 어드바이스를 실행하는지 위치표시(시작하거나 끝날 때)
애스팩트 : 어드바이스(동작)와 포인트 컷(동작을 적용하는 조건)을 조합해서 횡단 관심사에 대한 코드와 그것을 적용할 지점을 정의한 것.
추가
1. 횡단 관심사
- 모든 영역에 공통적으로 적용되는 코딩
- 중간중간 삽입되어야 할 기능들(로깅,보안,트랜잭션)
- 주로 메소드 단위로 확인한다.
2. 조인 포인트
- 어드바이스를 실행할 수 있는 지점 -> 포인트 컷의 후보
- 어플리케이션 실행의 특정 지점
3. 어드바이스
- 조인포인트에게 실행 할 것인가?
- 종류
- before : 메서드 호출 전
- after : 메서드 호출 후
- after returning : 메서드가 정상적으로 실행 된 후
- after throwing : 메서드를 실행하는 도중 에러가 발생하는 경우
- around : 메서드 앞 뒤로 실행한다.
- 조인포인트에서 실행 할 코드 이다.
4. 포인트 컷
- 어드바이스를 실행하는 조인 포인트
- 어드바이스를 실행하는 시간(위치)
5. 애스팩트
- 어드바이스 + 포인트컷
- 무엇을 언제할지 정의한다
메이븐에서 라이브러리 등록
Spring AOP 5.3.17
AspectJ Weaver 1.8.10
AspectJ Runtime 1.8.10 aspectjrt
CGLib 3.2.4
pom.xml 환경설정 파일 ( 여기서 메이븐 라이브러리를 등록한다 )
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples.spring</groupId>
<artifactId>spring-utility</artifactId>
<version>1.0.0.CI-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring Utility</name>
<url>https://www.springframework.org</url>
<description>
<![CDATA[
This project is a minimal jar utility with Spring configuration.
]]>
</description>
<properties>
<maven.test.failure.ignore>true</maven.test.failure.ignore>
<spring.framework.version>3.0.6.RELEASE</spring.framework.version>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<!-- aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<!-- aspectjrt -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<!-- AOP Proxy cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
중요한 것은
Spring-aop는 <version>${spring.framework.version}</version> 현재 스프링 버전에 맞춰줘야 한다.
메이븐 라이브러리를 pom.xml 에 추가해준다.
SpringAOP
src/main/java - com.exe.aop - TargetA.java
package com.exe.aop;
import org.springframework.stereotype.Component;
@Component("targetA")
public class TargetA {
public void doSomething1() {
System.out.println("TargetA.doSomething1");
}
public void doSomething2() {
System.out.println("TargetA.doSomething2");
}
public void doAnother1() {
System.out.println("TargetA.doAnother1");
}
public void doAnother2() {
System.out.println("TargetA.doAnother2");
}
}
TargetB.java
src/main/java - com.exe.aop - TargetB.java
package com.exe.aop;
import org.springframework.stereotype.Component;
@Component("targetB")
public class TargetB {
public void doSomething1(String str) {
System.out.println("TargetB.doSomething1:" + str);
}
public void doSomething2() {
System.out.println("TargetB.doSomething2");
}
public void doAnother1() {
System.out.println("TargetB.doAnother1");
}
public void doAnother2() {
System.out.println("TargetB.doAnother2");
}
}
src/main/java - app-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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- context 부분을 aop도 똑같이 적어준다. -->
aop도 context처럼 문서를 위한 XML 네임스페이스를 명시한다.
<description>Example configuration to get you started.</description>
<context:component-scan base-package="com.exe.aop" />
<bean id="beforeAdvice" class="com.exe.aop.MyBeforeAdvice"/>
base-package 어느 패키지에 지정할 것인지. 모두 다 적용하려면 *
MyBeforeAdvice 클래스에 beforeAdvice객체 생성 - 의존성 주입
com.exe.aop 패키지에서 MyBeforeAdvice.java
package com.exe.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
public class MyBeforeAdvice {
public void beforeMethodCall() {
// 어드바이스
// 메소드가 실행되기전 실행될 코드
System.out.println("메소드 실행전(BeforeAdvice)");
}
}
특정 메서드가 실행되기 전 beforeMethodCall 실행
<aop:config>
<aop:aspect ref="beforeAdvice">
<aop:pointcut expression="execution(public void com..aop.*.*(*.*))"
id="beforePointcut"/>
<aop:before method="beforeMethodCall" pointcut-ref="beforePointcut"/>
</aop:aspect>
aspect는 pointcut과 어드바이스를 실행할 프로그램을 어디다가 적용할지 정의한다.
xecution(public void com.. aop.*.*2(..))"
- execution(메서드 접근 지정자) 반환 값 패키지. 클래스. 메서드명(인수)
- 메서드에 끝에 2가 들어가는 것만 실행
aop:before method의 pointcut-ref는 pointcut id beforePointcut를 참조한다.
expression = execution(public void com.. aop.*.*(*.*)) 특정 메서드가 실행하기 전에
beforeMethodCall 메서드를 호출하라는 의미이다.
AopMain.java
package com.exe.aop;
import org.springframework.context.support.GenericXmlApplicationContext;
public class AopMain {
public static void main(String[] args) {
// xml파일을 읽어온다.
GenericXmlApplicationContext context =
new GenericXmlApplicationContext("app-context.xml");
TargetA ta = (TargetA)context.getBean("targetA"); //@Componet 객체 읽어옴
TargetB tb = (TargetB)context.getBean("targetB");
// 특정메소드가 실행되기전에 호출시키기
// beforeMethodCall 이 실행되고 나서 각각의 메소드가 실행됨
ta.doSomething1();
ta.doSomething2();
ta.doAnother1();
ta.doAnother2();
// "execution(public void com..aop.*B.*(..))
// targetA는 다 나오지만 TargetB는 메소드실행전..(BeforeAdvice)실행후 출력
/*
tb.doSomething1("테스트");
tb.doSomething2();
tb.doAnother1();
tb.doAnother2();
*/
// "execution(public void com..aop.*.*(..))
// execution(public void com..aop.*.*S*(..))" 대문자 S자가 들어간 메소드만 실행
// execution(public void com..aop.*.*2(..))" 메소드에 끝에 2가 들어가는 것만 실행
// "execution(public void com..aop.*.*(String))" String형태 매개변수에만 있는 메소드에 적용
}
}
MyAfterAdvice.java
public class MyAfterAdvice {
// 하고싶은 작업
@After("execution(public void com..aop.*.*(..))")
public void afterMethodCall() {
System.out.println("메소드 실행후(AfterAdvice)...");
}
}
<!-- aop:after : pointcut에 지정한 특정메소드가 실행되고 나서 실행 -->
<!-- execution(메소드 접근지정자 반환값 패키지.클래스.메소드명(인수) -->
<!-- execution(* com..aop.*.*(String) -->
<!-- 특정 메소드가 실행된후 afterMethodCall 실행 -->
<aop:aspect ref="afterAdvice">
<aop:pointcut id="afterPointcut"
expression="execution(public void com.exe.aop.*.*(..))"/>
<aop:after method="afterMethodCall"
pointcut-ref="afterPointcut"/>
</aop:aspect>
MyAroundAdvice.java
public class MyAroundAdvice {
// Object : 그 메소드가 어느것을 반환할지 모르므로 Object로 받음
// around : before -> 메소드 -> after
// 메소드를 실행시켜주는 코딩은 ProceedingJoinPoint
@Around("execution(public void com..aop.*.*(..))")
public Object aroundMethodCall(ProceedingJoinPoint jointPoint) {
// 반환값
Object result = null;
try {
System.out.println("메소드 실행 전(AroundAdvice)..."); // Arround 실행 전
result = jointPoint.proceed(); // Arround 메소드 실행 전에 하는 메소드
/* ta.doSomething1();
ta.doSomething2();
ta.doAnother1();
ta.doAnother2();*/
System.out.println("메소드 실행 후(AroundAdvice)..."); // 메소드가 끝나고 실행
} catch (Throwable e) { // 회장나와 ! 사장Exception 보다 높음
}
return result;
}
}
around는 beforeAdvice와 afterAdvice 합친 것이다.
즉 beforeAdvice 실행하면서 리턴 값을 주고 나서 afterAdvice가 실행한다.
이때 리턴 값의 타입은 object이다. (무엇을 줄지 모르니 최상위 객체)
ProceedingJoinPoint을 전달하며 proceed() 메서드 호출을 통해 대상 포인트컷을 실행한다.
result = joinPoint.proceed(); 메소드 실행 전 메서드가 호출되고 나서
메서드를 통해 반환 값을 리턴 주고 나서 메서드가 끝나고 실행된다.
catch문에는 Throwable 회장 나와! Throwable를 적어줘야 한다.
<bean id="afterAdvice" class="com.exe.aop.MyAfterAdvice"/>
<bean id="aroundAdvice" class="com.exe.aop.MyAroundAdvice"></bean>
<bean id="afterReturnAdvice" class="com.exe.aop.MyAfterReturnAdvice"/>
<bean id="afterThrowAdvice" class="com.exe.aop.MyAfterThrowAdvice"/>
afterAdvice : 특정 메서드가 실행된 후 afterMethodCall 실행
aroundAdvice : before 실행하면서 리턴 주고 after실행
afterReturnAdvice : 무조건 메서드가 정상적 끝났을 때 실행
afterThrowAdvice : 메소드가 오류가 났을때 실행
<!-- 특정 메소드가 실행되기전 beforeMethodCall 실행 -->
<aop:config>
<aop:aspect ref="beforeAdvice"> <!-- 애스팩트 : 어드바이스(동작)와 포인트컷(동작을적용하는조건)을 조합해서
횡단 관심사에 대한 코드와 그것을 적용할 지점을 정의한 것 -->
<aop:pointcut id ="beforePointcut"
expression="execution(public void com..aop.*.*(..))" /> <!-- B자로 끝나느 클래스만 적용 -->
<aop:before method="beforeMethodCall"
pointcut-ref="beforePointcut"/> <!-- 메소드가 실행되기 전 expression조건에 before에 적용한다. -->
</aop:aspect>
<!-- aop:after : pointcut에 지정한 특정메소드가 실행되고 나서 실행 -->
<!-- execution(메소드 접근지정자 반환값 패키지.클래스.메소드명(인수) -->
<!-- execution(* com..aop.*.*(String) -->
<!-- 특정 메소드가 실행된후 afterMethodCall 실행 -->
<aop:aspect ref="afterAdvice">
<aop:pointcut id="afterPointcut"
expression="execution(public void com.exe.aop.*.*(..))"/>
<aop:after method="afterMethodCall"
pointcut-ref="afterPointcut"/>
</aop:aspect>
<!-- b 실행 하면서 리턴 주고 a실행해야함 -->
<aop:aspect ref="aroundAdvice">
<aop:pointcut id ="aroundPointcut"
expression="execution(public void com..aop.*.*(..))" />
<aop:around method="aroundMethodCall"
pointcut-ref="aroundPointcut"/>
</aop:aspect>
<!-- 무조건 메소드가 정상적 끝났을때 -->
<!-- expression 표현 execution 집행 -->
<aop:aspect ref="afterReturnAdvice">
<aop:pointcut id ="afterReturnPointcut"
expression="execution(public void com..aop.*.*(..))" />
<aop:after-returning method="afterReturnMethodCall"
pointcut-ref="afterReturnPointcut"/>
</aop:aspect>
<!-- 메소드가 오류가 났을때 실행 -->
<aop:aspect ref="afterThrowAdvice">
<aop:pointcut id ="afterThrowPointcut"
expression="execution(public void com..aop.*.*(..))" />
<aop:after-throwing method="afterThrowMethodCall"
pointcut-ref="afterThrowPointcut"/>
</aop:aspect>
</aop:config>
</beans>
어노테이션으로 만들어보기
MyAfterAdvice 클래스
@Aspect
@Component
public class MyAfterAdvice {
// 하고싶은 작업
@After("execution(public void com..aop.*.*(..))")
public void afterMethodCall() {
System.out.println("메소드 실행후(AfterAdvice)...");
}
}
@Aspect 기능 아래의 기능과 같다.
<aop:aspect ref="afterAdvice">
<aop:pointcut id="afterPointcut"
expression="execution(public void com.exe.aop.*.*(..))"/>
<aop:after method="afterMethodCall"
pointcut-ref="afterPointcut"/>
</aop:aspect>
@Component 기능은 아래와 같다.
<bean id="afterAdvice" class="com.exe.aop.MyAfterAdvice"/>
@After("execution(public void com.. aop.*.*(..))")
하고 싶은 작업을 적는다. Aftrer 특정 조건의 메서드들이 실행되고 나서
afterMethodCall() 메서드가 실행된다
MyAfterReturnAdvice 클래스
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAfterReturnAdvice {
// 메소드가 정상적으로 실행이 됬을때
@AfterReturning("execution(public void com..aop.*.*(..))")
public void afterReturnMethodCall() {
System.out.println("메서드 실행후(After return)..");
// TargetA.doSomething2 정상적으로 실행이 됬을때
// 메서드 실행후(After return)..
}
}
@AfterReturning :특정 메서드가 정상적으로 실행이 됬을 때 afterReturnMethodCall() 메소드가 실행된다.
@Aspect
@Component
public class MyAfterThrowAdvice {
// 에러가 났을때 실행
@AfterThrowing("execution(public void com..aop.*.*(..))")
public void afterThrowMethodCall() {
System.out.println("메소드 에러발생후 실행(afterThrow...");
}
}
@AfterThrowing : 특정 메서드가 에러가 발생하여 실행하지 못했을때 afterThrowMethodCall() 메소드가 실행된다.
package com.exe.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAroundAdvice {
// Object : 그 메소드가 어느것을 반환할지 모르므로 Object로 받음
// around : before -> 메소드 -> after
// 메소드를 실행시켜주는 코딩은 ProceedingJoinPoint
@Around("execution(public void com..aop.*.*(..))")
public Object aroundMethodCall(ProceedingJoinPoint jointPoint) {
// 반환값
Object result = null;
try {
System.out.println("메소드 실행 전(AroundAdvice)..."); // Arround 실행 전
result = jointPoint.proceed(); // Arround 메소드 실행 전에 하는 메소드
/* ta.doSomething1();
ta.doSomething2();
ta.doAnother1();
ta.doAnother2();*/
System.out.println("메소드 실행 후(AroundAdvice)..."); // 메소드가 끝나고 실행
} catch (Throwable e) { // 회장나와 ! 사장Exception 보다 높음
}
return result;
}
}
@Around("execution(public void com.. aop.*.*(..))"
특정 메서드가 실행되기 전에 실행 전 메소드 호출시키고
result = jointPoint.proceed(); 실행하면서 반환 값을 준다.
반환 값은 object로 받는다. (무엇을 받을지 모르니까)
반환 값을 주고 나서 실행 후 메서드를 실행한다.
jointPoint.proceed()는
Throwable로 예외처리해줘야 한다.
MyBeforeAdvice 클래스
package com.exe.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect // <aop:aspect~</aop:aspect>구조로 만듬
@Component // 객체 생성
public class MyBeforeAdvice {
@Before("execution(public void com..aop.*.*(..))")//내가 정해준 execution에 적용
public void beforeMethodCall() {
// 어드바이스
// 메소드가 실행되기전 실행될 코드
System.out.println("메소드 실행전(BeforeAdvice)");
}
}
@Before(executuin(public void com.. aop.*.*(..))") 내가 정해준 execution에 적용한다.
어노테이션으로 만든 AOP도 똑같은 결과가 나오게 된다.
AroundAdvice : 특정 메서드 실행전 한번 호출하고 나서 리턴값을 주고 나서 특정 메소드 실행 후 메소드 출력
BeforeAdvice : 특정 메소드 실행 전에 호출
AroundAdvice : 특정 메서드가 실행 후에 호출
After return : 특정 메소드가 정상적으로 끝나면 호출
afterThrowAdvice : 특정 메서드가 정상적으로 실행하지 못했으면 호출
'BACKEND > 스프링 Spring' 카테고리의 다른 글
[spring3.0] RowMapper 에 대해서 (2) | 2022.03.29 |
---|---|
[spring3.0] 롬 복(loombok) (0) | 2022.03.28 |
[spring3.0] 의존성주입 DI 어노테이션 GET,POST 방식 (0) | 2022.03.24 |
[spring3.0] sts 환경세팅 (0) | 2022.03.24 |
[spring2.5] 스프링 객체 생성 (0) | 2022.03.23 |