AOP 목적 - 횡단관심사와 이에 영향을 받는 객체간 결합도를 떨어뜨리는 것.
결합도와 응집도란?
모듈의 독립성을 판단하는 지표
결합도 : 모듈과 모듈간의 상호 의존 정도
응집도 : 모듈 내부의 기능적인 집중 정도
모듈화 : 소프트웨어를 각 기능별로 나누는 것.
횡단관심사 : 주 로직은 아닌데 어떤 특정 파트에서 필요한 것들 (Logging이나 Filter 같은 것들)


Aspect - 관점
: 부가기능을 정의한 코드인 Advice와 어드바이스를 어디에 적용할지를 결정하는 PointCut을 합친 개념
※ Advisor도 어드바이스 + 포인트컷. 같은 거라고 봐도 됨. 어드바이저는 Spring AOP에서만 사용됨.
로깅작업 그 자체 - Advice
package site.levinni.proxy;
HelloWorld.java
package site.levinni.proxy;
public interface HelloWorld {
String sayHello();
}
HelloWorldImpl.java
package site.levinni.proxy;
public class HelloWorldImpl implements HelloWorld{
@Override
public String sayHello() {
System.out.println("안녕 세상");
return "hello world";
}
}
HelloWorldHandler.java
package site.levinni.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class HelloWorldHandler implements InvocationHandler {
private Object target;
public HelloWorldHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke prev :: " + method.getName());
Object ret = method.invoke(target, args);
System.out.println("invoke next :: " + method.getName());
return ret;
}
}
HelloWorldApp.java
package site.levinni.proxy;
import java.lang.reflect.Proxy;
public class HelloWorldApp {
public static void main(String[] args) {
Class<?>[] arrClass = { HelloWorld.class };
HelloWorldHandler handler = new HelloWorldHandler(new HelloWorldImpl());
HelloWorld origin = new HelloWorldImpl();
HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
HelloWorld.class.getClassLoader(), arrClass, handler);
System.out.println("======= origin =======");
System.out.println(origin.sayHello());
System.out.println("======= proxy =======");
System.out.println(proxy.sayHello());
System.out.println(origin);
System.out.println(proxy);
// 1. 인터페이스를 통한 자동 프록시 객체 생성
// 2. 인터페이스가 없으면? 상속을 통한 자동 프록시 객체 생성
}
}
.class : 클래스 리터럴
package site.levinni.ally;
HelloWorld.java
package site.levinni.ally;
public class HelloWorld implements HelloWorldService{
public void sayHello() {
System.out.println("안녕");
}
}
MsgDeco.java
package site.levinni.ally;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MsgDeco implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object obj = invocation.proceed(); // 원래 해야 할 일 하는 거.
System.out.println("세상");
return obj;
}
}
HelloApp.java
package site.levinni.ally;
import org.springframework.aop.framework.ProxyFactory;
public class HelloApp {
public static void main(String[] args) {
HelloWorldService origin = new HelloWorld();
ProxyFactory pf = new ProxyFactory();
pf.addAdvice(new MsgDeco());
pf.setTarget(origin);
HelloWorldService proxy = (HelloWorld) pf.getProxy();
System.out.println("======= origin =======");
origin.sayHello();
System.out.println("======= proxy =======");
proxy.sayHello();
System.out.println(origin);
System.out.println(proxy); // 싱글턴이라 주소 같음
}
}

HelloWorldService
package site.levinni.ally;
public interface HelloWorldService {
void sayHello();
}
package site.levinni.smallmart;
SmallMart.java
package site.levinni.smallmart;
public interface SmallMart {
void getProducts(String productName) throws Exception;
}
SmallMartImpl.java
package site.levinni.smallmart;
public class SmallMartImpl implements SmallMart{
@Override
public void getProducts(String productName) throws Exception {
System.out.println(productName);
// throw new Exception("small mart 예외");
}
}
package site.levinni.smallmart.advice;
BeforeLoggingAdvice.java
package site.levinni.smallmart.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class BeforeLoggingAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before 처리함");
}
}
Around
package site.levinni.smallmart.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundLoggingAdvice implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("around before 처리함");
Object obj = invocation.proceed();
System.out.println("around after 처리함");
return obj;
}
}
After
package site.levinni.smallmart.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class AfterLoggingAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("After 처리함");
}
}
Throws
package site.levinni.smallmart.advice;
import org.springframework.aop.ThrowsAdvice;
public class ThrowsLoggingAdvice implements ThrowsAdvice{
public void afterThrowing(Throwable throwable) {
System.out.println("예외 발생함");
}
}
자동완성 안 되기 때문에 afterThrowing(Throwable 오타 주의!!
SmallMartApp.java
package site.levinni.smallmart;
import org.springframework.aop.framework.ProxyFactory;
import site.levinni.smallmart.advice.AfterLoggingAdvice;
import site.levinni.smallmart.advice.AroundLoggingAdvice;
import site.levinni.smallmart.advice.BeforeLoggingAdvice;
import site.levinni.smallmart.advice.ThrowsLoggingAdvice;
public class SmallMartApp {
public static void main(String[] args) {
// 대상 객체 생성
SmallMart target = new SmallMartImpl();
// 프록시팩토리 객체 생성
ProxyFactory factory = new ProxyFactory();
// 충고 추가
factory.addAdvice(new AfterLoggingAdvice());
factory.addAdvice(new AroundLoggingAdvice());
factory.addAdvice(new BeforeLoggingAdvice());
factory.addAdvice(new ThrowsLoggingAdvice());
// 대상 설정
factory.setTarget(target);
// 프록시 객체 생성 (SmallMart 타입)
SmallMart proxy = (SmallMart) factory.getProxy();
try {
System.out.println("===proxy===");
proxy.getProducts("칫솔");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

충고 추가 부분 내가 쓴 대로 출력 되는데, after는 가장 마지막에 나옴.
SmallMartImpl.java의 주석을 해제하면 아래 두 줄이 안 나오고 "예외 발생함"이 나옴.
Pointcut

pointcut interface

package site.levinni.aop1;
First.java, Second.java
package site.levinni.aop1;
public class First {
public void one() {
System.out.println("First.one()");
}
public void two() {
System.out.println("First.two()");
}
}
SimpleStaticPointCut.java
package site.levinni.aop1;
import java.lang.reflect.Method;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
public class SimpleStaticPointCut extends StaticMethodMatcherPointcut{
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("one") && targetClass == First.class;
}
}
Aop1App.java
package site.levinni.aop1;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import site.levinni.smallmart.advice.AroundLoggingAdvice;
public class Aop1App {
public static void main(String[] args) {
First first = new First();
Second second = new Second();
System.out.println("===origin===");
first.one();
first.two();
second.one();
second.two();
// 어드바이저 객체 생성
Advisor advisor = new DefaultPointcutAdvisor(new SimpleStaticPointCut(), new AroundLoggingAdvice());
// 프록시 팩토리 객체 생성
ProxyFactory pf = new ProxyFactory();
// 어드바이저 추가
pf.addAdvisor(advisor);
// 타겟 설정
pf.setTarget(first);
First proxy1 = (First) pf.getProxy();
// 프록시 팩토리 객체 생성
pf = new ProxyFactory();
// 어드바이저 추가
pf.addAdvisor(advisor);
// 타겟 설정
pf.setTarget(second);
Second proxy2 = (Second) pf.getProxy();
System.out.println("===proxy===");
proxy1.one();
proxy1.two();
proxy2.one();
proxy2.two();
}
}


package site.levinni.smallmart2;
SmallMart.java
package site.levinni.smallmart2;
public interface SmallMart {
public void getProducts(String productName) throws Exception;
public void getProducts2(String productName) throws Exception;
}
SmallMartImpl.java
package site.levinni.smallmart2;
public class SmallMartImpl implements SmallMart{
@Override
public void getProducts(String productName) throws Exception {
System.out.println(productName + ":: getProducts()");
}
@Override
public void getProducts2(String productName) throws Exception {
System.out.println(productName + ":: getProducts2()");
}
}
SmallMartApp.java
package site.levinni.smallmart2;
import java.lang.reflect.Method;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import site.levinni.aop1.SimpleStaticPointCut;
import site.levinni.smallmart.advice.AfterLoggingAdvice;
import site.levinni.smallmart.advice.AroundLoggingAdvice;
public class SmallMartApp {
public static void main(String[] args) {
SmallMart target = new SmallMartImpl();
// 프록시 팩토리 객체 생성
ProxyFactory factory = new ProxyFactory();
// 어드바이저 객체 생성
Advisor advisor = new DefaultPointcutAdvisor(new SimpleStaticPointCut(), new AroundLoggingAdvice());
// 충고 추가
factory.addAdvisor(new DefaultPointcutAdvisor(new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// TODO Auto-generated method stub
return method.getName().endsWith("2");
}
}, new AfterLoggingAdvice()));
factory.addAdvice(new AfterLoggingAdvice());
// 대상 설정
factory.setTarget(target);
SmallMart proxy = (SmallMart) factory.getProxy();
try{
System.out.println("===proxy===");
proxy.getProducts2("칫솔2");
} catch (Exception e) {
// TODO: handle exception
}
}
}

AspectJ pointcut
package site.levinni.smallmart3;
smallmart3.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-4.2.xsd">
<bean class="site.levinni.smallmart3.SmallMartImpl" id="smallMart"/>
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxy">
<property name="interceptorNames">
<list>
<value>pointcutAdvisor</value>
</list>
</property>
<property name="target" ref="smallMart"></property>
</bean>
<bean class="site.levinni.smallmart.advice.BeforeLoggingAdvice" id="before"/>
<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut" id="pc">
<property name="expression">
<value>execution(* *.getProducts(..))</value>
</property>
</bean>
<bean class="org.springframework.aop.support.DefaultPointcutAdvisor" id="pointcutAdvisor">
<property name="advice" ref="before"/>
<property name="pointcut" ref="pc"/>
</bean>
<aop:config>
<aop:advisor advice-ref="before" pointcut-ref="pc" id="myAdv"/>
</aop:config>
</beans>
SmallMartApp에서 new가 다섯 개 였으니깐 bean도 다섯 개 생성.
SmallMartApp.java
package site.levinni.smallmart3;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SmallMartApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("smallmart3.xml");
SmallMart proxy = ctx.getBean("proxy", SmallMart.class);
try{
System.out.println("===proxy===");
proxy.getProducts("칫솔");
proxy.getProducts2("치실2");
} catch (Exception e) {
// TODO: handle exception
}
}
}

aspectj rt와 aspectj weaver 라이브러리 메이븐에서 가져와서 추가.
Annotation을 활용한 포인트 컷
site.levinni.aop2
First.java
package site.levinni.aop2;
public class First {
@AdviceRequired
public void hello() {
System.out.println("First.hello()");
}
public void sayHello() {
System.out.println("First.sayHello()");
}
}
AdviceRequired.java
package site.levinni.aop2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AdviceRequired {
}
Retention 지속, 유효
RetentionPolicy의 타입 : Enum >상수 값이 저장된 하나의 클래스로 보면 됨.
Aop2App.java ( 안 됨)
package site.levinni.aop2;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import site.levinni.smallmart.advice.AroundLoggingAdvice;
public class Aop2App {
public static void main(String[] args) {
First first = new First();
System.out.println("===origin===");
first.hello();
first.sayHello();
// 어드바이저 객체 생성
AnnotationMatchingPointcut pc = new AnnotationMatchingPointcut(AdviceRequired.class);
Advisor advisor = new DefaultPointcutAdvisor(pc, new AroundLoggingAdvice());
// 프록시 팩토리 객체 생성
ProxyFactory pf = new ProxyFactory();
// 어드바이저 추가
pf.addAdvisor(advisor);
// 타겟 설정
pf.setTarget(first);
First proxy1 = (First) pf.getProxy();
// 프록시 팩토리 객체 생성
pf = new ProxyFactory();
// 어드바이저 추가
pf.addAdvisor(advisor);
// 타겟 설정
System.out.println("===proxy===");
proxy1.hello();
proxy1.sayHello();
}
}

책 445
'스프링 Spring' 카테고리의 다른 글
21. 04. 01. (0) | 2021.04.01 |
---|---|
Spring JDBC 21. 03. 31. (0) | 2021.03.31 |
AOP 끝 21. 03. 31. (0) | 2021.03.31 |
IoC 21. 03. 29. (0) | 2021.03.29 |
Spring IoC 및 DI 21. 03. 26. (0) | 2021.03.26 |