출처 : www.youtube.com/watch?v=y2JkXjOocZ4&list=PLq8wAnVUcTFVUMCKI4CL_EHWW5ykPnL0z
** Annotation 방식은 다음에 따로 정리하겠습니다.
AOP란 (Aspect Oriented Programming)
관점 지향 프로그래밍
- 사용자 중심인 객체 지향 프로그래밍에서 좀 더 큰 틀인 관점 지향 프로그래밍이라고 합니다.
- 기존엔 사용자가 원하는 업무 기반의 로직에만 관심을 가졌었지만, 사용자의 요구사항말고 이 요구사항을 수반하기 위해 코드들을 작성하다보니 그 외의 코드가 들어가게 됨을 느낍니다.
(개발자/관리자가 프로그램을 구현하기 위해서든 테스트하기 위해서든 필요한 코드들이 존재하게 됨.)
- 사용자 관점으로 봤떤것이 지금까지 객체지향으로 잘 만들어 졌다면, 추가로 만들게 되는 로직을 개발자/운영자 관점에서 넣어야하는 로직이 있어야함.
- 이걸 관점 지향 업무라 함. (객체지향보다 큰 개념)
- 주 업무 로직 : 객체지향, 그 외의 것까지 포함 : 관점 지향
- Primary(Core) Concern : 관점에 해당되는 것중에서 주 관심사에 해당되는 것들은 객체로 만들어짐, 실질적인 업무들은 메소드로 만들어짐
- cross-cutting : 관점이 다른로직
- 주업무 로직과 뺏다 꽂았다가 자유로워야 함.
- 보안처리 : 권한을 줄수도 뺄수도있어야함.
- 로그처리 : 남겼다 뺏다 지웠다..
- 과거 커팅작업이 쉽지않아 직접 지우거나,, 주석처리,,, 소스를 가지고 잇는 사람만이 할 수 있고 재배포에 불편함을 느꼈습니다.
그래서 이 방법을 쉽게 해보려고 생각해낸게 AOP 방법론이라고 합니다.
- 주 업무 로직 분리(Core Concern) , 개발자가 처리 방식을 따로 생성 (cross cutting Concern)
바로 따로 공부한 강의 코드들로 보여드리겠습니다.
자바코드 관점지향 방식
프록시 개념
- 실제 주 업무를 갖고 있는 녀석을 호출해주는 그러한 역할을 함.
- 사용자는 주업무로직을 직접 호출하는게 아니라 프록시를 호출하게 됨.
- 프록시에서 앞뒤로 개발자가 원하는 코드를 삽입
스프링 방식의 AOP
- 스프링을 이용하면 이것을 자유롭게 구현이 가능함.
- 파일 구성
Program.java
public class Program {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/aop/setting.xml");
// ApplicationContext context = new AnnotationConfigApplicationContext(NewlecAppConfig.class);
Exam exam = context.getBean("exam",Exam.class);
System.out.printf("total is %d\n", exam.total());
System.out.printf("avg is %f\n", exam.avg());
}
}
LogAroundAdvice
- implements MethodInterceptor (작업 전 후로)
(코어 컨서 실행전)
LogBeforeAdvice
- implements MethodBeforeAdvice
(반환 값을 가지고 할것이 있다면)
logAfterReturning 결과값을 반환한다음 이어서 작업할게 있으면 ..
- implements AfterReturningAdvice
LogAfterThrowingAdvice
- implements ThrowsAdvice 어떤 예외가 발생하느냐에 따라 함수의 인자가 달라짐
LogAroundAdvice
package spring.aop.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
// 주 업무 로직의 앞 뒤로 생성
public class LogAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
String message = (end - start) + "ms 시간이 걸렸다.";
System.out.println(message);
return result;
}
}
LogBeforeAdvice
package spring.aop.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
// 주 업무 로직의 앞에서만 실행이 됨.
public class LogBeforeAdvice implements MethodBeforeAdvice{
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("앞에서 실행될 로직");
}
}
LogAfterReturningAdvice
package spring.aop.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
// 주 업무 로직의 결과값을 이용하여 ..
public class LogAfterReturningAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("returnValue:"+returnValue + ", method:"+method.getName());
}
}
LogAfterThrowingAdvice
package spring.aop.advice;
import org.springframework.aop.ThrowsAdvice;
// 예외가 발생했을 시.
public class LogAfterThrowingAdvice implements ThrowsAdvice{
public void afterThrowing(IllegalArgumentException e) throws Throwable{
System.out.println("예외가 발생하였습니다.: " + e.getMessage());
}
}
- Spring AOP 용어
AOP는 프록시를 갖고있음
- 포인트 컷(Pointcuts) : total() 메서드만 위빙이 됐음 좋겠다. (별도의 메서드만 호출)
- 조인 포인트(JoinPoint) : 프록시는 타겟을 대상으로 모든 메서드를 조인포인트라 생각함.조인하게될 메서드 포인트
- 위빙(weaving) : 프록시와의 연결과정
org.springframework.aop.support.NameMatchMethodPointcut
- 네임이란것을 기준으로 하는 포인트 컷
- total, avg : advice 별로 포인트 컷 사용 가능
org.springframework.aop.support.DefaultPointcutAdvisor
- 포인트 컷과 어드바이스를 연결해주는 역할
setting.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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">
<bean id="target" class="spring.aop.entity.NewlecExam" p:kor="1" p:eng="1" p:math="1" p:com="1" />
<!-- 곁다리업무를 하는 핸들러 -->
<bean id="logAroundAdvice" class="spring.aop.advice.LogAroundAdvice" />
<bean id="logBeforeAdvice" class="spring.aop.advice.LogBeforeAdvice" />
<bean id="logAfterReturningAdvice" class="spring.aop.advice.LogAfterReturningAdvice" />
<bean id="logAfterThrowingAdvice" class="spring.aop.advice.LogAfterThrowingAdvice" />
<!--
<bean id = "classicPointCut" class = "org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value = "total" />
</bean>
-->
<!-- <bean id="classicBeforeAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice"/>
<property name="pointcut" ref="classicPointCut"/>
</bean>
<bean id="classicAroundAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logAroundAdvice"/>
<property name="pointcut" ref="classicPointCut"/>
</bean> -->
<!-- <bean id="classicBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice"/>
<property name="mappedNames">
<list>
<value>total</value>
<value>avg</value>
</list>
</property>
</bean> -->
<!-- 간소화된 advisor -->
<bean id="classicBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="logBeforeAdvice"/>
<property name="patterns">
<list>
<value>.*to.*</value>
</list>
</property>
</bean>
<bean id="classicAroundAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logAroundAdvice"/>
<property name="mappedName" value = "total"/>
</bean>
<!-- 프록시 생성 -->
<bean id="exam" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="target" /> <!-- name 프록시 세터, ref target 레퍼런스 -->
<property name="interceptorNames">
<list>
<value>classicAroundAdvisor</value>
<value>classicBeforeAdvisor</value>
<value>logAfterReturningAdvice</value>
<value>logAfterThrowingAdvice</value>
</list>
</property>
</bean>
</beans>
반응형
'자바과정 > 스프링' 카테고리의 다른 글
스프링 Web(DispatcherServlet, HandlerMapping, HandlerAdapter, viewResolver) (2) | 2021.04.08 |
---|---|
스프링 AOP (Annotation 방식과 Pointcut 종류) (0) | 2021.04.08 |
스프링 실습예제(Xml 방식의 AOP) (0) | 2021.04.08 |
스프링 실습 예제(Profile, Environment 사용) (0) | 2021.04.07 |
스프링 실습 예제(static과 load로 외부 데이터 2개) (0) | 2021.04.07 |
댓글