(스프링교육/마이바티스교육학원추천_탑크리에듀)#10.스프링프레임워크...
TRANSCRIPT
스프링프레임워크 & 마이바티스
(Spring Framework, MyBatis)
3-5. 포인트컷(Pointcut)
Pointcut은 모든 Join Point중 Advice가 Weaving 되어야 할 Join Point의 집합을 정의한 것이
다.
교차점(PointCut)은 특정한 클래스의 특정한 메소드가 특정한 기준과 일치하는지를 판단한다.
만약 그 메소드가 실제로 일치한다면 충고가 적용된다.
스프링은 충고를 받으려고 하는 클래스와 메소드의 관점에서 교차점을 정의하며 충고는 클래
스의 이름과 메소드 시그네처(Method Signature)와 같은 특징에 기초하여 대상 클래스와 메
소드에 엮인다.
스프링의 교차점 프레임워크를 위한 핵심 인터페이스는 PointCut, PointCut은 메소드와 클래
스에 기초하여 충고를 어디에 엮을지 결정한다.
Pointcut 구현체를 사용하려면 먼저 Advisor 인터페이스의 인스턴스를 생성하거나 좀 더 구체
적으로 PointcutAdvisor 인터페이스의 인스턴스를 생성해야 한다.
PointCut Interface
public interface PointCut {
ClassFilter getClassFilter(); //클래스의 특정메소드에 pointcut적용여부판단
MethodMatcher getMethodMatcher();
}
ClassFilter Interface : 인자로 넘어온 Class가 충고를 받아야 하는지를 판단해야 한다.
public interface ClassFilter {
boolean matches(Class class);
}
MethodMatcher
public interface MethodMatcher {
boolean matches(Method m, Class targetClass);
public boolean isRuntime();
public boolean matches(Method m, Class target, Object[] args);
}
matches(Method ,Class) 메소드는 대상 Class와 Method를 기초로 메소드가 충고를 받을 수 있는
지의 여부를 판단한다. true가 리턴되면 어떤 유형의 MethodMatcher인지 판단하기 위해
isRuntime()이 호출된다. Static 교차점(pointcut)은 항상 실행되는 교차점을 정의하며 이 경우
isRuntime()은 false를 return하며 동적 교차점(Dynamic Pointcut)은 런타임시의 메소드 인자를 확
인하여 충고가 실행되어야 하는지를 결정한다. matches 메소드와 마찬가지로 isRuntime()은 프록
시 객체가 생성될 때 오직 한번만 호출된다.
충고자(Advisor)
Aspect는 행동을 정의한 충고와 실행돼야 할 위치를 정의한 교차점(Pointcut)의 조합으로 이루어
짂다. 스프링에서는 이를 위해 충고자라는 것을 제공한다. 충고자는 충고(Advice)와 교차점
(Pointcut)을 하나의 객체로 합친것 이다.
public interface PointcutAdvisor {
Pointcut getPointcut();
Advice getAdvice();
}
3-5-1. 포인트컷(Pointcut) - StaticMethodMatcherPointCut
StaticMethodMatcherPointcut을 상속할 때는 matches 메소드만 구현하면 되지만 올바른 타
입의 메소드만 어드바이스가 적용되도록 할려면 getClassFilter() 메소드를 오버라이드 하는 것
이 좋다.
File -> New -> Spring Legacy Project
Project name : springonj
Simple Spring Maven 선택
[pom.xml에 아래 의존성 추가]
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
[First.java]
package onj.edu.aop1;
public class First {
public void one() {
System.out.println("First One...");
}
public void two() {
System.out.println("First Two...");
}
}
[Second.java]
package onj.edu.aop1;
public class Second {
public void one() {
System.out.println("Second One...");
}
public void two() {
System.out.println("Second Two...");
}
}
[SimpleAdvice.java]
package onj.edu.aop1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SimpleAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(invocation.getMethod().getName());
Object o = invocation.proceed();
System.out.println("... SimpleAdvice의 충고가 적용됨 ...");
return o;
}
}
[SimpleStaticPointcut.java]
package onj.edu.aop1;
import java.lang.reflect.Method;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
// 아래는First.class의 one()의 실행 전/후 충고가 적용된다.
public boolean matches(Method method, Class<?> cls) {
return ("one".equals(method.getName()) && cls == First.class);
}
}
[StaticPointcutExam.java]
public class StaticPointcutExam {
public static void main(String[] args) {
First f = new First();
Second s = new Second();
First proxyFirst;
Second proxySecond;
//pointcut, advice, advisor 생성
Pointcut pc = new SimpleStaticPointcut();
Advice advice = new SimpleAdvice();
Advisor advisor = new DefaultPointcutAdvisor(pc, advice);
//First 프록시 생성
ProxyFactory pf = new ProxyFactory();
pf.addAdvisor(advisor);
pf.setTarget(f); //First.class를 타겟으로
proxyFirst = (First)pf.getProxy();
proxyFirst.one();
proxyFirst.two();
//Second 프록시 생성
pf = new ProxyFactory();
pf.addAdvisor(advisor);
pf.setTarget(s); //Second.class를 타겟으로
proxySecond = (Second)pf.getProxy();
proxySecond.one();
proxySecond.two();
}
}
[결과]
one
First One...
... SimpleAdvice의 충고가 적용됨 ...
First Two...
Second One...
Second Two...
3-5-2. 포인트컷(Pointcut) - DynamicMethodMatcherPointCut
DynamicMethodMatcherPointcut에서 충고가 적용되는 메소드는 모든 메소드를 검사하는 초
기화 단계, 메소드가 처음 호출되는 시점에 걸쳐 두 번의 정적 검사를 받게 된다. 이 처럼
동적 포인트 컷은 정적 포인트 컷 보다 유연하게 적용될 수 있지만 성능 부담을 고려해 필요
한 경우만 사용해야 한다.
[First.java]
package onj.edu.aop2;
public class First {
public void one(int i) {
System.out.println("First One... i = " + i);
}
public void two() {
System.out.println("First Two...");
}
}
[SimpleAdvice.java]
package onj.edu.aop2;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//주변충고
public class SimpleAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(invocation.getMethod().getName());
Object o = invocation.proceed();
System.out.println("... SimpleAdvice의 충고가 적용됨 ...");
return o;
}
}
[SimpleDynamicPointcut.java]
package onj.edu.aop2;
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
/* DynamicMethodMatcherPointcut은
* matches(Method method, Class<?> cls, Object[] args) 반드시 구현해야함
* 정적체크를 위해서는 matches(Method method, Class<?> cls)도 같이 구현해야 한다.
*/
public class SimpleDynamicPointcut extends DynamicMethodMatcherPointcut {
//메소드 이름이 one 인 경우 true 즉 충고가 주입된다.아래는 정적체크
//스프링은 two 메소드에 대해서는 동적 검사를 진행 안함
public boolean matches(Method method, Class<?> cls) {
System.out.println("static check :: method.getName() :: " + method.getName());
return "one".equals(method.getName());
}
//동적 검사
public boolean matches(Method method, Class<?> cls, Object[] args) {
System.out.println("Dynamic Check : " + ((Integer)args[0]).intValue());
int i = ((Integer)args[0]).intValue();
return i > 100;
}
//First.class만 충고가 주입된다.
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class <?> cls) {
return (cls == First.class);
}
};
}
}
[DynamicPointcutExam.java]
package onj.edu.aop2;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
public class DynamicPointcutExam {
public static void main(String[] args) {
First target = new First();
//어드바이저 생성
Advisor advisor = new DefaultPointcutAdvisor(new
SimpleDynamicPointcut(), new SimpleAdvice());
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
First proxy = (First)pf.getProxy();
proxy.one(99);
proxy.one(101);
proxy.two();
}
}
[결과]
3-5-3. 포인트컷(Pointcut) - NameMatchMethodPointCut
가끔은 메소드 시그네처, 리턴형은 무시하고 메소드 이름으로 적용여부를 판단하는 포인트
컷이 필요하다. 이때 사용되는 포인트 컷이 NameMatchMethodPointcut 이다. 이번에는 포
인트 컷으로 사용할 클래스를 만들지 않아도 되며 직접 NameMatchMethodPointcut을 new
하면 된다.
[First.java]
package onj.edu.aop3;
public class First {
public void one() {
System.out.println("First One...");
}
public void two() {
System.out.println("First Two...");
}
public void three() {
System.out.println("First Three...");
}
}
[SimpleAdvice.java]
package onj.edu.aop3;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SimpleAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(invocation.getMethod().getName());
Object o = invocation.proceed();
System.out.println("... SimpleAdvice의 충고가 적용됨 ...");
return o;
}
}
[NameMatchMethodPointcutExam.java]
package onj.edu.aop3;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.NameMatchMethodPointcut;
public class NameMatchMethodPointcutExam {
public static void main(String[] args) {
First target = new First();
//Advisor
NameMatchMethodPointcut pc = new NameMatchMethodPointcut();
pc.addMethodName("one");
pc.addMethodName("two");
Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());
//Proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
First f = (First)pf.getProxy();
f.one();
f.two();
f.three();
}
}
[결과]
3-5-4. 포인트컷(Pointcut) - JdkRegexpMethodPointcut
앞 예제 처럼 메소드 이름을 정확히 알지 못하는 경우, 이름대싞 패턴을 사용하여 포인트 컷
을 생성할 수 있는데 이때 사용되는 포인트 컷이 JdkRegexpMethodPointcut 이다.
[First.java]
package onj.edu.aop4;
public class First {
public void hello1() {
System.out.println("hello1 ... ");
}
public void hello2() {
System.out.println("hello2 ... ");
}
public void sayHello() {
System.out.println("sayHello ... ");
}
}
[RegExpExam.java]
public class RegExpExam {
public static void main(String[] args) {
First target = new First();
//Advisor
JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();
//스프링은 비교할 때 onj.edu.aop4.RegExam.hello1을 사용한다.
//.* 어떤패키지에있던지... hello를 담고 있으면 OK
pc.setPattern(".*hello.*");
Advisor advisor = new DefaultPointcutAdvisor(pc, new
SimpleAdvice());
//Proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
First f = (First)pf.getProxy();
f.hello1();
f.hello2();
f.sayHello();
}
}
[결과]
Pointcut은 Advice가 적용될 메소드를 골라야 하는데 Method Signature에 일치하는 패턴을 지정
하는 방법을 주로 사용하는 포인트 컷이다.
org.springframework.aop.support.JdkregexpMethodPointcut JDK1.4이상 사용
<bean id=“smallMartPointcut”
class=“org.springframework.aop.support.JdkregexpMethodPointcut”>
<!-- 어떤클래스든 관계없고 get으로 시작하는 메소드
</bean>
Pointcut을 정의했으면 이를 Advice에 결합시킬 수 있다.
<bean id=“smallMartAdvisor” class=“org.springframework.aop.support.DefaultPointcutAdvisor”>
<property name=“advice” ref=“beforeLogging”/>
<property name=“pointcut” ref= “smallMartPointcut”/>
</bean>
앞에서 pointcut을 만들고 advice와 결합을 시켰는데 하나의 빈에 pointcut과 advice를 동시에
정의할 수 있는 특별한 Advisor가 있다.
(org.springframework.aop.support.RegexpMethodPointcutAdvisor)
<bean id=“smallMartAdvisor” class=“org.springframework.aop.support.
RegexpMethodPointcutAdvisor”>
<property name=“advice” ref=“beforeLogging”/>
<property name=“pattern” value=“*.get*”/>
</bean>
3-5-5. 포인트컷(Pointcut) - AspectJExpressionPointcut
Jdk 정규 표현식외 AspectJ 포인트컷 표현식 언어를 통해 포인트 컷을 선언할 수도 있다. JDK
정규식 보다 많이 사용되며 스프링은 AspectJExpressionPointcut 클래스를 제공하며
aspectjrt.jar, aspectjweaver.jar 두 라이브러리 파일이 필요하다.
[AspectJPointcutExam.java]
public static void main(String[] args) {
First target = new First();
//Advisor
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
//인자, 반홖 관계없이 hello로 시작하는...
pc.setExpression("execution(* hello*(..))");
Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());
//Proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
First f = (First)pf.getProxy();
f.hello1();
f.hello2();
f.sayHello();
}
XML 기반 사용예
<bean id=“smallMartPointcut” class=“org.springframework.aop.aspectj.
AspectJExpressionPointcut”>
<!–모든 리턴형에 대해, 모든 클래스의 getProduct 메소드
<property name=“expression” value=“execution(* *.getProducts(..)”/>
</bean>
아래처럼 한번에도 가능하다.
<bean id=“smallMartAdvisor” class=“org.springframework.aop.aspectj.
AspectJExpressionPointcutAdvisor”>
<property name=“advice” ref=“beforeLogging”/>
<property name=“expression” value=“execution(* *.getProducts(..)”/>
</bean>
3-5-6. 포인트컷(Pointcut) - AnnotationMatchingPointcut
Application이 어노테이션 기반이라면 커스텀 어노테이션을 사용해서 포인트컷을 지정하고 어
드바이스를 특정 어노테이션이 적용된 모든 메소드 또는 타입에 적용하고 싶을 때가 있다.
스프링은 어노테이션을 사용해서 포인트컷을 정의할 수 있도록 해주는
AnnotationMatchingPointcut 클래스를 제공한다.
[AdviceRequired.java]
package onj.edu.aop6;
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.TYPE, ElementType.METHOD}) public @interface AdviceRequired
{ //interface를 어노테이션으로 선언
}
[First.java]
package onj.edu.aop6;
public class First {
@AdviceRequired //어드바이스가 적용될 것
public void hello() {
System.out.println("hello1 ... ");
}
public void sayHello() {
System.out.println("sayHello ... ");
}
}
[SimpleAdvice.java]
package onj.edu.aop6;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SimpleAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(invocation.getMethod().getName());
Object o = invocation.proceed();
System.out.println("... SimpleAdvice의 충고가 적용됨 ...");
return o;
}
}
[AnnotationPointcutExam.java]
public class AnnotationPointcutExam {
public static void main(String[] args) {
First target = new First();
//Advisor, 메소드를 호출하면서 지정한 어노테이션이 적용된 모든 메소드에 어
드바이스를 적용
AnnotationMatchingPointcut pc =
AnnotationMatchingPointcut.forMethodAnnotation(AdviceRequired.class);
Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());
//Proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
First f = (First)pf.getProxy();
f.hello();
f.sayHello();
}
}
[결과]
3-5-7. 포인트컷(Pointcut) - ControllFlowPointcut
특정 메소드 하위의 모든 메소드 호출 또는 특정 클래스 하위의 모든 메소드 호출에 사용한
다
[First.java]
package onj.edu.aop7;
public class First {
public void hello() {
// 특정 메소드인 myAdvice()에서 Call할 때 충고 적용함
System.out.println("hello ... ");
}
}
[SimpleBeforeAdvice.java]
package onj.edu.aop7;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class SimpleBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object args[], Object target) throws Throwable {
System.out.println("Before Method ::" + method);
}
}
[ControlFlowExam.java]
public class CaontrolFlowExam {
public static void main(String[] args) {
ControlFlowExam c = new ControlFlowExam();
c.go();
}
void go() {
First target = new First();
//ControlFlowExam.class의 myAdvice() 가 호출하는 모든 메소드에 충고적용
Pointcut pc = new ControlFlowPointcut(ControlFlowExam.class, "myAdvice");
Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleBeforeAdvice());
//Proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisors(advisor);
First f = (First)pf.getProxy();
//Normal Invoke
f.hello();
//ControlFlowExam.myAdvice()
myAdvice(f);
}
void myAdvice(First f) {
f.hello();
}
}
[결과]