-
Notifications
You must be signed in to change notification settings - Fork 0
m. AOP
스프링 프레임워크의 중요한 요소 중에 하나가 Aspect oriented programming (AOP) 관점 지향 프로그래밍 이다. AOP는 프로그램 로직을 관심사에 따라 별개의 부분으로 나눈다. 어플리케이션의 여러 곳에 걸쳐 있는 기능은 cross-cutting concerns 이라 불리어진다. 그리고 그 것들의 cross-cutting concerns은 비즈니스 로직으로부터 개념적으로 나누어진다. 로깅, 트랜잭션, 시큐리티, 캐시, 그외 등등 측면의 많은 예가 있다. OOP에서 모듈화의 핵심 단위는 클래스이다. 그에 반해, AOP의 모듈화 단위는 aspect이다. Dependency Injection은 객체를 서로로 부터 분리시켜주는 걸 돕는다 그리고 AOP는 그 객체들이 영향을 끼치는 것으로 부터 cross-cutting concerns 을 분리시켜준다. 스프링 AOP 모듈은 어플리케이션을 intercept하는 intercepter를 제공한다. 예를 들어, 메소드가 실행될 때 메소드 실행 전/후 추가 기능을 추가 할 수있다.
- Aspect : cross-cutting되는 요구를 제공하는 API 들의 집합 모듈. 예를 들어, 로깅 모듈은 로깅을 위한 AOP의 Aspect를 호출 할 것이다. 어플리케이션은 필요에 따라 aspect의 수에는 제한이 없습니다.
- Join point : 이것은 어플리케이션의 AOP aspect 를 플러긴 할 시점을 표현한다. 스프링 AOP 프레임워크를 사용하여 동작하는 실제 장소이다.
- Advice : 이는 실제 메소드 실행 전/후에 가져지는 실제 작업이다. 이 실제 코드 부분은 스프링 AOP 프레임워크에 의해 프로그램 실행 중에 호출된다.
- Pointcut : advice 가 실행되야하는 하나 이상의 Join point 집합이다. expressions 이나 patterns 을 사용해서 명시해준다.
- Introduction : Introduction는 새로운 메소드나 어트리뷰트를 이미 존재하는 클래스에 더해주는 것을 허락한다.
- Target object : 하나 이상의 aspect들에 의해 Advice 가 작동할 객체. 이 객체는 항상 proxied object 이다. 또한, advised object로서 언급된다.
- Weaving : Weaving은 다른 어플리케이션의 타입들과 aspect 들을 연결해주는 과정 또는 advised object를 만드는 객체이다. compile time, load time, 또는 runtime 에 마무리된다.
- before : 메소드 실행 전에 advice가 실행된다.
- after : 이 것의 결과에 상관 없이 메소드 실행 후에 advice가 실행된다.
- after-returning : 메소드가 성공적으로 실행 됐으면 메소드 실행 후에 advice가 실행된다.
- after-throwing : 메소드에 예외가 발생 했으면 메소드 실행 후에 advice가 실행된다.
- around : 메소드 호출 전후에 advice가 실행된다.
이 섹션에선 aop 네임스페이스 태그를 사용하여 표현한다. spring-aop를 아래와 같이 import해줘야 한다.
<?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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- bean definition & AOP specific configuration -->
</beans>
- Declaring an aspect : aop:aspect 요소를 사용하여 aspect는 선언된다. 그리고 bean은 ref를 사용해서 참조된다.
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
- Declaring a pointcut : 포인트 컷은 다른 advice들로 실행되는 관심의 조인 포인트 (즉, 방법)를 결정하는 데 도움이됩니다. XML 기반 설정 파일로 작동할 때, pointcut 아래와 같이 정의한다.
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
아래와 같이 Student의 getName() 메소드 실행에 정의할 수 있다.
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.tutorialspoint.Student.getName(..))"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
- Declaring advices : 다섯개의 advice들을 aop:aspect 내부에 aop:{ADVICE NAME}를 써서 선언했다.
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<!-- a before advice definition -->
<aop:before pointcut-ref="businessService"
method="doRequiredTask"/>
<!-- an after advice definition -->
<aop:after pointcut-ref="businessService"
method="doRequiredTask"/>
<!-- an after-returning advice definition -->
<!--The doRequiredTask method must have parameter named retVal -->
<aop:after-returning pointcut-ref="businessService"
returning="retVal"
method="doRequiredTask"/>
<!-- an after-throwing advice definition -->
<!--The doRequiredTask method must have parameter named ex -->
<aop:after-throwing pointcut-ref="businessService"
throwing="ex"
method="doRequiredTask"/>
<!-- an around advice definition -->
<aop:around pointcut-ref="businessService"
method="doRequiredTask"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
doRequiredTask 로 동일하게 할 수도 있고 다른 advice마다 다르게 메소드를 설정할 수 있다. 그 메소드들은 aspect 모듈의 한 부분으로 정의될 것이다.
- 예시
Logging.java
package com.tutorialspoint;
public class Logging {
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised.
*/
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
Student.java
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
MainApp.java
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
Beans.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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:config>
<aop:aspect id="log" ref="logging">
<aop:pointcut id="selectAll"
expression="execution(* com.tutorialspoint.*.*(..))"/>
<aop:before pointcut-ref="selectAll" method="beforeAdvice"/>
<aop:after pointcut-ref="selectAll" method="afterAdvice"/>
<aop:after-returning pointcut-ref="selectAll"
returning="retVal"
method="afterReturningAdvice"/>
<aop:after-throwing pointcut-ref="selectAll"
throwing="ex"
method="AfterThrowingAdvice"/>
</aop:aspect>
</aop:config>
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging"/>
</beans>
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content
@AspectJ 는 Java 5 어노테이션과 함께 자바 클래스의 일반적인 aspect 선언 방법이다. @AspectJ는 아래와 같이 XML 설정 파일에 요소를 넣음으로써 사용 가능하다.
<aop:aspectj-autoproxy/>
- Declaring an aspect : Aspects 클래스들은 @Aspect가 어노테이트 됐을 뿐 다른 클래스들과 똑같다.
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AspectModule {
}
XML 설정 파일엔
<bean id="myAspect" class="org.xyz.AspectModule">
<!-- configure properties of aspect here as normal -->
</bean>
- Declaring a pointcut : @AspectJ 일 때 두 가지 부분으로 나뉜다.
- pointcut expression 는 정확히 관심 있는 메소드를 결정한다.
- pointcut signature 는 이름과 몇몇의 인자로 구성된다. 실제 몸체는 상관 없다. 실제로 비어있다.
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression
private void businessService() {} // signature
import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.tutorialspoint.Student.getName(..))")
private void getname() {}
- Declaring advices : 다섯개의 advice를 @{ADVICE-NAME}를 사용해서 선언할 수 있다.
@Before("businessService()")
public void doBeforeTask(){
...
}
@After("businessService()")
public void doAfterTask(){
...
}
@AfterReturning(pointcut = "businessService()", returning="retVal")
public void doAfterReturnningTask(Object retVal){
// you can intercept retVal here.
...
}
@AfterThrowing(pointcut = "businessService()", throwing="ex")
public void doAfterThrowingTask(Exception ex){
// you can intercept thrown exception here.
...
}
@Around("businessService()")
public void doAroundTask(){
...
}
인라인 으로 설정할 수도 있다.
@Before("execution(* com.xyz.myapp.service.*.*(..))")
public doBeforeTask(){
...
}
- 예시 Logging.java
package com.tutorialspoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
@Aspect
public class Logging {
/** Following is the definition for a pointcut to select
* all the methods available. So advice will be called
* for all the methods.
*/
@Pointcut("execution(* com.tutorialspoint.*.*(..))")
private void selectAll(){}
/**
* This is the method which I would like to execute
* before a selected method execution.
*/
@Before("selectAll()")
public void beforeAdvice(){
System.out.println("Going to setup student profile.");
}
/**
* This is the method which I would like to execute
* after a selected method execution.
*/
@After("selectAll()")
public void afterAdvice(){
System.out.println("Student profile has been setup.");
}
/**
* This is the method which I would like to execute
* when any method returns.
*/
@AfterReturning(pointcut = "selectAll()", returning="retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("Returning:" + retVal.toString() );
}
/**
* This is the method which I would like to execute
* if there is an exception raised by any method.
*/
@AfterThrowing(pointcut = "selectAll()", throwing = "ex")
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("There has been an exception: " + ex.toString());
}
}
Student.java
package com.tutorialspoint;
public class Student {
private Integer age;
private String name;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
System.out.println("Age : " + age );
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
System.out.println("Name : " + name );
return name;
}
public void printThrowException(){
System.out.println("Exception raised");
throw new IllegalArgumentException();
}
}
MainApp.java
package com.tutorialspoint;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
Student student = (Student) context.getBean("student");
student.getName();
student.getAge();
student.printThrowException();
}
}
Beans.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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<aop:aspectj-autoproxy/>
<!-- Definition for student bean -->
<bean id="student" class="com.tutorialspoint.Student">
<property name="name" value="Zara" />
<property name="age" value="11"/>
</bean>
<!-- Definition for logging aspect -->
<bean id="logging" class="com.tutorialspoint.Logging"/>
</beans>
Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content