반응형
- 일반적으로 특별한 경우가 아니면, 매개변수는 잘 만들지 않음
- 핵심관심모듈이 가진 데이타에 대한 처리의 부가적인 기능을 하고 싶을 때 매개변수 사용함
01. [핵심관심모듈] DAO클래스&Service클래스
JoinPointBean
- 핵심관심모듈이 가진 메소드들은 개발자들에 의해 호출하므로 아무렇게나 작성 가능
package xyz.itwill07.aop;
//핵심관심모듈
public class JoinPointBean {
public void add() {
System.out.println("### JoinPointBean 클래스의 add() 메소드 호출 ###");
}
public void modify(int num, String name) {
System.out.println("### JoinPointBean 클래스의 modify(int num, String name) 메소드 호출 ###");
}
public void remove(int num) {
System.out.println("### JoinPointBean 클래스의 remove(int num) 메소드 호출 ###");
}
public String getName() {
System.out.println("### JoinPointBean 클래스의 getName() 메소드 호출 ###");
return "홍길동";
}
public void calc(int num1, int num2) {
System.out.println("### JoinPointBean 클래스의 calc(int num1, int num2) 메소드 호출 ###");
System.out.println("몫 = "+(num1/num2));
}
}
02. [횡단관심모듈] : Advice클래스
- 횡단관심모듈이 가진 메소드는 aspectj에 의해 핵심관심모듈이 실행되기 전에 전 , 후 , 전후 에 호출되기 위해 사용되므로 우리 마음대로 만들 수는 없음
- 횡단관심모듈의 메소드명은 상관없음 xml 파일에서 설정할 수 있음
- 그러나 매개변수는 딱 정해져있는 매개변수만을 받을 수 있음 (아무렇게 설정 불가능)
JoinPointAdvice
package xyz.itwill07.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
//횡단관심모듈 - Advice 클래스
public class JoinPointAdvice {
// [Around Advice 메소드]를 제외한 나머지 Advice 메소드 :
// => 반환형 : 일반적으로 [반환형을 void]로 작성 - 반환해봤자 얻어서 사용할 수 있는 곳이 없으믐로 그냥 void 하는 것임
// => 매개변수: 매개변수를 작성하지 않거나, 매개변수를 작성한다면 JoinPoint 인터페이스로 선언된 매개변수만을 작성 가능
// => Advice 메소드를 작성 규칙에 맞지 않게 작성할 경우 IllegalArgumentException 발생 (매개변수를 잘못 썼어요! 예외)
//[JoinPoint 객체] : 타겟 메소드(핵심관심모듈이 가진 메소드) 관련 정보가 저장된 객체
// => 스프링 컨테이너가 Advice 클래스의 메소드를 호출할 때 타겟메소드 관련 정보를 JoinPoint 객체에 저장하여 매개변수에 전달
// => Advice 클래스의 메소드에서 타겟메소드 관련 정보가 필요한 경우 매개변수를 선언하여 사용
//1.
//Before Advice 메소드 - 전 삽입
//사용가능한 매개변수 : 없음 or JoinPoint
public void beforeDisplay(JoinPoint joinPoint) {
//System.out.println("[before] 핵심관심코드 실행 전에 삽입되어 실행될 횡단관심코드");
//JoinPoint.getTarget() : 타겟메소드를 호출한 객체(핵심관심모듈)를 반환하는 메소드 - Object 타입으로 객체 반환
//Object.getClass() : 객체를 생성한 클래스의 Class 객체(Clazz)를 반환하는 메소드
//리플랙션 기술을 이용할 수도 있으나, 리플랙션은 접근지정자에 상관없이 필드에 모두 접근하므로 위험함 , 잘못만드면 무한루프에 빠지거나 오버플로우 발생하여 메모리 어마무시하게 차지할 수도 있음
//=> Class 객체(클래즈) : 클래스 정보를 저장하고 있는 객체 - ex) 클래스명(패키지명 포함) , 클래스는어디의메모리에 저장되고 있는지
//=> Class 객체(클래즈 - Clazz) 만드는 방법 3가지 : class.forName | class.Class | class.getClass()
//Class.getName() : Class 객체에 저장된 클래스(패키지포함)의 이름을 문자열로 반환하는 메소드
//System.out.println(joinPoint.getTarget().getClass().getName());
//=> xyz.itwill07.aop.JoinPointBean - 타겟메소드가 포함된 클래스가 누구인가?
//Class.getSimpleName() : Class 객체에 저장된 클래스(패키지미포함)의 이름을 문자열로 반환하는 메소드
//System.out.println(joinPoint.getTarget().getClass().getSimpleName());
//=> JoinPointBean
//JoinPoint.getSignature() : 타켓메소드의 정보가 저장된 Signature 객체를 반환하는 메소드
//Signature.getName() : 타켓메소드의 이름을 문자열로 반환하는 메소드
//System.out.println(joinPoint.getSignature().getName());
//=> add , modify, remove, getName, calc
//JoinPoint.getArgs() : 타켓메소드의 매개변수에 저장된 모든 값(객체)을 제공받아 Object
//객체 배열로 반환하는 메소드
//System.out.println(joinPoint.getArgs());
//=> [Ljava.lang.Object;@1de5f0ef : Object 객체 배열
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Object[] params = joinPoint.getArgs();
System.out.print("[before]"+className+" 클래스의 "+methodName+"(");
for(int i=0; i<params.length; i++) {
System.out.print(params[i]);
if(i<params.length-1) {
System.out.print(", ");
}
}
System.out.println(") 메소드 호출");
}
//2.
//After Advice 메소드 - 무조건 실행 후 삽입
//사용가능한 매개변수 : 없음 or JoinPoint
//After Advice 메소드
public void displayMessage(JoinPoint joinPoint) {
//System.out.println("[after]핵심관심코드 실행 후에 무조건 삽입되어 실행될 횡단관심코드");
Object[] params=joinPoint.getArgs();
System.out.println("[after]학번이 "+params[0]+"인 학생정보를 삭제 하였습니다.");
}
//3.
//After Returning Advice 메소드 - 정상 실행 후 삽입
//xml 파일에 반드시 [returning 속성값] 작성
//사용가능한 매개변수 : 없음 or JoinPoint or Object (반환값이 무조건 정해져있다면? String , int..가능)
// => After Returning Advice 메소드에는 JoinPoint 인터페이스의 매개변수 외에 Object 클래스의 매개변수 선언 가능
// => 스프링 컨테이너는 Object 클래스의 매개변수에 타겟메소드의 반환값이 저장되도록 전달
// => 타겟메소드에서 반환되는 값(객체)의 자료형이 고정되어 있는 경우 Object 클래스 대신 반환되는 값(객체)의 자료형으로 매개변수 작성 가능
// => Spring Bean Configuration File의 AOP 설정에서 after-returning 엘리먼트에 반드시 returning 속성을 사용하여 반환값을 저장할 매개변수의 이름을 속성값으로 지정
// => after-returning 엘리먼트에 returning 속성이 없거나 속성값이 잘못 설정된 경우 IllegalArgumentException 발생
public void displayName(Object object) {
//System.out.println("[after-returning]핵심관심코드가 정상적으로 실행된 후에 삽입되어 실행될 횡단관심코드");
//반환값이 Object 타입이라 형변환 필요 - 자식이 누구인지 명확하게 구분하기 위해!!
//instanceof 연산자를 사용하여 매개변수에 저장된 객체의 자료형을 구분하여 처리
if(object instanceof String) {
String name=(String)object;//명시적 객체 형변환
System.out.println("[after-returning]"+name+"님, 안녕하세요.");
}
}
//4.
//After Throwing Advice 메소드 - 예외발생된 경우 삽입
//xml 파일에 반드시 [throwing 속성값] 작성
//사용가능한 매개변수 : 없음 or JoinPoint or Exception (예외가 무조건 정해져있다면? ex)NullPointerExcpetion 가능
//하지만 프로그램에서 발생할 수 있는 예외는 다양하므로 그냥 부모객체인 Exception으로 작성하는 것이 대부분
// => After Throwing Advice 메소드에는 JoinPoint 인터페이스의 매개변수 외에 Exception 클래스의 매개변수 선언 가능
// => 스프링 컨테이너는 Exception 클래스의 매개변수에 타켓메소드의 명령 실행 시 발생된 예외(Exception 객체)가 저장되도록 전달
// => 타겟메소드에서 발생되는 예외가 고정되어 있는 경우 Exception 클래스 대신 자식클래스로 선언된 매개변수 작성 가능
// => Spring Bean Configuration File의 AOP 설정에서 after-returning 엘리먼트에 반드시 throwing 속성을 사용하여 Exception 객체를 저장할 매개변수의 이름을 속성값으로 지정
// => after-throwing 엘리먼트에 throwing 속성이 없거나 속성값이 잘못 설정된 경우 IllegalArgumentException 발생
public void exceptionHandle(JoinPoint joinPoint, Exception exception) {
System.out.println("[after-throwing]핵심관심코드 실행 시 예외가 발생된 경우 삽입되어 실행될 횡단관심코드");
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
System.out.println("[after-throwing]"+className+" 클래스의 "+methodName
+" 메소드에서 발생된 예외 = "+exception.getMessage());
}
//5.
//Around Advice 메소드 - 전,후 삽입
//만약 타겟메소드의 반환형이 없다면 굳이 반환형을 쓸 필요는 없지만, 일반적으로 Object클 반환하는 경우가 많긴함
//사용가능한 매개변수 : 없음 or ProceedingJoinPoint
//Throwable 예외가 발생하므로 **예외처리하거나 예외전달 필수 **
// => Around Advice 메소드는 반환형을 Object 클래스로 작성하고, 매개변수에 자료형은 ProceedingJoinPoint 인터페이스로 작성
// => 타켓메소드의 반환값을 제공받아 반환하기 위해 Object 클래스를 반환형으로 작성
// => 타겟메소드 관련 정보를 ProceedingJoinPoint 인터페이스의 매개변수로 제공받아 Around Advice 메소드에서 사용
// => ProceedingJoinPoint : 타겟메소드 관련 정보를 저장하기 위한 객체
// => JoinPoint 객체와 다른 점은 타겟메소드를 직접 호출하기 위한 메소드를 제공함
// => 그래야지 타겟메소드 호출 전에 실행이 가능헐 것임!!
public Object disply(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[around]핵심관심코드 실행 전 삽입되어 실행될 횡단관심코드");
//ProceedingJoinPoint.proceed() : 타겟메소드를 호출하는 메소드 - 핵심관심코드 실행
// => 타겟메소드를 호출하여 반환되는 결과값을 제공받아 저장
// => Throwable(Error 클래스와 Exception 클래스의 부모클래스) 객체(예외)가 발생되므로
//예외를 처리하거나 예외를 전달
Object object=joinPoint.proceed();
System.out.println("[around]핵심관심코드 실행 후 삽입되어 실행될 횡단관심코드");
return object;//타겟메소드를 호출하여 반환된 결과값을 메소드를 호출한 명령으로 반환
}
}
03. 💖Spring Bean Configuration File
[07-2_param.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.xsd>
<http://www.springframework.org/schema/aop> <http://www.springframework.org/schema/aop/spring-aop.xsd>">
<!-- [핵심관심모듈]로 선언된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.JoinPointBean" id="joinPointBean"/>
<!-- [횡단관심모듈]로 선언된 클래스를 Spring Bean으로 등록 -->
<bean class="xyz.itwill07.aop.JoinPointAdvice" id="joinPointAdvice"/>
<aop:config>
<aop:aspect ref="joinPointAdvice">
<!-- => [JoinPoint]: 타겟메소드 실행전 beforeDisplay 삽입, [pointCut]: 타겟메소드 = 모든 메소드 -->
<aop:before method="beforeDisplay" pointcut="execution(* *(..))"/>
<!-- => [JoinPoint]: 타겟메소드 무조건 실행후 displayMessage 삽입, [pointCut]: 타겟메소드 = remove 메소드 -->
<aop:after method="displayMessage" pointcut="execution(void remove(int))"/>
<!-- => [JoinPoint]: 타겟메소드 정상 실행후 displayName 삽입, [pointCut]: 타겟메소드 = getName 메소드, [returning]: 반환값 Object객체는 object 매개변수에 저장해주세요! -->
<!-- returning 속성 : 타겟메소드의 반환값을 전달받아 저장할 매개변수의 이름을 속성값으로 설정 -->
<aop:after-returning method="displayName" pointcut="execution(java.lang.String getName())" returning="object"/>
<!-- throwing 속성 : 타겟메소드의 Exception 객체를 전달받아 저장할 매개변수의 이름을 속성값으로 설정 -->
<!-- => [JoinPoint]: 타겟메소드에서 예외가 발생된 경우 exceptionHandle 삽입, [pointCut]: 타겟메소드 = calc 메소드 , [throwing]: 반환값 Exception객체는 exception 매개변수에 저장해주세요! -->
<aop:after-throwing method="exceptionHandle" pointcut="execution(void calc(int, int))" throwing="exception"/>
<aop:around method="disply" pointcut="execution(* modify(..))"/>
</aop:aspect>
</aop:config>
</beans>
04. 실행프로그램
JoinPointApp
package xyz.itwill07.aop;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JoinPointApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("07-2_param.xml");
JoinPointBean bean = context.getBean("joinPointBean",JoinPointBean.class);
System.out.println("======================================================");
bean.add();
System.out.println("======================================================");
bean.modify(1000, "홍길동");
System.out.println("======================================================");
bean.remove(2000);
System.out.println("======================================================");
bean.getName();
System.out.println("======================================================");
//bean.calc(20, 10);
bean.calc(20, 0); //어떤 수를 0으로 나누면 ArithmeticException 예외 발생
System.out.println("======================================================");
((ClassPathXmlApplicationContext)context).close();
}
}
반응형
'framework > spring AOP(관점지향)' 카테고리의 다른 글
[springAOP] 7. 스키마기반의 AOP 만들어 사용하기 | EmailSendApp프로그램 (0) | 2024.07.31 |
---|---|
[springAOP] 6. 스키마기반의 AOP 만들어 사용하기 | ExecutionTimeApp프로그램 (0) | 2024.07.31 |
[springAOP] 4. 스키마기반의 AOP 만들어 사용하기 | Advice클래스 (0) | 2024.07.30 |
[springAOP] 3. AOP 만들기 전 설정할 것 (0) | 2024.07.30 |
[springAOP] 2. AOP의 용어 | springAOP의 특징 및 생성법 (0) | 2024.07.30 |