본문 바로가기

스프링 Spring

AOP 21. 03. 30.

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

if문의 조건식 같은 느낌

 

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();
	}
}

 

proxy의 First.hello()에 Around 충고가 걸려야 하는데 안 걸림.

 

책 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