Spring AspectJ 이용
AOP를 실현하는 것으로, Java에서는 “AspectJ"라는 라이브러리가 널리 사용되고 있다. Spring AOP에서이 AspectJ를 이용한 AOP 처리에 대해 설명한다.
AspectJ와 pom.xml 수정
AOP에 대해 조사해 보면, 아마 “AspectJ"라는 소프트웨어에 대해 많이 찾게 될것이다. AspectJ는 Java AOP 소프트웨어의 사실상의 표준이라고 해도 될 정도로 널리 사용되고 있는 소프트웨어이다.
Spring AOP에도 이 AspectJ를 이용하여 AOP 구현을 위한 기능이 포함되어 있다. 마지막으로 사용한 Spring AOP와는 또 다른 형태로 AOP를 구현할 수 있기에, 이쪽의 사용법에 대해서도 배워보도록 하자.
그럼 먼저 AspectJ를 이용하기 위한 준비를 하자. pom.xml을 열고 <dependencies>
태그 안에 아래와 같이 내용을 추가하자 (이미 기술되어 있는 spring-core와 spring-aop 태그는 삭제하지 않아야 한다!).
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
예제로, 여기에서는 Spring Framework 4.1.7에 맞는 버전을 지정하고 있으므로 다른 버전을 사용하는 경우는 거기에 아울러 <version>
을 조정하도록 하자.
여기에는 2개의 라이브러리를 추가한다. “AspectJ RT"는 AspectJ 런타임 프로그램이다. 이 라이브러리는 추가하면 AspectJ의 기능을 사용할 수 있게 된다. 또한 “AspectJ Weaver"는 aspect의 정보를 바탕으로 aspect를 구성한 코드를 생성하는데 필요한 유틸리티 프로그램이다. Spring AOP에서 AspectJ를 사용을 하려면, 이 두 가지를 세트로 준비해야 한다.
Aspect 클래스 생성
Aspect으로 삽입하는 처리되는 클래스를 만들어 보자. 이번에는 AspectJ를 이용하기 때문에 이전과는 다른 형태이다.
아래 코드가 작성한 예제이다. com.devkuma.spring.aop 패키지에 “SampleAspect"라는 클래스 이름으로 코드를 작성한다.
package com.devkuma.spring.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class SampleAspect {
@Before("execution(* com.devkuma.spring.aop.SampleAopBean.*(..))")
public void before() {
System.out.println("before:");
}
@After("execution(* com.devkuma.spring.aop.SampleAopBean.*(..))")
public void after() {
System.out.println("after:");
}
}
보면 알 수 있듯이, 이것은 매우 일반적인 POJO 클래스이다. 어떤 인터페이스도 구현하지 않고, 어떤 클래스도 상속받지 않는다. 다른 것은 어노테이션이 부여되었다는 것뿐이다.
@Aspect
이 클래스가 Aspect 클래스 임을 나타낸다. AspectJ에서 사용하는 클래스는 이 어노테이션을 부여한다.
@Before
이 어노테이션은 메소드의 앞에 넣는다. 메소드의 실행 전에 삽입하는 처리임을 나타내는 어노테이션이다. Spring AOP에 있는 “before"과 유사한 것으로 생각해도 된다.
@After
이 어노테이션은 메소드의 앞에 넣는다. 이것은 메소드의 실행 후에 삽입되는 처리인 것을 나타내는 어노테이션이다. Spring AOP에 있는 “afterRunning"에 해당하는 것으로 생각하면 된다.
어노테이션 자체는 간단하다. 알기 어려운 것은 @Before와 @After 뒤의 괄호()안에 기술된 내용이다. 이것은 다음과 같이 기술이 되어 있다.
("execution (...... 할당되는 메소드의 지정 ...)")
execution 후의 괄호()에 어떤 메소드에 이 메소드 삽입 할 것인지를 지정한다. 이것은 패키지 이름부터 제대로 정확하게 메소드를 지정해야 한다. 단, 모든 이름을 다 기술해야 한다는 뜻은 아니다.
여기에는 와일드 카드(*)를 사용할 수 있기 때문에, 그것을 이용하여 특정 패키지와 클래스의 모든 메소드 등을 지정할 수 있다. 또한 지정하는 메소드의 인수도 “..” 기호로 불특정 인수를 지정할 수 있다.
여기에 작성된 것을 보면, 다음과 같이 되어 있다.
* com.devkuma.spring.aop.SampleAopBean.*(..)
알기 쉽게 말해, “불특정 값"을 XX라고 기술한다면, 이런 식으로 적혀있는 것을 알 수 있다.
XX com.devkuma.spring.aop.SampleAopBean.XX(XX)
맨 처음에있는 XX는 public이나 private 같은 것이 지정된 경우를 생각하여 붙여 있다. 그리고 SampleAopBean의 후에 붙은 XX는 이 클래스 내에 있는 어떤 방법도 모두 대상으로 지정하는 것이다. 또, (XX)는 인수가 어떤 형태여도 해당되도록 하기 위한 것이다.
이 execution 작성 방법을 어느 정도 알게 된다면, 원하는대로 AOP 처리 대상이 되는 메소드를 지정할 수 있게 될 것이다.
aopbean.xml 작성
이어서 Bean 설정 파일을 준비하다. 이번에는 “aopbean.xml"라는 파일을 새로 추가하도록 하자.
“resources"폴더에 “aopbean.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.xsd">
<bean id="sampleAspect" class="com.devkuma.spring.aop.SampleAspect" />
<bean id="sampleAopBean" class="com.devkuma.spring.aop.SampleAopBean">
<property name="message" value="this is AOP bean!" />
</bean>
<aop:aspectj-autoproxy />
</beans>
이번에는 지금까지 사용해 온 bean.xml에 비해 여러가지 기술해야 하는 것이 많다. 내용에 대해서 아래와 같이 정리를 해보자.
<beans>
태그의 속성
먼저 <beans>
태그 부분을 보자. 다음 속성이 추가되어 있다.
xmlns : aop = "http://www.springframework.org/schema/aop"
그리고, 이 스키마 위치(schema location)를 나타내는 값이 xsi:schemaLocation
값에 추가되어 있다. 구체적으로는 아래와 같은 부분이다.
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
이것은 설정 파일에서 사용되고 있는 <aop:aspectj-autoproxy/>를 사용할 수 있도록 하기 위한 것이다. 다소 길지만, 반드시 추가 기입해야 한다.
Bean 등록
<bean id = "sampleAspect"class = "comdevkuma.spring.aop.SampleAspect"/>
<bean id = "sampleAopBean"class = "com.devkuma.spring.aop.SampleAopBean">
<property name = "message"value = "this is AOP bean!"/>
</bean>
이것은 이미 여러 번 작성해 봤었다. Bean 등록 태그이다. SampleAspect 클래스와 SampleAopBean 클래스를 각각 Bean으로 등록한다.
AspectJ 자동 프록시
<aop:aspectj-autoproxy/>
이것은 AspectJ를 위한 태그이며, 먼저 Spring AOP 때 이용한 ProxyFactoryBean에 해당하는 것을 자동으로 생성하는 태그이다. 이를 기술하게 되면 ProxyFactoryBean으로 준비된 기능이 자동으로 포함된다.
프로그램 실행
이것으로 AspectJ를 사용할 준비가 되었다. 그럼 Bean을 이용하여 보자. com.devkuma.spring.aop 패키지의 App 클래스 (전에 사용 했던 거)을 아래와 같이 수정한다.
package com.devkuma.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("aopbean.xml");
SampleAopBean bean = (SampleAopBean) app.getBean("sampleAopBean");
String msg = bean.getMessage();
bean.setMessage("<<" + msg + ">>");
bean.printMessage();
}
}
실행 결과:
before :
after :
before :
after :
before :
message : [<< this is AOP bean! >>
after :
여기에서는 getMessage로 메시지를 얻어서, 그것을 바탕으로 setMessage 메시지를 설정하고, 그것으로 부터 printMessage 내용을 출력한다. 각각의 메소드를 호출마다 그 전과 후에 before / after가 출력되는 것을 볼 수 있다.
이전에 Spring AOP와 결정적인 차이는 getBean으로 얻어 실행하는 클래스이다. Spring AOP에서는 ProxyFactoryBean를 통해서 수정된 SampleAopBean를 얻을 수 있게 되어 있었다.
이번에 getBean으로 얻은 것은 SampleAopBean이다. 즉, SampleAopBean 클래스 자체가 변경된 메소드가 삽입되어 있는 것이다.
동작을 확인했다면 aopbean.xml을 열고 <aop : aspectj-autoproxy/> 태그를 삭제하자. 그리고 실행하면 이번에는 “message:[<< this is AOP bean! >>]“만 표시된다. before / after에 삽입된 것 메소드가 사라진 것을 알 수 있다.
이처럼 AspectJ를 사용하면 자동 프록시의 ON / OFF만으로 화면 처리의 삽입과 제거가 간단히 할 수 있다. 사용하는 Bean을 프록시 Bean에서 원래대로 되돌릴 수는 없다.
Bean 설정 클래스 이용
이제 이것으로 AspectJ의 기본적인 이용 방법은 알았다. Spring Framework에서는 Bean은 설정 파일이 아닌 클래스를 사용하여 정의 할 수 있었다. AspectJ에도 정의 클래스로 설정을 해 보자.
com.devkuma.spring.aop 패키지에 “SampleAspectConfig"라는 클래스를 작성한다. 그리고 아래와 같이 코드를 작성하자.
package com.devkuma.spring.aop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class SampleAspectConfig {
@Bean
SampleAopBean sampleAopBean() {
return new SampleAopBean("this is AspectJ bean.");
}
@Bean
SampleAspect sampleAspect() {
return new SampleAspect();
}
}
이것이 AspectJ 설정 클래스이다. 작성한 후 App.java을 열고 다음 문장을 수정한다.
App 수정 전
ApplicationContext app =
new ClassPathXmlApplicationContext ( "aopbean.xml");
App 수정 후
ApplicationContext app =
new AnnotationConfigApplicationContext (SampleAspectConfig.class);
이제 실행하면 이전과 마찬가지로 AspectJ를 이용한 방법의 삽입이 이루어 메소드 호출 전후에 처리가 실행되게 된다. 이 클래스에는 설정 클래스임을 나타내는 @Configuration 외에,
@EnableAspectJAutoProxy
이러한 어노테이션이 클래스에 추가되어 있다. 이것은 Bean 정의 파일에 넣었던 <aop:aspectj-autoproxy/> 태그에 해당하는 것이다. 이것을 기술하는 것으로 AspectJ의 자동 프록시 기능이 ON이 되고, 자동으로 Aspect 클래스의 메소드 삽입이 이루어지게 된다.
Bean의 정의는 지금까지와 변함이 없다. 다만, @EnableAspectJAutoProxy를 추가하는 것만으로 AspectJ의 기능이 ON이 된다는 점이다. 그래도 설정 클래스으로 변경을 한다고 해도 AspectJ 이용의 간단함은 변함이 없다는 것을 알 수 있다.