Life is Good

수콩이의 시선

Coding/SPRING

[Spring] AOP / 공통 클래스(Aspect), 공통 함수(Advice), 핵심코드(JoinPoint)=Part.2

Soocong 2022. 4. 7. 18:02

Advice 동작시점

동작시점 설명
Before 메소드 실행 전에 동작
After 메소드 실행 후에 동작
After-returning 메소드가 정상적으로 실행된 후에 동작
After-throwing 예외가 발생한 후에 동작
Around 메소드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작

 


예제 실습

 

[interface] Person.java

package com.java.aop03;

public interface Person {
	public void work();
}

 

Student.java

package com.java.aop03;

public class Student implements Person {

	@Override
	public void work() {
		System.out.println("학생은 공부를 합니다.");
	}
}

 

Teacher.java

package com.java.aop03;

public class Teacher implements Person {	//핵심클래스

	@Override
	public void work() {	//핵심 함수
		System.out.println("선생님은 강의를 합니다.");

	}
}

 

PAspect.java

package com.java.aop03;

import org.aspectj.lang.JoinPoint;

public class PAspect { // 공통클래스(aspect)
	public void open(JoinPoint joinPoint) { //공통함수(advice), 핵심코드(JoinPoint)
		System.out.println("수업시작 -> 강의장에 들어온다.");
	}
	
	public void close(JoinPoint joinPoint) {
		System.out.println("수업끝 -> 강의장을 나간다.");
	}
}

 

appCTX.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-3.1.xsd">

	<!-- 핵심클래스 -->
	<bean id="teacher" class="com.java.aop03.Teacher"/>
	<bean id="student" class="com.java.aop03.Student"/>
	
	<!-- 공통클래스 -->
	<bean id="pAspect" class="com.java.aop03.PAspect"/>
	
	<!-- AOP -->
	<aop:config>
		<aop:aspect id="aspect" ref="pAspect">
			<aop:pointcut expression="within(com.java.aop03.*)" id="pMethod"/>
			
			<aop:before method="open" pointcut-ref="pMethod"/>
			<aop:after method="close" pointcut-ref="pMethod"/>
		</aop:aspect>
	</aop:config>
</beans>

 

MainClass.java

package com.java.aop03;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:com/java/aop03/appCTX.xml");
		Person student = (Person) ctx.getBean("student");
		student.work();
		System.out.println();
		
		Person teacher = (Person) ctx.getBean("teacher");
		teacher.work();
		System.out.println();
	}
}

 

실행결과


에러 발생 시키기

 

 

PAspect.java

package com.java.aop03;

import org.aspectj.lang.JoinPoint;

public class PAspect { // 공통클래스(aspect)
	//핵심전
	public void open(JoinPoint joinPoint) { //공통함수(advice), 핵심코드(JoinPoint)
		System.out.println("수업시작 -> 강의장에 들어온다.");
	}
	//핵심후
	public void close(JoinPoint joinPoint) {
		System.out.println("수업끝 -> 강의장을 나간다.");
	}
	//에러
	public void error(JoinPoint joinPoint) {
		System.out.println("떠들어 강의에서 쫓겨남..흡 식권도없음!");
	}
	//정상
	public void eat(JoinPoint joinPoint) {
		System.out.println("점심식사를 맛나게 함!!");
	}
}

 

appCTX.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-3.1.xsd">

	<!-- 핵심클래스 -->
	<bean id="teacher" class="com.java.aop03.Teacher"/>
	<bean id="student" class="com.java.aop03.Student"/>
	
	<!-- 공통클래스 -->
	<bean id="pAspect" class="com.java.aop03.PAspect"/>
	
	<!-- AOP -->
	<aop:config>
		<aop:aspect id="aspect" ref="pAspect">
			<aop:pointcut expression="within(com.java.aop03.*)" id="pMethod"/>
			
			<aop:before method="open" pointcut-ref="pMethod"/>
			<aop:after method="close" pointcut-ref="pMethod"/>
			
			<aop:after-returning method="eat" pointcut-ref="pMethod"/>
			<aop:after-throwing method="error" pointcut-ref="pMethod"/>
		</aop:aspect>
	</aop:config>
</beans>

 

Student.java

package com.java.aop03;

import java.util.Scanner;

public class Student implements Person {

	@Override
	public void work() {
		System.out.println("학생은 공부를 합니다.");
		
		Scanner sc = new Scanner(System.in);
		
		System.out.println("수입력: ");
		int su = sc.nextInt();
		
		if(su==5) System.out.println(su/0);
		
		sc.close();
	}
}

 

 

 

+ 다른 방법

 

PAspect.java

package com.java.aop04;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class PAspect { // 공통클래스(aspect)
	
	public void sub(ProceedingJoinPoint joinPoint) {
		try {
			//핵심함수전
			System.out.println("수업시작 -> 강의장에 들어온다.");
			
			//핵심코드
			joinPoint.proceed();
			
			//정상
			System.out.println("점심식사를 맛나게 함!!");
		} catch (Throwable e) {
			//에러
			System.out.println("떠들어 강의에서 쫓겨남..흡 식권도없음!");
		} finally {
			//핵심함수 후
			System.out.println("수업끝 -> 강의장을 나간다.");
		}
	}
}

appCTX.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-3.1.xsd">

	<!-- 핵심클래스 -->
	<bean id="teacher" class="com.java.aop04.Teacher"/>
	<bean id="student" class="com.java.aop04.Student"/>
	
	<!-- 공통클래스 -->
	<bean id="pAspect" class="com.java.aop04.PAspect"/>
	
	<!-- AOP -->
	<aop:config>
		<aop:aspect id="aspect" ref="pAspect">
			<aop:pointcut expression="within(com.java.aop04.*)" id="pMethod"/>
			<aop:around method="sub" pointcut-ref="pMethod"/>
		</aop:aspect>
	</aop:config>
</beans>

MainClass.java

package com.java.aop04;

import org.springframework.context.support.GenericXmlApplicationContext;

public class MainClass {

	public static void main(String[] args) {
		GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("classpath:com/java/aop04/appCTX.xml");
		Person student = (Person) ctx.getBean("student");
		student.work();
		System.out.println();
		
		Person teacher = (Person) ctx.getBean("teacher");
		teacher.work();
		System.out.println();
	}
}

위의 방식과 동일한 결과창

 


스프링 @AOP 실습

스프링 AOP를 사용하여 성능 측정해보기

Annotation을 활용하여 성능 측정을 해보자. 성능을 측정하고 싶은 메소드에 annotation을 붙여서 콘솔에 성능이 출력되도록 할 것이다.

LogExecutionTime.java

    package org.springframework.samples.petclinic.owner;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LogExecutionTime {
    }

메소드에 LogExecutionTime이라는 annotation을 추가하자. @Target은 어디에 쓸 수있는지 메소드에게 알려주는 annotation이고, @Retention은 annotation의 정보를 언제까지 유지할 것인지를 알려주는 annotation이다.

이렇게 만들어 놓고 추가한다고 실행하면 동작하느냐? 아니다. 그냥 만들기만 하면 주석과 다름없다. 이러한 annotation을 읽어서 처리하는 Aspect를 만들도록 하자.

LogAspect.java

    package org.springframework.samples.petclinic.owner;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;    
    import org.springframework.stereotype.Component;
    import org.springframework.util.StopWatch;

    @Component
    @Aspect
    public class LogAspect {

        Logger logger = LoggerFactory.getLogger(LogAspect.class);

        @Around("@annotation(LogExecutionTime)")
        public Object logExecutionTime(ProceedingJoinPoint JoinPoint) throws Throwable {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();

            Object proceed = joinPoint.proceed();

            stopWatch.stop();
            logger.info(stopWatch.prettyPrint());

            return proceed;
        }
    }

빈으로 등록하기 위해서 @Component라는 annotation을 붙여주고, @Aspect임을 알려준다. slf4j로 Logger를 만들고, @Around 이하의 코드는 어드바이스라고 하는데, @Around라는 annotation으로 joinPoint라는 파라미터를 받을 수 있다. 이 파라미터는 타겟, 여기서는 @LogExecutionTime이 붙어있는 메소드를 말한다.

이 Aspect는 프록시 패턴 기반으로 동작하는 AOP이다.