관리 메뉴

민우의 코딩노트

스프링으로 AOP 구현해보기 - 4가지 구현 관점 본문

Knowledge/Spring

스프링으로 AOP 구현해보기 - 4가지 구현 관점

미미누 2022. 3. 29. 09:08

스프링에서는 4가지 보조 업무가 존재한다.

Before Advice: 앞에만 필요한 경우

After returnning Advice: 뒤에만 필요한 경우

After throwing Advice: 예외를 처리하는 경우

Around Advice: 앞뒤 둘다 필요한 경우

 

 

<자바 코드>

public class Program {
	
    public static void main(string[] args) {
    	Exam exam = new NewlecExam(1,1,1,1);
        
        Exam proxy = (Exam)Proxy.newProxyInstance(newlecExam.class,
        			new Class[] {Exam.class},
                    new InvocationHandler() {
                    
                    	@Override
                        public Object invoke(Object proxy, Method method, Object[] args) {
                        	long start = System.curretTimeMillis();
        
        					// method는 실제 업무를 실행할 수 있는 메서드 호출 가능
                            // invoke를 사용하여 실제 업무 객체를 넣어준다 (exam)
                            // 두 번째 인자 args : exam안에 여러 메서드를 넣을 수 있음.
                            // invoke는 Object 형으로, 모든 형태를 반환 가능
                            Object result = method.invoke(exam, args);
        					long end = System.curretTimeMillis();
        
        					String message = (end-start) + "ms";
        					System.out.println(message);
                            
                            return result;
                        
                        
                        }
        			
        
        System.out.printf("total is %d\n", proxy.total());
        System.out.printf("total is %d\n", proxy.avg());
    }

}

 

 

xml 방식으로 구현해보기

...

<bean id="target" class="spring.di.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>
<bean id="logAroundAdvice" class="spring.aop.LogAroundAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="target" />
    <property name="interceptorNames">
        <list>
            <value>logAroundAdvice</value>
        </list>
    </property>
</bean>

setting.xml

 

1. 4개의 값을 초기화하는 작업

2. 프락시라는 객체 생성

프락시에는 2가지 코드가 있어야함. 

실제 target에 해당하는 classloader, 곁다리 업무

-> 프로퍼티로 설정

참조면 ref, value 값인 경우 val로 설정

NewlecExam이 참조이니까 ref에 target을 설정

핸들러를 넣어줘야 함 (interceptorNames를 setter로 해서 설정)

 

list의 경우는 참조열이기 때문에 value는 참조하는 영역에 이름을 넣어줘야 함

 

LogAroundAdvice.class

 

MethodInterceptor 인터페이스를 구현해야 함.

public class LogAroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();

        Object result = invocation.proceed();

        long end = System.currentTimeMillis();
        String message = (end - start) + "ms 시간이 걸렸습니다.";
        System.out.println(message);

        return result;
    }
}

 

Program.class

 

public class Program {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring/aop/setting.xml");

        Exam proxy = (Exam) context.getBean("proxy");

        System.out.printf("total is %d\n", proxy.total());
        System.out.println("avg is %d\n " + proxy.avg());

    }
}

 

Program에서는 프락시가 필요함. 

getBean을 통해서 프락시 값을 꺼내와야 함.

 

BeforeAdvice

...

<bean id="target" class="spring.di.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1"/>
<bean id="logAroundAdvice" class="spring.aop.LogAroundAdvice" />
<bean id="logBeforeAdvice" class="spring.aop.LogBeforeAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="target" />
    <property name="interceptorNames">
        <list>
            <value>logAroundAdvice</value>
            <value>logBeforeAdvice</value>
        </list>
    </property>
</bean>
public class LogBeforeAdvice implements MethodBeforeAdvice
{
    @Override
    // 현재 호출되고 있는 함수의 이름, 파라미터 얻고 싶으면 method, args 이용
    // arget에 대한 객체를 얻고 싶다면 target 설정
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("LogBeforeAdvice.before");
    }
}

-> before가 출력되고, target에 대한 메서드가 실행된다.

 

AfterReturning

afterReturning의 경우, 함수가 호출된 이후 반환값을 갖게 되면 returnValue 사용

public class LogAfterReturningAdvice implements AfterReturningAdvice
{
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
        System.out.println("returnvalue:" + returnValue + "method:"+method.getName());
    }
}

-> target의 메서드가 실행되고 returnvalue 출력

 

 

예외가 발생했을때 무엇을 실행시키고 싶은 경우 AfterThrowingAdvice

public class LogAfterThrowingAdvice implements ThrowAdvice
{
    public void afterThrowing(IllegalArgumentException e) throws Throwable {
        System.out.println("예외가 발생하였습니다.: " + e.getMessage());
    }
}

구현해야 하는 afterThrowing 메서드에는 감지할 특정 예외를 넣어주면 된다.

 

target인 newLecExam.class의 total() 메서드의 특정 예외가 있는 경우

...
// newLecExam.class

@Override
public int total() {
	
    int result = kor+eng+math+com;
    
    if(kor > 100)
    	throw new IllegalArgumentException("유효하지 않은 국어점수");
        
  ...

 

proxy.total()을 실행시키면 예외가 발생하고, 예외 발생 시점에 메시지를 출력가능