열심히 끝까지

디바이스 융합 자바(Java) day67(1) - 바인드 변수, joinPoint 본문

디바이스 융합 자바(Java)기반 풀스택 개발자 양성과정(수업내용)

디바이스 융합 자바(Java) day67(1) - 바인드 변수, joinPoint

노유림 2022. 9. 16. 11:52

[ 지난 수업 ]
Spring 프레임워크
  : 유지보수를 위해 사용
     - IoC와 AOP를 지원하는 경량의 프레임워크  덕분
     >  제어의 역행(IoC)
                  : 의존관계
                       - 어떤 객체가 특성메서드를 수행할 때에 다른 객체를 사용하는 것(멤버변수)
                           ex ) 캐릭터 - 무기, 폰 - 워치, C(Service) - DAO, ....
                  : 의존성주입(DI)
                       - new 결합도를 높히는 코드
                       - 결합도를 낮추기 위해 컨테이너에게 부탁함
                        .xml                                        @
                        <bean>                                  @Component
                          생성자인젝션                          @Autowired
                          setter인젝션

    >  관점지향 프로그래밍
              우선 두 개로 분리
                  - CRUD(비즈니스메서드, 핵심로직)
                  - 횡단관심(공통로직)
                          : 어떤 핵심로직 + 횡단관심을 엮을지를 스프링 컨테이너에게 알려주어야 함!
                          : .xml, @으로 설정 가능
       ※ Advice
                 : 횡단관심

<bean id="logAdvice" class="com.ryo.biz.common.LogAdvice" />

       ※ pointcut 
                 : 횡단관심과 엮일 핵심로직

<aop:pointcut expression="execution(* com.ryo.biz..*Impl.*(..))" id="aPointcut"/>

       ※ aspect
                 : 횡단관심 + 포인트컷 의 "결합"을 의미
                 : 시점이 필요 -> before, after, around, ...

<aop:aspect ref="logAdvice">
		<aop:before method="printLog" pointcut-ref="aPointcut"/>	
</aop:aspect>

-------------------------------------------------------------------------------------------
[오늘 수업]
[ JoinPoint | 바인드 변수 ]

   - 바인딩
        ex)
          상위 추상클래스 포켓몬
              : 추상 메서드 정의 가능
          하위 클래스 피카츄 / 바이리 / 꼬부기
              : 추상메서드를 반드시 재정의해야함(강제)

      이때, 포켓몬 배열 생성  
      포켓몬[] datas={new 피카츄(), ...};
      datas[0] hello(); = 피카피카
    >> 이 것을 바인딩 되었다고 함
       >> 상위 클래스의 메서드가 호출되는 것이 아니라,
            하위 클래스의 메서드가 호출되는 현상 
             == 동적 바인딩 => 다형성 실현 예시
                   " 현재 메서드를 수행하는 실제 [주체]가 누구인지"
                                                             -> 객체

-----------------------------
>> 횡단관심이 좀 더 의미를 가지려면, 
     현재 수행중인 비즈니스 메서드가 무엇인지 파악하는 것이 좋음

  ※ joinPoint(조인포인트)
         : 수행중인 포인트컷
            ex ) AroundAdvice에 pjp가 인자이기 때문에
                  현재 진행중인 비즈니스 메서드를 파악할 수 있고
                  돌려주는 것이 있기 때문에 Object라는 반환값을 가짐
         : joinpoint를 인자로 가지게 되면(pjp처럼) 현재 수행중인 비즈니스 메서드의 시그니처 등을 알 수 있음
             >> 비즈니스 메서드 시그니처 ex ) CRUD 인자, Output 등

 

ex ) -----------------------------------------------------

-----------------LogAdvice.java

package com.ryo.biz.common;

import org.aspectj.lang.JoinPoint;

public class LogAdvice {
	public void printLog(JoinPoint jp) {
		// 인자로 joinpoint 제공(=aspectj 통해 import)
		
		// output == signature
		// getName() == 현재 수행중인 포인트컷(핵심로직, CRUD)의 메서드명
		String methodName=jp.getSignature().getName();
		
		// output == object  어떤 것이든 다 뱉음
		Object[] args=jp.getArgs();
		
		System.out.println("수행중인 핵심메서드명 : " + methodName);
		
		System.out.println("사용하는 인자");
		// 오버라이딩 + 동적바인딩
		for(Object v : args) {
			System.out.println(v);
		}
		
		// System.out.println(">>> 공통로직 <<<     핵심로직 수행 전에 호출됨");
	}
}

-----------------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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

	<context:component-scan base-package="com.ryo.biz" />
	
	<bean id="logAdvice" class="com.ryo.biz.common.LogAdvice" />
    
	<aop:config>
		<aop:pointcut expression="execution(* com.ryo.biz..*Impl.*(..))" id="aPointcut"/>
        
		<aop:aspect ref="logAdvice">
			<aop:before method="printLog" pointcut-ref="aPointcut"/>	
		</aop:aspect>
        
	</aop:config>
    
</beans>

--------------------결과

>> ++++++++++뒤로 인자 뜨고 ===========로 종료

 

>> 다른 예시

 

-----------------AfterReturningAdvice.java(값을 돌려주는 advice)

package com.ryo.biz.common;

import org.aspectj.lang.JoinPoint;

import com.ryo.biz.member.MemberVO;

public class AfterReturningAdvice {
	public void printLogAfterReturning(JoinPoint jp, Object returnObj) {
		String methodName=jp.getSignature().getName();
		Object[] args=jp.getArgs();
		
		System.out.println("수행중인 핵심 메서드 명 : " + methodName);
		System.out.println("사용하는 인자");
		System.out.println("++++");
		for(Object v : args) {
			System.out.println(v);
		}
		System.out.println("====");
		
		if(returnObj instanceof MemberVO) {
			MemberVO mvo = (MemberVO)returnObj;
			if(mvo.getRole().equals("ADMIN")) {
				System.out.println("관리자입니다.");
			}
			else {
				System.out.println("일반계정입니다.");
			}
		}
		System.out.println("핵심메서드의 반환값 : " + returnObj);
	}
}

-----------------AfterThrowingAdvice.java(예외시 던지는 advice)

package com.ryo.biz.common;

import org.aspectj.lang.JoinPoint;

public class AfterThrowingAdvice {
	public void printLogAfterThrowing(JoinPoint jp, Exception exceptObj) {
		String methodName=jp.getSignature().getName();
		Object[] args=jp.getArgs();
		
		System.out.println("수행중인 핵심 메서드 명 : " + methodName);
		System.out.println("사용하는 인자");
		System.out.println("++++");
		for(Object v : args) {
			System.out.println(v);
		}
		System.out.println("====");
		
		// 약식(이런 형태로 사용 가능)
		System.out.println("발생한 예외" + exceptObj.getMessage());
		if(exceptObj instanceof IllegalArgumentException) {
			System.out.println("올바르지 않은 인자값을 사용했습니다...");
		}
		else if(exceptObj instanceof NumberFormatException) {
			System.out.println("숫자 형식이 아닌 값을 사용했습니다...");
		}
		else if(exceptObj instanceof Exception) {
			System.out.println("예외가 발생했습니다...");
		}
		else {
			System.out.println("확인되지 않은 에러가 발생했습니다!!!");
		}
	}
}

-----------------AroundAdvice.java(시간 측정 advice)

package com.ryo.biz.common;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

// around로 사용할 advice는 반드시 pjp를 input으로 가져야한다!!!
public class AroundAdvice { // 본인이 핵심메서드를 수행하게 해야 함
	public Object printLogAround(ProceedingJoinPoint pjp) throws Throwable {
		String methodName=pjp.getSignature().getName();
		System.out.println("수행중인 핵심 메서드 명 : " + methodName);
		
		StopWatch sw = new StopWatch();
		// 시간을 측정하는 메서드 == spring 제공
		
		sw.start();
		Object returnObj=pjp.proceed(); // 수행해야할 포인트컷
		sw.stop();
		
		System.out.println("수행시간 : " + sw.getTotalTimeMillis()+"ms");
		
		return returnObj;
	}
}

-----------------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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

	<context:component-scan base-package="com.ryo.biz" />
	<bean id="ara" class="com.ryo.biz.common.AfterReturningAdvice" />
	<bean id="ata" class="com.ryo.biz.common.AfterThrowingAdvice" />
	<bean id="aa" class="com.ryo.biz.common.AroundAdvice" />
	<!-- 
	<bean id="logAdvice" class="com.ryo.biz.common.LogAdvice" />
	<bean id="logSelectAdvice" class="com.ryo.biz.common.LogSelectAdvice" /> 
	<bean id="aroundAdvice" class="com.ryo.biz.common.AroundAdvice" />
	 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.ryo.biz..*Impl.*(..))" id="aPointcut"/>  
		<aop:pointcut expression="execution(* com.ryo.biz..*Impl.select*(..))" id="bPointcut"/>
		<!-- 
		<aop:pointcut expression="execution(* com.ryo.biz..*Impl.select*(..))" id="bPointcut"/>
		<aop:pointcut expression="execution(* com.ryo.biz..*Impl.*(..))" id="cPointcut"/>
		 -->
		
		<aop:aspect ref="aa">
			<aop:around method="printLogAround" pointcut-ref="aPointcut"/>
		</aop:aspect>
		<aop:aspect ref="ara">
			<aop:after-returning method="printLogAfterReturning" pointcut-ref="bPointcut" returning="returnObj"/>
		</aop:aspect>
		<aop:aspect ref="ata">
			<aop:after-throwing method="printLogAfterThrowing" pointcut-ref="bPointcut" throwing="exceptObj"/>
		</aop:aspect>
		<!-- 
		<aop:aspect ref="logAdvice">
			<aop:before method="printLog" pointcut-ref="aPointcut"/>	
		</aop:aspect>
		
		<aop:aspect ref="logSelectAdvice">
			<aop:before method="printLogSelect" pointcut-ref="bPointcut"/>
		</aop:aspect>
		 
		<aop:aspect ref="aroundAdvice">
			<aop:around method="printLogAround" pointcut-ref="cPointcut"/>
		</aop:aspect>
		 -->
	</aop:config>
	
	
</beans>

-----------MemberService.java

package com.ryo.biz.member.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ryo.biz.member.MemberService;
import com.ryo.biz.member.MemberVO;

@Service("memberService")
public class MemberServiceImpl implements MemberService {

	@Autowired // MemberDAO 타입
	private MemberDAO memberDAO; // 핵심로직을 수행할 객체
	
	@Override
	public void insertMember(MemberVO vo) {
		memberDAO.insertMember(vo);
	}

	@Override
	public MemberVO selectOneMember(MemberVO vo) {
		if(vo.getMid().equals("timo")) {
	         throw new IllegalArgumentException("[실행시예외]");
	    }
		return memberDAO.selectOneMember(vo);
	}

	@Override
	public void updateMember(MemberVO vo) {
		memberDAO.updateMember(vo);
	}

	@Override
	public void deleteMember(MemberVO vo) {
		memberDAO.deleteMember(vo);
	}
	
}

 

>> 일부러 예외를 발생시키기 위해 memberservice에 예외 처리

 

---------------------결과

timo로 예외발생
admin 로그인 시 관리자라고 띄움