Dev/Spring

AOP 기능 사용하기 예제

surimi🍥 2020. 12. 28. 23:59
반응형

Chapter_03.zip
7.02MB

 

 

 

/Chapter_03/src/acQuickStart.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"
	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-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<bean id="messageBeanImpl" class="sample01.MessageBeanImpl">
		<property name="str" value="Have a nice day!!"/>
	</bean>
	
	<bean id="loggingAdvice" class="sample01.LoggingAdvice"></bean>
	
	<!-- AOP -->
	<aop:config>
		<aop:pointcut 
		expression="execution(public void *.*.*Before(..))" 
		id="beforeMethod"/>
		
		<aop:pointcut 
		expression="execution(public * *.*.*After(..))" 
		id="afterMethod"/><!-- public * *.*.* = public void등 *(패키지).*(클래스).*After(메소드 이름이 뭘로 시작하든 After로 끝나는 메소드) (..)파라미터 상관 없다. -->
		
		<aop:pointcut 
		expression="execution(public * *.*.*Print*(..))" 
		id="aroundMethod"/>
		
		<aop:aspect ref="loggingAdvice">
			<aop:before method="beforeTrace" pointcut-ref="beforeMethod"/>
			<aop:after method="afterTrace" pointcut-ref="afterMethod"/>
			<aop:around method="aroundTrace" pointcut-ref="aroundMethod"/>
		</aop:aspect>
	</aop:config>

</beans>

aop 

	<!-- AOP -->
	<aop:config>
		<aop:pointcut 
		expression="execution(public void *.*.*Before(..))" 
		id="beforeMethod"/>
		
		<aop:pointcut 
		expression="execution(public * *.*.*After(..))" 
		id="afterMethod"/><!-- public * *.*.* = public void등 *(패키지).*(클래스).*After(메소드 이름이 뭘로 시작하든 After로 끝나는 메소드) (..)파라미터 상관 없다. -->
		
		<aop:pointcut 
		expression="execution(public * *.*.*Print*(..))" 
		id="aroundMethod"/>
		
		<aop:aspect ref="loggingAdvice">
			<aop:before method="beforeTrace" pointcut-ref="beforeMethod"/>
			<aop:after method="afterTrace" pointcut-ref="afterMethod"/>
			<aop:around method="aroundTrace" pointcut-ref="aroundMethod"/>
		</aop:aspect>
	</aop:config>

expression

지정해둔 메소드의 조건 (public (void) (패키지).(클래스).(메소드(파라미터)) )에 맞는 메소드가 실행되면

지정해둔 id를 pointcut-ref로 참조하는 aop가 작동한다.

* =  와일드카드
*Before는 이름이 ~before로 끝나는 메소드를 말한다. printBefore(), viewBefore 등..
*Print*는 viewPrintMethod() showPrint2() 등~

aop:before, after, around

pointcut-ref로 참조하던 id의 pointcut이 작동되었을 경우,

before, after는 작동 트리거가 된 메소드각각 앞과 뒤,

around는 앞뒤 모두 

원하는 메소드가 실행되도록 할 수 있다.

 

aop가 동작하는 메소드 조건을 이용해

여러 페이지에 똑같이 작동하는 코드들을 한꺼번에 처리할 수 있다.

 

 

# /Chapter_03/src/sample01/LoggingAdvice.java

더보기
package sample01;

import java.text.DecimalFormat;

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

//공통관심사항, 공통모듈, 부가기능, Aspect
public class LoggingAdvice {
	public void beforeTrace() {
		System.out.println("before trace 작동");
	}

	public void afterTrace() {
		System.out.println("after trace 작동");
	}

	public void aroundTrace(ProceedingJoinPoint joinPoint) throws Throwable {
		// 공통
		System.out.println("핵심코드 작동 전");
		String methodName = joinPoint.getSignature().toShortString();
		System.out.println("메소드 = " + methodName);

		StopWatch sw = new StopWatch();
		sw.start(methodName);

		// 핵심 코드 호출
		joinPoint.proceed();

		// 공통
		System.out.println("핵심코드 작동 후");
		sw.stop();
		System.out.println("처리시간 : " + new DecimalFormat("#ms").format(sw.getTotalTimeMillis()));
	}
}

 

aop 순서

# 어노테이션 이용하기

/Chapter_03/src/acQuickStart.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"
	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-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<context:component-scan base-package="sample01"></context:component-scan>
	
	<aop:aspectj-autoproxy/> 

</beans>

component-scan

지정 패키지("sample01")에 @Component가 달린 클래스를 bean으로 생성한다.

 

<aop:aspectj-autoproxy/> 

@Aspect 가 달린 클래스를 찾아 pointcut시 작동시킬 메소드들을 읽어온다.

 

# /Chapter_03Chapter03_SpringMaven/src/sample01/LoggingAdvice.java

@Aspect // 공통모듈이라고 xml에 알림
@Component
public class LoggingAdvice {
	
	@Before("execution(public void *.*.*Before(..))")
	public void beforeTrace() {
		System.out.println("before trace...");
	}
	
	@After("execution(public * *.*.*After(..))" )
	public void afterTrace() {
		System.out.println("after trace...33");
	}
	
	@Around("execution(public * *.*.*Print*(..))")
	public void aroundTrace(ProceedingJoinPoint joinPoint) throws Throwable {
		//공통
		String methodName = joinPoint.getSignature().toShortString();
		System.out.println("메소드 = "+ methodName);
		
		StopWatch sw = new StopWatch();
		sw.start(methodName);
        
		// 핵심 관심사항 호출
		Object ob = joinPoint.proceed();
		System.out.println("# 핵심 리턴 : " + ob);
		
		// 공통
		sw.stop();
		System.out.println("처리시간 : " + new DecimalFormat("#,###ms").format( sw.getTotalTimeMillis()));
	}
}

@Before(..), After(..), Around(..)

execution(메소드 조건)은 xml에 적었던 코드와 같다.

 

aroundTrace(ProceedingJoinPoint joinPoint)

에서 파라미터로 받는 joinPoint는 execution(메소드 조건)에 걸려 들어오는 메소드이다.

 

출력시키면, 

joinPoint = execution(String sample01.MessageBean.showPrint2())
joinPoint.getSignature() = String sample01.MessageBean.showPrint2()
joinPoint.getSignature().toShortString() = MessageBean.showPrint2()

이렇게 메소드 경로가 나온다.

 

Stopwatch 클래스

시간 재는 기능

 

 

# SpringConfiguration 생성해서 하기

 

 

/Chapter_03/src/acQuickStart.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"
	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-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<context:component-scan base-package="sample01"></context:component-scan>
	<context:component-scan base-package="spring.conf"></context:component-scan>

</beans>

+ spring.conf 패키지를 읽어오게 해서 SpringConfiguration.java의 설정을 적용시킨다.

 

 

src/spring/conf/SpringConfiguration.java

package spring.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import sample01.LoggingAdvice;
import sample01.MessageBeanImpl;

@Configuration // 해당 클래스를 xml에서 읽어들일때 설정파일로써 인식하게 한다.
@EnableAspectJAutoProxy
public class SpringConfiguration {

	// @Bean 어노테이션을 달아야 리턴되는 객체를 bean으로 인식해 getBean메소드로 호출이 가능하다.
	// 메소드명(getSungJukDTO)을 객체명으로 인식하므로 @Bean에서 name설정을 하지 않으면 호출할때 context.getBean("getSungJukDTO"); 이렇게 불러야 한다. 
	
	@Bean(name = "messageBeanImpl")
	public MessageBeanImpl getMessageBeanImpl() {
		return new MessageBeanImpl();
	}
	
	@Bean(name = "loggingAdvice")
	public LoggingAdvice getLoggingAdvice() {
		return new LoggingAdvice();
	}
	
}

@EnableAspectJAutoProxy

xml의 <aop:aspectj-autoproxy/>를 어노테이션으로 붙이는 방법이다.

xml에서 이 태그는 제거한다.

 

# @Component를 @ComponentScan으로 바꿔 직접 spring.conf 패키지 안의 설정파일을 읽어오게 한다.

 

/Chapter_03Chapter03_SpringMaven/src/sample01/LoggingAdvice.java

package sample01;

import java.text.DecimalFormat;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.util.StopWatch;

//공통관심사항, 공통모듈, 부가기능, Aspect
@Aspect // 공통모듈이라고 xml에 알림
@ComponentScan("spring.conf")
public class LoggingAdvice {
	
	@Before("execution(public void *.*.*Before(..))")
	public void beforeTrace() {
		System.out.println("before trace...");
	}
	
	@After("execution(public * *.*.*After(..))" )
	public void afterTrace() {
		System.out.println("after trace...33");
	}
	
	@Around("execution(public * *.*.*Print*(..))")
	public void aroundTrace(ProceedingJoinPoint joinPoint) throws Throwable {
		//공통
		String methodName = joinPoint.getSignature().toShortString();
		System.out.println("메소드 = "+ methodName);
		System.out.println("joinPoint = "+ joinPoint);
		System.out.println("joinPoint.getSignature() = "+ joinPoint.getSignature());
		System.out.println("joinPoint.getSignature().toShortString() = "+ joinPoint.getSignature().toShortString());
		
		StopWatch sw = new StopWatch();
		sw.start(methodName);
		System.out.println("앞");
		// 핵심 관심사항 호출
		Object ob = joinPoint.proceed();
		System.out.println("# 핵심 리턴 : " + ob);
		System.out.println("뒤");
		
		// 공통
		sw.stop();
		System.out.println("처리시간 : " + new DecimalFormat("#,###ms").format( sw.getTotalTimeMillis()));
	}
}

 

 

/Chapter03_SpringMaven/src/sample01/MessageBeanImpl.java

package sample01;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;

//핵심관심사항, Target
//@Component

@ComponentScan("spring.conf")
public class MessageBeanImpl implements MessageBean {
	private String str;

	// autowire로 자동으로 setter메소드가 호출되도록
	@Autowired
	public void setStr(@Value("Have a nice day!!") String str) {
		this.str = str;
	}

	@Override
	public void showPrintBefore() {
		System.out.println("showPrintBefore 메시지 = "+str);
	}

	@Override
	public void viewPrintBefore() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("viewPrintBefore 메시지 = "+str);
		
	}
	
	@Override
	public void showPrintAfter() {
		System.out.println("showPrintAfter 메시지 = "+str);
	}

	@Override
	public void viewPrintAfter() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("viewPrintAfter 메시지 = "+str);
		
	}

	@Override
	public void display() {
		System.out.println("display 메시지 = "+str);
	}

	@Override
	public String showPrint2() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("showPrint 메시지! = "+str);
		
		return "## 리턴메세지 스프링";
	}

	@Override
	public void viewPrint2() {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("viewPrint 메시지!! = "+str);		
	}

}




 

 

# Proxy 클래스 이용 1

 

/Chapter_03/src/sample02_Java/HelloSpring.java

package sample02_Java;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HelloSpring {

	public static void main(String[] args) {
		
	 	MessageBean messageBean = new MessageBeanImpl();
	 	
	 	MessageBean proxy = (MessageBean) Proxy.newProxyInstance(
	 			MessageBeanImpl.class.getClassLoader(), 
	 			new Class[] {MessageBean.class}, 
	 			new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						// 공통
						System.out.println("입실 체크"); 
						
						//핵심코드 호출
						Object ob = method.invoke(messageBean, args);
						
						// 공통
						System.out.println("퇴실 체크"); 
						
						return ob;
					}
				});
			
	 	proxy.study(); // 호출
	 	System.out.println("-----------------");
	 	System.out.println("결과  : " + proxy.game());
	}

}

 

public static Object newProxyInstance(

ClassLoader loader,

Class<?>[] interfaces,

InvocationHandler h

) throws IllegalArgumentException

 

package sample02_Java;

public interface MessageBean {
	public void study();

	public String game();
}

///////////////////////////////////////////////////////////////////


package sample02_Java;

public class MessageBeanImpl implements MessageBean {

	@Override
	public void study() {
		System.out.println("수업시간 공부");
	}

	@Override
	public String game() {
		System.out.println("수업시간  몰래 게임");
		return "바부팅이";
	}
}

 

# Proxy 클래스 이용 2

 

/Chapter_03/src/applicationContext03.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:context="http://www.springframework.org/schema/context"
	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.3.xsd">
		
		
	<bean id="messageBeanImpl" class="sample03_Advice.MessageBeanImpl"></bean>
	
	<bean id="loggingBeforeAdvice" class="sample03_Advice.LoggingBeforeAdvice"></bean>
	<bean id="loggingAfterAdvice" class="sample03_Advice.LoggingAfterAdvice"></bean>
	<bean id="Arounding" class="sample03_Advice.LoggingAroundAdvice"></bean>
   
   <!-- ProxyFactoryBean 클래스로 aop기능을 대신한다.
   		target은 aop를 적용할 클래스
   		interceptorNames에는 before, After, Around할 클래스를 불러온다.
   	-->
   	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
   		<property name="target" ref="messageBeanImpl"></property>
   		<property name="interceptorNames">
   			<list>
   				<value>loggingBeforeAdvice</value>
   				<value>loggingAfterAdvice</value>
   				<value>Arounding</value>
   			</list>
   		</property>
   	</bean>
</beans>

 

org.springframework.aop.framework.ProxyFactoryBean 클래스를 bean 생성하며,

 클래스 안에 들어있는 target setter에는 aop를 적용할 bean( "messageBeamImpl" ),

 interceptorNames의 setter에는 Before, After, Around로 사용할 bean을 불러온다.

 

 

/Chapter_03/src/sample03_Advice/LoggingAroundAdvice.java

package sample03_Advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LoggingAroundAdvice implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		//핵심 호출
		System.out.println("# 어라운드 입실체크 start");
		long start = System.currentTimeMillis();
		
		Object ob = invocation.proceed(); // 핵심코드
		
		System.out.println("# 어라운드 입실체크 end");
		long end = System.currentTimeMillis();
		
//		System.out.println("start : " + start);
//		System.out.println("end : " + end);
		System.out.println("걸린 시간 : " + (long)(end - start));
		
		return ob;
	}

	
}

 

/Chapter_03/src/sample03_Advice/MessageBeanImpl.java

 

package sample03_Advice;

public class MessageBeanImpl implements MessageBean {

	@Override
	public void study() {
		System.out.println("수업시간에 공부한다");		
	}

	@Override
	public String game() {
		System.out.println("수업시간에 몰래 게임한다");
		return "바부팅이";
	}

}

 

/Chapter_03/src/sample03_Advice/HelloSpring.java

package sample03_Advice;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloSpring {
	
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext03.xml");
		MessageBean messageBean = (MessageBean) context.getBean("proxy");
		messageBean.study();
		
		System.out.println("===================");
		
		String str = messageBean.game();
		System.out.println("결과 = " + str);
	}

}

 

 

# Proxy 클래스 이용 3

/Chapter_03/src/sample04_Advisor/HelloSpring.java

 

/Chapter_03/src/applicationContext04.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:context="http://www.springframework.org/schema/context"
	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.3.xsd">

	<bean id="messageBeanImpl" class="sample04_Advisor.MessageBeanImpl"></bean>
	
	<bean id="loggingBeforeAdvice" class="sample04_Advisor.LoggingBeforeAdvice"></bean>
	<bean id="loggingAfterAdvice" class="sample04_Advisor.LoggingAfterAdvice"></bean>
	
	<bean id="beforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
		<property name="advice" ref="loggingBeforeAdvice"></property>
		<property name="mappedNames">
			<list>
				<value>study</value>
				<value>lesson</value>
			</list>
		</property>
	</bean>
	
	<bean id="afterAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
		<property name="advice" ref="loggingAfterAdvice"></property>
		<property name="mappedNames" value="study, game"></property>
	</bean>
	
	<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="messageBeanImpl"></property>
		<property name="interceptorNames">
			<list>
				<value>beforeAdvisor</value>
				<value>afterAdvisor</value>
			</list>
		</property>
	</bean>

</beans>

NameMatchMethodPointcutAdvisor 

org.springframework.aop.support.NameMatchMethodPointcutAdvisor 클래스

 

NameMatchMethodPointcutAdvisor또한 aop 기능을 사용할 수 있는 클래스이다.

advice의 setter로 참조할 클래스를 받아오고

mappedNames의 setter로 적용할 메소드의 리스트를 담는다.

그리고 ProxyFactoryBean 클래스를 bean 생성해

target과 작동시킬 NameMatchMethodPointcutAdvisor bean id 리스트를 담는다.

 


beforeAdvisor bean은 실행시킬 loggingBeforeAdvice bean을 참조하고

실행 조건으로 studylesson 메소드를 담아둔다.

studylesson 메소드는 proxy bean의 target으로 참조하는 클래스에서 찾는다.

 

afterAdvisor bean 처럼 mappedNames에 리스트 대신 value를 써서 ,콤마로 여러 메소드를 넣어 똑같이 동작시킬 수 있다.


 

/Chapter_03/src/sample04_Advisor/MessageBeanImpl.java

package sample04_Advisor;

public class MessageBeanImpl implements MessageBean {

	@Override
	public void study() {
		System.out.println("수업시간에 공부한다");		
	}

	@Override
	public String game() {
		System.out.println("수업시간에 몰래 게임한다");
		return "바부팅이";
	}

	@Override
	public void lesson() {
		System.out.println("컴퓨터 수업을 듣는다");
	}

}

 

/Chapter_03/src/sample04_Advisor/HelloSpring.java

package sample04_Advisor;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloSpring {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext04.xml");
		MessageBean messageBean = (MessageBean) context.getBean("proxy");
		messageBean.study();
		System.out.println("------------");
		System.out.println("결과 = " + messageBean.game());
		System.out.println("------------");
		messageBean.lesson();
	}
}

 

After, Before

package sample04_Advisor;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class LoggingAfterAdvice implements AfterReturningAdvice {

	@Override
	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
		System.out.println("퇴실 체크");
		
	}

}


////////////////////////////////////////////////////////////////////////////////////////////

package sample04_Advisor;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class LoggingBeforeAdvice implements MethodBeforeAdvice {

	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
		System.out.println("입실 체크");
		
	}

}

 

 

반응형

'Dev > Spring' 카테고리의 다른 글

스프링 MVC with @Annotation  (0) 2021.01.14
스프링 MVC + Maven + Tomcat 프로젝트 생성  (0) 2021.01.12
스프링 MVC 기본구조 정리  (0) 2021.01.10
[ 스프링 연습 ] txt파일로 Export  (0) 2020.12.27
STS3 정리  (0) 2020.12.22