m. AOP - kimxavi/spring_tutorial GitHub Wiki
์คํ๋ง ํ๋ ์์ํฌ์ ์ค์ํ ์์ ์ค์ ํ๋๊ฐ 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