일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 구간합
- MariaDB Query Log
- Bean LifecCycle
- 백준 1235번
- @NoArgsConstructor
- @AllArgsConstructor
- select
- json
- this와 this() 차이
- Java
- InterruptException
- ajax
- 백준 11660번
- 자바 람다식
- SQL
- 생성자
- 합배열
- 상속과 참조
- interrupted()
- 마리아DB 쿼리 로그
- 2차원배열 구간합
- 슈더코드
- 백준
- 구간합구하기
- 백준 11659번
- map()
- pseudo-code
- this
- function test
- jquery
- Today
- Total
평범한 연구소
[Spring] AOP (관점 지향 프로그래밍) 본문
AOP (Aspect Oriented Programming)
- 관점 지향 프로그래밍은 객체 지향 프로그래밍(Object Oriented Programming)을 더욱 OOP답게 사용할 수 있도록 도와주는 개념이다.
- 핵심 비지니스 로직과 공통 모듈을 분리하여 개발자가 좀 더 비지니스 로직에만 집중해서 처리할 수 있는 방법을 제공한다.
- 공통 모듈을 적용하면 의존 관계의 복잡성과 코드 중복을 해소할 수 있다. 즉, 공통 모듈(로깅, 보안인증, 트랜잭션 등)을 별도로 작성한 후 코드 밖에서 비지니스 로직 사이에 삽입하는 것이 AOP 기능을 사용한 개발이다.
AOP 용어
타겟 (Target)
- 핵심 기능을 담고 있는 모듈.
- 공통 모듈을 부여할 대상.
어드바이스 (Advice)
- 타겟에 제공할 공통 기능(부가 기능)을 담고 있는 모듈
- Advice 종류
- Before Advice : target의 메소드 호출 전에 적용
- After returning : target의 메소드 호출 이후에 적용
- After throwing : target의 예외 발생 후 적용
- After : target의 메소드 호출 후 예외 발생에 관계 없이 적용
- Around : target의 메소드 호출 이전과 이후 모두 적용
조인 포인트 (Join Point)
- Advice가 적용될 위치.
- Target 객체가 구현한 인터페이스의 모든 메소드는 Join Point.
포인트 컷 (Pointcout)
- Advice를 적용할 Target의 메소드를 선별하는 정규 표현식
- execution 으로 시작하고 메소드의 Signature를 비교하는 방법을 주로 이용한다.
에스펙트 (Aspect)
- AOP의 기본 모듈
- Advice + Pointcut
- 싱글톤 형태의 객체로 존재.
어드바이저 (Advisor)
- Advice + Pointcut
- Spring AOP 에서만 사용되는 특별한 용어. Aspect와 동일.
위빙 (Weaving)
- Pointcut 에 의해 결정된 Target 의 Join Point에 Advice를 삽입하는 과정
- AOP가 핵심기능(Target)의 코드에 영향을 주지 않으면서 필요한 공통 모듈(Advice)을 추가할 수 있도록 해주는 핵심적인 처리 과정.
Pointcut 표현식
1) bean 명시자
- 스프링 빈을 이용하여 JoinPoint 설정
- bean(userBean) : 이름이 userBean인 빈의 모든 메소드
- bean(user*) : 빈의 이름이 user으로 시작하는 빈의 모든 메소드
2) execution 명시자
- 엑시큐션은 Advice를 적용할 모든 메소드를 명시할 때 사용.
- execution(수식어패턴 리턴타입 클래스이름패턴 이름패턴(파라미터패턴)) 예외 패턴
- 수식어 패턴: public, protected ... 생략 가능
- 리턴 타입 패턴
- 클래스 이름 패턴: 패키지를 포함한 클래스명 명시. 생략 가능.(패키지를 생략하면 모든 패키지, 클래스명을 생략하면 모든 클래스에 적용)
- 이름 패턴 : 메소드 이름 명시
- 파라미터 패턴
- 예외 패턴 : 생략 가능
- 패턴 문자
- * : 모든 값
- .. : 0개 이상
- 예시
- execution(public ** (..)) : public 메소드
- execution(* set*(..)) : 이름이 set으로 시작하는 모든 메소드
- execution(public * com.sp.app.service.UserService.*(..)) : UserService 인터페이스의 모든 메소드
- execution(public * com.sp.app.service.*.*(*)) : service 패키지의 파라미터가 하나인 모든 메소드
- execution(public * com.sp.app.service.*.*(..)) : service 패키지의 모든 메소드
- execution(public * com.sp.app.service..*.*(..)) : service 패키지와 하위 패키지의 모든 메소드
- execution(public * com.sp.app.service..*Service.*(..)) : service 패키지와 하위 패키지의 Service로 끝나는 모든 메소드
AOP 의 특징
- Spring은 프록시(Proxy) 기반 AOP 지원.
- Target 객체에 대한 프록시를 만들어 제공한다.
- Target을 감싸는 프록시는 실행시간(Runtime)에 생성된다.
- 프록시는 Advice를 타겟 객체에 적용하면서 생성되는 객체.
- 프록시(Proxy)가 호출을 가로챈다.(intercept)
- 프록시는 Target 객체에 대한 호출을 가로챈 다음 Advice의 부가 기능 로직을 수행하고, 그 후 Target의 핵심 기능 로직을 호출한다. (전 처리 Advice)
- Target의 핵심 기능 로직 메소드를 호출한 후에 공통 기능(Advice)를 수행한다. (후 처리 Advice)
- Spring AOP는 메소드 Join Point 만 지원한다.
- Spring은 동적 프록시를 기반으로 AOP를 구현한다. 따라서 메소드 Join Point 만 지원한다.
- 핵심 기능(Target)의 메소드가 호출되는 런타임 시점에서만 공통 기능(Advice)을 적용할 수 있다.
- AspectJ 같은 고급 AOP 프레임워크를 사용하면 객체의 생성, 필드 값의 조회와 조작, static 메소드 호출 및 초기화 등의 다양한 작업에 공통 기능을 적용할 수 있다.
AOP 구현
1) XML 기반
① 스프링 API 이용
- 공통 기능을 제공하는 Advice 클래스를 아래 인터페이스를 구현하여 작성.
MethodBeforeAdvice | 대상 객체의 메소드를 실행하기 전에 공통 기능을 실행 |
AfterReturningAdvice | 대상 객체의 메소드 실행 이후에 공통 기능 실행 |
ThrowsAdvice | 대상 객체의 메소드를 실행하는 도중 예외가 발생한 경우 |
MethodInterceptor | 세 가지 Advice를 하나로 묶은 Advice |
AfterReturningAdvice 활용 예제
package ex01.aop.after;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
// AfterReturningAdvice
// : 대상 객체의 메소드 실행 이후에 공통 기능을 실행할 때 사용하는 Advice
// : 예외가 없이 실행된 경우에만 실행
public class AfterLogAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target + " 클래스의 " + method.getName() + " 메소드 호출 후 ...");
System.out.println("리턴 값 : " + returnValue);
}
}
applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- Target 클래스 -->
<bean id="testServiceTarget" class="ex01.aop.after.TestServiceImpl"/>
<!-- Advice 클래스 -->
<bean id="afterAdvice" class="ex01.aop.after.AfterLogAdvice"/>
<!-- Advisor : Advice와 Pointcut을 연결하는 작업 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="afterAdvice"/>
<property name="patterns">
<list>
<value>.*save.*</value>
<value>.*write.*</value>
</list>
</property>
</bean>
<!-- AOP 적용 -->
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="testServiceTarget"/>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
</property>
</bean>
</beans>
MethodBeforeAdvice 활용 예제
package ex01.aop.before;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
// MethodBeforeAdvice
// : 대상 객체의 메소드를 실행하기 전에 공통 기능을 실행할 때 사용하는 Advice
public class BeforeLogAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
String s;
s = target.getClass() + " 클래스의 " + method.getName() + " 메소드 실행 전에 실행...";
s += "\n매개변수 : ";
if(args != null) {
for(int i=0; i<args.length; i++) {
s += args[i] + " ";
}
}
System.out.println(s);
}
}
applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- Target 클래스 : AOP를 적용할 클래스 -->
<bean id="testServiceTarget" class="ex01.aop.before.TestServiceImpl"/>
<!-- Advice 클래스 : 공통사항을 가지고 있는 클래스 -->
<bean id="beforeAdvice" class="ex01.aop.before.BeforeLogAdvice"/>
<!-- Pointcut : 실제 Advice가 적용될 Joinpoint(Advice가 적용될 지점) -->
<!-- 하나의 패턴 -->
<!--
<bean id="myPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*write.*"/>
</bean>
-->
<!-- 다수의 패턴 -->
<bean id="myPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="patterns">
<list>
<value>.*save.*</value>
<value>.*write.*</value>
</list>
</property>
</bean>
<!-- Advisor : Advice와 Pointcut을 연결하는 작업 -->
<bean id="testAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="beforeAdvice"/>
<property name="pointcut" ref="myPointcut"/>
</bean>
<!-- AOP 적용 -->
<bean id="testService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="testServiceTarget"/>
<property name="interceptorNames">
<list>
<value>testAdvisor</value>
</list>
</property>
</bean>
</beans>
② POJO 기반 AOP 구현
- 공통 기능을 제공하는 Advice 클래스를 POJO 기반으로 작성.
- XML 설정 파일에 <aop:config>를 이용해서 Aspect를 설정.
package ex04.aop.pojo;
import org.aspectj.lang.JoinPoint;
public class MyLogAspect {
// JoinPoint : 호출한 비지니스 메소드의 정보를 가지고 있는 인터페이스
public String beforeLogging(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
// getSignature(): 메소드의 시그니처(리턴타입, 이름, 매개변수)
System.out.println("before : " + methodName);
return methodName;
}
public void returningLogging(JoinPoint joinPoint, Object ret) {
String methodName = joinPoint.getSignature().getName();
System.out.println("returning : " + methodName + ", 리턴값: " + ret);
}
public void throwingLogging(JoinPoint joinPoint, Throwable ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("throwing: " + methodName + ", throws: "
+ ex.getClass().getName());
}
public void afterLogging(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("after: " + methodName);
}
}
applicationContext.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- Target 클래스 -->
<bean id="testService" class="ex04.aop.pojo.core.TestServiceImpl"></bean>
<!-- Advice -->
<bean id="myLogAspect" class="ex04.aop.pojo.MyLogAspect"></bean>
<!-- aop 네임스페이스를 이용한 AOP 설정 --> <!-- .*Service.*(..)): Service 인터페이스 안에 있는 모든 것에 적용 -->
<aop:config>
<aop:pointcut expression="execution(public * ex04.aop.pojo.core.*Service.*(..))"
id="publicMethod"/>
<aop:aspect id="loggingAspect" ref="myLogAspect">
<aop:before method="beforeLogging" pointcut-ref="publicMethod"/>
<aop:after-returning method="returningLogging"
pointcut-ref="publicMethod" returning="ret"/>
<aop:after-throwing method="throwingLogging"
pointcut-ref="publicMethod" throwing="ex"/>
<aop:after method="afterLogging" pointcut-ref="publicMethod"/>
</aop:aspect>
</aop:config>
</beans>
2) @Aspect 어노테이션
- @Aspect 어노테이션을 이용해서 부가 기능을 제공하는 Aspect 클래스를 작성.
- Aspect 클래스는 Advice를 구현하는 메소드와 Pointcut 을 포함.
- XML 설정 파일에 <aop:aspectj-autoproxy/> 설정하여 Aspect 관련 어노테이션을 활성화.
package ex05.aop.anno;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/*
- @Aspect
: 설정 파일에 Advice 및 Pointcout 등의 설정을 하지 않고 자동으로 Advice를 적용
*/
@Aspect
public class MyLogAspect {
@Pointcut(value = "execution(public * ex05.aop.anno.core.*Service.*(..))")
private void publicMethod() {
// Pointcut를 위한 가명 메소드
// private void이어야 하고, 메소드의 몸체는 없어야한다.
}
// JoinPoint : 호출한 비지니스 메소드의 정보를 가지고 있는 인터페이스
// 메소드 호출 전
@Before("publicMethod()")
public String beforeLogging(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
// getSignature(): 메소드의 시그니처(리턴타입, 이름, 매개변수)
System.out.println("before : " + methodName);
return methodName;
}
// 메소드가 예외없이 실행 된 후
@AfterReturning(value = "publicMethod()", returning = "ret")
public void returningLogging(JoinPoint joinPoint, Object ret) {
String methodName = joinPoint.getSignature().getName();
System.out.println("returning : " + methodName + ", 리턴값: " + ret);
}
// 예외 발생할 때
@AfterThrowing(value = "publicMethod()", throwing = "ex")
public void throwingLogging(JoinPoint joinPoint, Throwable ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("throwing: " + methodName + ", throws: "
+ ex.getClass().getName());
}
// 예외 발생 유무와 상관 없이 메소드 실행 후
@After("publicMethod()")
public void afterLogging(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("after: " + methodName);
}
}
applicationContext.xml
- <aop:aspectj-autoproxy/> : 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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:task="http://www.springframework.org/schema/task"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- AOP 관련 어노테이션 활성화 -->
<aop:aspectj-autoproxy/>
<!-- Target 클래스 -->
<bean id="testService" class="ex05.aop.anno.core.TestServiceImpl"></bean>
<!-- Advice -->
<bean id="myLogAspect" class="ex05.aop.anno.MyLogAspect"></bean>
</beans>
'Spring Framework' 카테고리의 다른 글
[Spring] 스프링에서 AJAX 사용하기 (@ResponseBody) (0) | 2022.11.17 |
---|---|
[Spring] MVC (0) | 2022.11.13 |
[Spring] 어노테이션 (Annotation) (0) | 2022.11.10 |
[Spring] 스프링 컨테이너, Bean (0) | 2022.11.09 |
[Spring] 의존 DI(Dependency Injection) (0) | 2022.11.09 |