티스토리 뷰

개발/java

[Spring] AOP 예제 프로젝트

드림&T 2012. 8. 3. 00:40

프로그램을 짜다보면 흘러가는 로직 속에 부분 부분 포함되는 공통 로직에 대한 고민은 누구나(?) 하게 된다. 어떻게 더 깔끔하게 보일 수 있을까.. 비지니스 로직만 똑 떼어내고 싶은데.. 이런 고민들을 깊게 생각했던 사람들이 훨씬 스프링의 AOP 의 필요성을 알고 잘 사용할 수 있을지도 모르겠다.

 

[Spring] Spring Tool Suite(STS) 설치 및 예제 프로젝트 생성

[Spring] STS 예제 프로젝트에 기본으로 있는 파일들에 대해서

 

지난 포스팅때 만들었던 예제 프로젝트를 계속 이어서 사용한다. AOP 라는 말을 딱 꼬집어 설명하긴 어렵지만 공통 모듈을 여러 코드에 쉽게 적용할 수 있는 하나의 수단이라고 보면 쉽지 않을까 한다. AOP 관련 용어가 꽤나 좀 많은데 헷갈리고 한번에 눈에 들어오는 구조는 아닌거 같다. AOP 를 구현할 순서를 정해보자. 예전에도 말했다시피 스프링에서는 이방법말고도 여러 방법으로 구현할 수 있다.

 

Advice 클래스 구현.

Advisor 선언.

DefaultAdvisorAutoProxyCreator 선언.

 

말이 다 생소하고 어렵다. 하나씩 구현해보자.

 

1. Advice 클래스를 만들자.(com.spring.sample 패키지에 생성하자) Advice는 지정한 메소드가 실행되기 전이나 후에 공통적으로 해야하는 행동들을 정의하는 곳이라 생각하면 된다. 여러가지 중에 가장 기능이 많은 MethodInterceptor 인터페이스를 사용해보겠다.

package com.spring.sample;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;

@Component
public class ExampleAdvice implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("start : "+invocation.getMethod().getName());
		Object obj = invocation.proceed();
		System.out.println("end : "+invocation.getMethod().getName());
		return obj;
	}
}

MethodInvocation 객체에는 실행되는 객체의 메서드 정보가 다 담겨있다. 단순하게 메소드 호출 전과 후에 print 를 한줄씩 해보자. 평소에 꽤나 해보고 싶었던게 아닌가... -_-... 아닌가..

 

2. Advisor 를 선언하자. Advisor는 Pointcut 과 Advice 를 합쳐놓은 것을 말한다. 우리가 앞서 Advice 를 선언한건 특정 메소드를 호출할 때 호출하기전과 후에 로그를 한줄씩 넣겠다는 공통 모듈을 구현한 것이다. 이제 해야할 것은 저 공통모듈을 어떤 메소드를 호출할 때 사용할 것인지에 대한 기준을 선언해야 한다. 말이 점점 꼬여간다.. 공통모듈을 구현해놓았지만 프로젝트 안에 있는 모든 메소드들을 호출할 때마다 적용하고 싶진 않을 것이다. 그 제한의 기준을 선언하는게 중요하다. 이것도 스프링에서는 여러가지 방법을 제시하는데.. 나는 AspectJExpressionPointcutAdvisor 을 이용해서 하겠다. 굳이 Pointcut을 따로 선언하지 않고 한번에 Advisor를 선언할 수 있어서 심플하다. 그리고 property 중에 execution 의 표현식이 따로 있는데.. 그건 나중에 정리하는게 나을 듯하다. 우선은 아래 같이 선언하자.

<bean id="exampleAdvisor" 
	class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
	<property name="advice" ref="exampleAdvice" />
	<property name="expression" 
		value="execution(public * com.spring.sample.Service.getMessage(..))" />
</bean>

위의 소스를 app-context.xml 에 추가하자. 그리고 아직 뭐 한건 별거 없지만 테스트 코드를 실행해보자.

블로그를 따라 예제 프로젝트를 따라오신 분이라면 에러가 분명히 난다. 안나면 그냥 말고.. -_-

 

java.lang.IllegalStateException: Failed to load ApplicationContext
 at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:308)
 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
 at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
 at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
 at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exampleAdvisor' defined in class path resource [META-INF/spring/app-context.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/BCException
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:965)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:911)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
 at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
 at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84)
 at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
 at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:280)
 at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:304)
 ... 24 more
Caused by: org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/BCException
 at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:141)
 at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:74)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:958)
 ... 38 more
Caused by: java.lang.NoClassDefFoundError: org/aspectj/weaver/BCException
 at org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor.<init>(AspectJExpressionPointcutAdvisor.java:30)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:126)
 ... 40 more
Caused by: java.lang.ClassNotFoundException: org.aspectj.weaver.BCException
 at java.net.URLClassLoader$1.run(Unknown Source)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(Unknown Source)
 at java.lang.ClassLoader.loadClass(Unknown Source)
 at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
 at java.lang.ClassLoader.loadClass(Unknown Source)
 ... 46 more

 

우리가 몬 큰잘못을 한듯하다. 잘되던게 갑자기 안될 때 우린 무지 당황하지만.. 에러를 잘 읽어보자.

 

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exampleAdvisor' defined in class path resource [META-INF/spring/app-context.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/BCException

 

대략 내용을 읽어보자니.. 설정파일을 읽다가 실패했다. 특히 exampleAdvisor의 Bean을 만들다가 에러가 났다는 것이다. 근데 그것은 org/aspectj/weaver/BCException 클래스를 찾을 수 없기 때문에 발생한 것이다. 어라..? 우리에게 없는게 있다.. 내가 쓰는 방법이 AspectJ 관련 jar 가 필요했던 모양이다. Maven 으로 찾아서 다운받자. org.aspectj 로 찾아보면 aspectjweaver 가 있다 그걸 받자. 그리고나서 테스트 코드 실행하면 에러가 나지 않는다.



 

 

3. 마지막으로 DefaultAdvisorAutoProxyCreator 선언이 남았다. 원래는 AOP를 사용하려면 ProxyFactoryBean 클래스를 생성해서 정의 해야한다. 하지만 AOP의 대상객체가 늘어나게 되면 Proxy 객체선언으로 복잡해질것이다. 그래서 굳이 ProxyFactoryBean을 생성하지 않아도 자동으로 AOP 를 사용할 수 있도록 기능을 지원한다. app-context.xml 설정파일에 아래 한줄만 추가하자.

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

자 간단한(?) AOP 설정은 모두 끝이다.





반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함