www.youtube.com/watch?v=y2JkXjOocZ4&list=PLq8wAnVUcTFUHYMzoV2RoFoY2HDTKru3T&index=19
항상 감사합니다 뉴렉쳐 쌤 !!
AOP는 마치 빵또아와 같다.
개발을 하다보면 사용자를 위한 주 업무 로직 이외에,
개발자와 운영자를 위한 로직이 같이 생긴다!!! 뚜둥 ! ㅃ빵또앟ㅎ !!
각 주 업무 로직마다 로그, 보안, 트랜잭션 처리를 해야 하는데,
각 로직마다 호출했던 저 일들을 AOP가 관리를 해준다.
앞 뒤 관련 처리를 하나의 장소 안에서, 주 업무 로직을 호출하는 방식으로 처리하기 때문에
해당 로직들은 모두 로그, 보안, 트랜잭션 처리를 하는 것처럼 보인다.
이모습이 마치 빵또아를 보는 것과 비슷하다.
구체적으로는 proxy를 통해서 호출할 수 있다.
아래는 순수 자바코드로 AOP를 구현한 것이다!
public static void main(String[] args) {
Exam exam = new NewlecExam(1, 1, 1, 1);
Exam proxy = (Exam) Proxy.newProxyInstance(
NewlecExam.class.getClassLoader(), new Class[] { Exam.class },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(exam, args);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?";
System.out.println(message);
return result;
}
});
System.out.println(proxy.total());
System.out.println(proxy.avg());
}
위와 같이 Proxy Instance를 생성하여 그 안에 InvocationHandler를 구현하여 매개변수로 던진다.
이떄 구현부 안에 .invoke 메서드를 호출하는데 이곳이 바로 주업무로직의 호출부이다 !
이제 스프링의 AOP 기능을 이용하여 작성해보자
아래와 같이 LogAroundAdvice를 만들고
public class LogAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?";
System.out.println(message);
return result;
}
}
실행결과
209ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?
204ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?
위와 같이 코드를 작성해 준다.
이번에는 호출부가 invocation.proceed() 이다.
<bean id="exam" class="newlecture_spring.di.entity.NewlecExam" >
<constructor-arg name="kor" value="1" />
<constructor-arg name="eng" value="1" />
<constructor-arg name="com" value="1" />
<constructor-arg name="math" value="1" />
</bean>
<bean id="logAroundAdvice" class="newlecture_spring.aop.advice.LogAroundAdvice" />
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="exam" />
<property name="interceptorNames" >
<list>
<value>logAroundAdvice</value>
</list>
</property>
</bean>
그리고 xml 설정은 위와 같이 바꾼다 !
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("newlecture_spring/aop/setting.xml");
Exam proxy = (Exam) context.getBean("proxy");
}
그리고 메인함수에서 실행하면 된다 !
참고로 아래는 XML을 Annotation으로 바꾼 것인데 잘 동작하지 않았다 ㅠㅠ..
참고 하면 아니 된다..
public class SwAppConfig {
@Bean
public Exam exam() {
Exam exam = new NewlecExam(1, 1, 1, 1);
return exam;
}
@Bean
public LogAroundAdvice logAroundAdvice() {
return new LogAroundAdvice();
}
@Bean
public Exam proxy() {
ProxyFactoryBean proxy = new ProxyFactoryBean();
proxy.setTarget("exam");
proxy.setInterceptorNames("logAroundAdvice");
return (Exam) proxy.getObject();
}
}
BeforeAdvice는 아래 처럼 해보았다.
<bean id="proxy"
...
<list>
<value>logAroundAdvice</value>
<value>logBeforeAdvice</value>
</list>
...
public class LogBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("앞에서 실행될 로직");
}
}
AfterReturningAdvice
public class LogAfterReturningAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args,
Object target) throws Throwable {
System.out.println("returnValue: " + returnValue.getClass() +", method: " + method.getName());
}
}
실행결과
앞에서 실행될 로직
returnValue: class java.lang.Integer, method: total
208ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?
total : 4
앞에서 실행될 로직
returnValue: class java.lang.Float, method: avg
203ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?
avg : 1.0
AfterThrowingAdivce
@Override
public int total() {
if(kor > 100)
throw new IllegalArgumentException("유효하지 않은 국어 점수");
...
}
total에서 해당 조건에서 예외 설정을 해주고
public class LogAfterThrowingAdvice implements ThrowsAdvice {
public void afterThrowing(IllegalArgumentException e) throws Throwable {
System.out.println("예외가 발생하였습니다 : " + e.getMessage());
}
}
위 클래스를 추가한다.
BeforeAdvice만 실행되고 AfterThrowingAdivce에서 예외가 발생한 것을 볼 수 있다.
Point Cut : 원하는 메서드에만 Weaving을 하고 싶다.
...
<bean id="classicPointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value="total" />
</bean>
<bean id="classBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice" />
<property name="pointcut" ref="classicPointCut" />
</bean>
<bean id="classAroundAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logAroundAdvice" />
<property name="pointcut" ref="classicPointCut" />
</bean>
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="exam" />
<property name="interceptorNames" >
<list>
<value>classAroundAdvisor</value>
<value>classBeforeAdvisor</value>
...
실행결과
앞에서 실행될 로직
returnValue: class java.lang.Integer, method: total
204ms 시간이 흘렀습니다. : 난 proxy얌 야 빵또아 좋아하냐?
total : 4
returnValue: class java.lang.Float, method: avg
avg : 1.0
total에서만 Before과 Around가 출력되었다.
<bean id="classAroundAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logAroundAdvice" />
<property name="mappedNames" >
<list>
<value>total</value>
</list>
</property>
</bean>
<bean id="classBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice" />
<property name="mappedNames" >
<list>
<value>total</value>
</list>
</property>
</bean>
Refactoring : 위와 같이 간소화된 Advisor로도 표현이 가능하다.
<bean id="classBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice" />
<property name="patterns" >
<list>
<value>.*to.*</value>
...
Refactoring 2 : NameMatchMethodPointcutAdvisor 대신에 RegexpMethodPointcutAdvisor를 사용하면 정규식으로 method 명을 지정할 수 있다.
'Back-end > Spring Boot, JPA' 카테고리의 다른 글
spring security (2) | 2021.05.05 |
---|---|
newlecture: spring MVC 1 (0) | 2021.04.26 |
newlecture: spring DI (IoC) (0) | 2021.04.23 |
스프링 활용 JPA 강의 내용 기록 (0) | 2021.03.29 |
스프링 intro 강의 복습 차원 정리, ★ 단축키 (0) | 2021.03.19 |
hi hello... World >< 가장 아름다운 하나의 해답이 존재한다
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!