스프링 AOP 포인트컷(pointcut), StaticMethodMatcherPointcut 이론및실습, Spring AOP 용어 프록시/포인트컷/타겟클래스/위빙(자바동영상/스프링동영상/자바동영상/자바교육/스프링교육/SpringFramework/스프링프레임워크/스프링학원/자바학원/자바/JAVA)
3-5. 포인트컷(Pointcut)
n Pointcut은 모든 Join Point중 Advice가 Weaving 되어야 할 Join Point의 집합을 정의한 것이다.
n 교차점(PointCut)은 특정한 클래스의 특정한 메소드가 특정한 기준과 일치하는지를 판단한다. 만약 그 메소드가 실제로 일치한다면 충고가 적용된다.
n 스프링은 충고를 받으려고 하는 클래스와 메소드의 관점에서 교차점을 정의하며 충고는 클래스의 이름과 메소드 시그네처(Method Signature)와 같은 특징에 기초하여 대상 클래스와 메소드에 엮인다.
n 스프링의 교차점 프레임워크를 위한 핵심 인터페이스는 PointCut, PointCut은 메소드와 클래스에 기초하여 충고를 어디에 엮을지 결정한다.
n Pointcut 구현체를 사용하려면 먼저 Advisor 인터페이스의 인스턴스를 생성하거나 좀 더 구체적으로 PointcutAdvisor 인터페이스의 인스턴스를 생성해야 한다.
n ControlFlowPointcut : 특정 메소드 하위의 모든 메소드 호출 또는 특정 클래스 하위의 모든 메소드 호출에 사용한다.
n PointCut Interface
public interface PointCut {
ClassFilter getClassFilter(); //클래스에 pointcut적용여부판단
MethodMatcher getMethodMatcher(); //클래스의 특정메소드에 pointcut적용여부판단
}
n ClassFilter Interface : 인자로 넘어온 Class가 충고를 받아야 하는지를 판단해야 한다.
public interface ClassFilter {
boolean matches(Class class);
}
n MethodMatcher
public interface MethodMatcher {
public 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()은 프록시 객체가 생성될 때 오직 한번만 호출된다.
n 충고자(Advisor)
Aspect는 행동을 정의한 충고와 실행돼야 할 위치를 정의한 교차점(Pointcut)의 조합으로 이루어 진다. 스프링에서는 이를 위해 충고자(Advisor)라는 것을 제공한다. 충고자는 충고(Advice)와 교차점(Pointcut)을 하나의 객체로 합친것 이다.
public interface PointcutAdvisor {
Pointcut getPointcut();
Advice getAdvice();
}
3-5-1. 포인트컷(Pointcut) - StaticMethodMatcherPointCut
n StaticMethodMatcherPointcut을 상속할 때는 matches 메소드만 구현하면 되지만 올바른 타입의 메소드만 어드바이스가 적용되도록 할려면 getClassFilter() 메소드를 오버라이드 하는 것이 좋다.
File -> New -> Spring Legacy Project
Project name : springonj
Simple Spring Maven 선택
[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 생성
Pointcut pc = new SimpleStaticPointcut();
Advice advice = new SimpleAdvice();
//advisor 생성(advice + pointcut)
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...
이전에 작성한 프로그래밍적 방법을 통한 SmallMart AOP 예제에 getProducts2() 메소드를 추가로 만들고 StaticMethodMatcherPointcut을 적용하여 getProducts2 메소드에만 충고가 적용되도록 변경하자.
File , New , Spring Starter Project
ProjectName : demo-smallmart-1
1. SmallMart.java
package onj.hello.aop;
public interface SmallMart {
public String getProducts(String productName) throws Exception ;
public String getProducts2(String productName) throws Exception ;
}
2. SmallMartImpl.java
package onj.hello.aop;
public class SmallMartImpl implements SmallMart{
public String getProducts(String productName) throws Exception {
System.out.println("getProduct()…" + productName);
//throw new Exception("예외 발생 시킴...");
return "SmallMart getProducts......................";
}
public String getProducts2(String productName) throws Exception {
System.out.println("getProduct2()…" + productName);
//throw new Exception("예외 발생 시킴...");
return "SmallMart getProducts2().....................";
}
}
3. 이번에는 충고 클래스를 각각 만들자.
package oraclejava.traing.aop2;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class AfterLoggingAdvice implements AfterReturningAdvice{
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable{
System.out.println(arg1.getName() + " : 사후충고");
}
}
package oraclejava.traing.aop2;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundLoggingAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation arg0) throws Throwable {
String findName = (String) arg0.getArguments()[0];
if (findName == null) {
throw new Throwable("예외");
}
System.out.println("[주변충고]메소드실행전..");
Object returnedObj = arg0.proceed();
System.out.println("[주변충고]메소드실행후");
//주변충고는 원래 메소드의 리컨값을 대체할 수 있다.
return "[주변충고]getProducts2() 메소드의 리턴을 대체 한것입니다.";
}// :
}
package oraclejava.traing.aop2;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class BeforeLoggingAdvice implements MethodBeforeAdvice{
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable{//대상메소드의첫번째인자를캐스팅(String productName) …
String findName= (String)arg1[0];
System.out.println(arg0.getName() + ":: 사전충고.");
}
}
package oraclejava.traing.aop2;
import org.springframework.aop.ThrowsAdvice;
public class ThrowsLoggingAdvice implements ThrowsAdvice{
public void afterThrowing(Throwable throwable) {
System.out.println("예외충고 ....");
}
}
4. 이번에는 포인트컷을 만들자. StaticMethodMatcherPointcut은 클래스이름 or 메소드 이름과 같은 정적인 정보에 기초하여 출고를 받을 메소드를 정의하는 포인트컷이며 스프링에서 제공한다. 이를 상속받아 포인트컷 클래스를 작성하자.
SmallMart클래스의 getProducts2 메소드가 충고를 받을 메소드 이다.
package oraclejava.traing.aop2;
import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
public class SmallMartStaticPointcut extends StaticMethodMatcherPointcut{
public boolean matches(Method method, Class<?> cls) {
return ("getProducts2".equals(method.getName()));
}
//아래처럼 getClassFilter를 만들어도 되지만 위 matches의 return문에 clas파라미터를
//이용하여 같이 클래스 이름도 비교하면 더 간단해 진다.
//return ("getProducts2".equals(method.getName() && clas == SmallMartImpl.class));
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class<?> cls) {
return(cls== SmallMartImpl.class);}};}
}
5. 메인클래스
package onj.hello.aop;
import org.aopalliance.aop.Advice;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoSmallmart1Application {
public static void main(String[] args) {
SpringApplication.run(DemoSmallmart1Application.class, args);
SmallMart target = new SmallMartImpl();
Pointcut pc = new SmallMartStaticPointcut();
Advice advice1= new AfterLoggingAdvice();
Advisor advisor1= new DefaultPointcutAdvisor(pc, advice1);
Advice advice2= new BeforeLoggingAdvice();
Advisor advisor2= new DefaultPointcutAdvisor(pc, advice2);
Advice advice3= new AroundLoggingAdvice();
Advisor advisor3= new DefaultPointcutAdvisor(pc, advice3);
Advice advice4= new ThrowsLoggingAdvice();
Advisor advisor4= new DefaultPointcutAdvisor(pc, advice4);
ProxyFactory pf = new ProxyFactory();
pf.addAdvisor(advisor1);
pf.addAdvisor(advisor2);
pf.addAdvisor(advisor3);
pf.addAdvisor(advisor4);
pf.setTarget(target);
try {
SmallMart proxy = (SmallMart) pf.getProxy();
System.out.println(proxy.getProducts("오라클자바커뮤니티"));
System.out.println(proxy.getProducts2("오라클자바커뮤니티"));
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}
#스프링충고, #포인트컷, #스프링pointcut, #스프링AOP, #스프링advice, #SpringAOP, #스프링DI, #스프링IoC, #SpringDI, #SpringIoC, #자바스프링, #Spring동영상, #Spring강의, #스프링프레임워크, #스프링교육, #스프링학원, #스프링강좌, #스프링강의, #자바학원, #자바, #스프링동영상, #자바동영상, #스프링프레임워크교육, #스프링프레임워크강의, #스프링프레임워크학원