2022. 6. 3. 13:54γBackend/πΏ Spring
ν¬μΈνΈμ»· (pointcut)
: νν°λ§ ν μμΉλ₯Ό μ§μ νλ νν°λ§ λ‘μ§μ΄λ€. μ¦ μ΄λμ λΆκ° κΈ°λ₯μ μ μ©ν μ§ μ΄μ μ λλ€.μ΄λλ°μ΄λ (advice)
: νλ‘μκ° νΈμΆνλ λΆκ° κΈ°λ₯ μ΄λ€. λ¨μν νλ‘μκ° μ μ©ν λΆκ°κΈ°λ₯ λ‘μ§μ΄λΌ μκ°νλ©΄ λλ€.μ΄λλ°μ΄μ (advisor)
: λ¨μνκ² νλμ ν¬μΈνΈμ»·κ³Ό νλμ μ΄λλ°μ΄μ€λ₯Ό κ°μ§κ³ μλ κ²μ΄λ€. μ½κ² μκ°ν΄ ν¬μΈνΈμ»· 1 : μ΄λλ°μ΄μ€ 1 μΈ κ².
λ¬Έμ₯μΌλ‘ μ 리νλ©΄, λΆκ° κΈ°λ₯ λ‘μ§μ μ μ©ν λ , μ΄λμ μ μ©ν μ§ μμΉ μ§μ μ pointcut μ΄ λ΄λΉνκ³ , μ΄λ€ λ‘μ§μ μ μ©ν μ§λ advice κ° λ΄λΉνλ€. 그리νμ¬ μ΄λμ, μ΄λ€ λΆκ° κΈ°λ₯ λ‘μ§μ μ§μ ν μ§ λͺ¨λ μκ³ μλ κ²μ΄ μ΄λλ°μ΄μ (advisor)κ° λλ€.
μν κ³Ό μ± μ
- advice, pointcut κ°λ μ΄ μμ λλ, proxy λΆκ° κΈ°λ₯ λ‘μ§ λ΄λΆμμ if λ¬ΈμΌλ‘ λΆκΈ°νμ¬ Pattern 맀μΉμΌλ‘ νν°λ§ λ‘μ§μ ꡬννλ€. μ΄λ SRP λ₯Ό μλ°νλ©° μ½λλ₯Ό κΉλνκ² λνλ΄μ§λ λͺ»νλ€.
- ν¬μΈνΈμ»·μ λμ μ¬λΆλ₯Ό νμΈνλ νν° μν λ§ λ΄λΉνκ² λκ³ ,
- μ΄λλ°μ΄μ€λ λΆκ° κΈ°λ₯ λ‘μ§λ§ λ΄λΉνκ² λλ€.
- λμ ν©μΉλ©΄ μ΄λλ°μ΄μ κ° λλ€. μ¦ μ€νλ§ Advisor λ νλμ pointcut + νλμ advice λ‘ κ΅¬μ±λλ€.
- μ¬κΈ°μ μ€μν μ μ νλμ μ΄λλ°μ΄μ€μ μ¬λ¬ λΆκ°κΈ°λ₯ λ‘μ§μ μΆκ°ν μ μλ€λ μ μ΄λ€. μ΄ λΆλΆμ μλμμ λ μμ보μ.
μ€κ° μ 리
μ°λ¦¬κ° advice, pointcut, advisor κ°μ κ°λ μ λ°°μ°λ μ΄μ λ νλ‘μλ₯Ό μ μ©νκΈ° μν¨μ΄λ€.
κ·ΈλΌ νλ‘μλ μ λ°°μ°λκ°? ν΄λΌμ΄μΈνΈκ° μ ν리μΌμ΄μ μ€ν μ μλλλ‘ μ¬μ©λ κΈ°λ₯μ save() μ find() μ΄λ€ (μ κ·Έλ¦Ό μ°Έκ³ ).
μ΄ λ save(), find() λ μ μμ μΌλ‘ μ€νμν€λ, κΈ°νμλλ κ°λ° μ΄μμ λΆκ°μ μΌλ‘ μ€νν΄μΌ ν κΈ°λ₯μ΄ μ‘΄μ¬ν μ μλ€. κ·Έλ΄ κ²½μ° νλ‘μλ₯Ό μ½μ νμ¬, target μΈμ€ν΄μ€μ κΈ°λ₯μ μ μμ μΌλ‘ μ€νμν€κ³ , μ€κ°μμ λΆκ°μ μΌλ‘ μ€ννκ³ μΆμ κΈ°λ₯λ μΆκ°ν μ μλ€. μ΄κ²μ΄ νλ‘μλ₯Ό μ¬μ©νλ μλ μ€ νλλΌ μ§κ³ λμ΄κ°λ©΄ μ’κ² λ€.
μ΄λλ°μ΄μ - μμ μ½λ
@Slf4j
public class AdvisorTest {
@Test
void advisorTest1() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
// μ΄μ κ³Ό λ¬λΌμ§ λΆλΆ : DefaultPointcutAdvisor() κ°μ²΄ μμ±.
// Advisor μΆκ° -> νλμ pointcut κ³Ό νλμ advice λ₯Ό κ°λλ€.
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(Pointcut.TRUE, new TimeAdvice());
proxyFactory.addAdvisor(advisor); // κΈ°μ‘΄ .addAdvise() -> .addAdvisor()
// addAdvise λ΄λΆλ₯Ό 보면 νΈμμ addAdvisor λ μ μ©μ΄ λμ΄μλ€.
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find();
}
}
- new DefaulPointcutAdvisor : Advisor μΈν°νμ΄μ€ κ°μ₯ μΌλ°μ μΈ κ΅¬ν체. μμ±μλ₯Ό ν΅ν΄ νλμ ν¬μΈνΈμ»·κ³Ό νλμ μ΄λλ°μ΄μ€λ₯Ό λ£μ΄μ£Όλ©΄ λλ€. 첫λ²μ§Έ μΈμμλ pointcut, λλ²μ§Έ μΈμμλ adviceκ° λ€μ΄κ°λ©΄ λλ€.
- Pointcut.TRUE : νμ true λ₯Ό λ°ννλ ν¬μΈνΈμ»·μΌλ‘, μ΄λ ν νν°λ μμμ μλ―Ένλ€.
- proxyFactory.addAdvisor(advisor) : νλ‘μ ν©ν 리μ μ μ©ν μ΄λλ°μ΄μ λ₯Ό μ§μ νλ€. μ΄λλ°μ΄μ λ λ΄λΆμ ν¬μΈνΈμ»·κ³Ό μ΄λλ°μ΄μ€λ₯Ό λͺ¨λ κ°μ§κ³ μκΈ° λλ¬Έμ, μ΄λμ μ΄λ€ λΆκ°κΈ°λ₯μ μ μ©ν΄μΌν μ§ μ΄λλ°μ΄μ νλλ‘ μ μ μλ€.
μ§μ λ§λ ν¬μΈνΈμ»· - μμ μ½λ
ν¬μΈνΈμ»·μ 컀μ€ν νμ¬ μ§μ μμ±ν μ μλ€ .λ¬Όλ‘ μ€νλ§μμ μ 곡νλ κ°λ¨ν λ°©λ²λ μ‘΄μ¬νλ€. νμ§λ§ μ§μ ν¬μΈνΈ μ»·μ μμ±νλ©΄ μ’ λ μ μ°νκ² λ‘μ§ μ²λ¦¬λ₯Ό νκ±°λ λ‘κ·Έλ₯Ό λ¨κΈΈ μ μλ μ΄μ μ΄ μλ€.
λ΄λΆ μ μ ν΄λμ€λ‘ MyPointcut μ΄λΌλ ν΄λμ€λ₯Ό μμ±νκ³ Pointcut μΈν°νμ΄μ€λ₯Ό ꡬννλ€.
methodMatcher μΆμ λ©μλλ₯Ό μ€λ²λΌμ΄λνμ¬, ν¬μΈνΈμ»·μ μ μ©ν λ‘μ§μ μ½μ ν μ μλ€.
μλ μ½λλ "save" λΌλ μ΄λ¦μ κ°μ§ λ©μλμ νλ‘μλ₯Ό μ μ©νλλ‘ matchName = "save" λ‘ μ§μ νμλ€.
@Slf4j
public class AdvisorTest {
@Test
@DisplayName("μ§μ λ§λ ν¬μΈνΈμ»·")
void advisorTest2() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
// κΈ°μ‘΄ Pointcut.TRUE -> new MyPointcut()
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(new MyPointcut(), new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find();
}
static class MyPointcut implements Pointcut {
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
} // νμ TRUE λ°ν
@Override
public MethodMatcher getMethodMatcher() {
return new MyMethodMatcher();
}
}
static class MyMethodMatcher implements MethodMatcher {
private String matchName = "save";
@Override
public boolean matches(Method method, Class<?> targetClass) {
boolean result = method.getName().equals(matchName);
log.info("ν¬μΈνΈμ»· νΈμΆ method={} targetClass={}", method.getName(), targetClass);
log.info("ν¬μΈνΈμ»· κ²°κ³Ό result={}", result);
return result;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
}
}
MethodMatcher μΈν°νμ΄μ€
MethodMatcher λ₯Ό μμν MyMethodMatcher ν΄λμ€λ 2κ°μ matches μ isRuntime λ©μλλ₯Ό μ€λ²λΌμ΄λνλ€. μ΄ λ isRuntime
μ΄ false λ₯Ό 리ν΄νλ©΄ 첫λ²μ§Έ matches κ° νΈμΆλκ³ , true μΌ λ μλμ matches(... args) κ° νΈμΆλλ€. μ¦ λμ μΌλ‘ λμ΄μ€λ 맀κ°λ³μλ₯Ό νλ¨νλ λ‘μ§μΌλ‘ μ¬μ©ν μ μλ€.
isRuntime() μ΄ false μΈ κ²½μ°, ν΄λμ€μ μ μ μ 보
λ§ μ¬μ©νκΈ° λλ¬Έμ (runtime = false) , μ€νλ§μ΄ λ΄λΆμμ μΊμ±μ ν΅ν΄ μ±λ₯ ν₯μμ΄ κ°λ₯νμ§λ§, isRuntime() μ΄ true μΈ κ²½μ° λ§€κ°λ³μκ° λμ
μΌλ‘ λ³κ²½λλ€κ³ κ°μ νκΈ° λλ¬Έμ μΊμ±μ νμ§ μλλ€.
κ²°κ³Ό
μ€ν κ²°κ³Όλ₯Ό νμΈνλ©΄, save() λ₯Ό νΈμΆν λ TimeProxy κ° μ μ μ©λκ³ , find() λ₯Ό νΈμΆν λ μ μ©λμ§ μμκ²μ νμΈν μ μλ€.
μ€νλ§μ΄ μ 곡νλ ν¬μΈνΈμ»· - μμ μ½λ
μμμλ μ°λ¦¬κ° μ§μ MethodMatcher λ₯Ό μμλ°μ ν¬μΈνΈμ»·μ κ°λ°νμ§λ§, μ€νλ§μ μ°λ¦¬κ° νμν ν¬μΈνΈμ»·μ μ΄λ―Έ λλΆλΆ μ 곡νλ€.
pointcut.setMappedNames("save")
μ²λΌ νμ€μ μ½λλ‘ ν¬μΈνΈμ»·μ μ§μ ν μ μλ€. μ΄μ²λΌ NameMatchMethodPointcut μ μΈμ€ν΄μ€λ‘ setMappedNames() λ©μλλ₯Ό μ¬μ©νλ©΄ κ°λ¨νκ² λ©μλ μ΄λ¦μ μ΄μ©ν ν¬μΈνΈμ»·μ λ§λ€ μ μλ€.
@Slf4j
public class AdvisorTest {
@Test
@DisplayName("μ€νλ§μ΄ μ 곡νλ ν¬μΈνΈμ»·")
void advisorTest3() {
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("save"); // λ©μλ μ΄λ¦μ΄ save μΈ κ²½μ°μλ§ κ°λ₯νλλ‘.
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find();
}
}
- NameMatchMethodPointcut : λ©μλ μ΄λ¦μ κΈ°λ°μΌλ‘ 맀μΉνλ€. λ΄λΆμμλ PatternMatchUtils λ₯Ό μ¬μ©νλ€. μ) *xxx* νμ©
- JdkRegexpMethodPointcut : JDK μ κ· ννμμ κΈ°λ°μΌλ‘ ν¬μΈνΈμ»·μ 맀μΉνλ€.
- TruePointcut : νμ μ°Έμ λ°ννλ€.
- AnnotationMatchingPointcut : μ λ Έν μ΄μ μΌλ‘ 맀μΉνλ€.
- AspectJExpressionPointcut : aspectJ ννμμΌλ‘ 맀μΉνλ€.
κ°μ₯ μ€μν κ²μ aspectJ ννμ
μμ μ¬λ¬ pointcut μ΅μ
보λ€λ aspectJ λ₯Ό μ€λ¬΄μμ κ°μ₯ λ§μ΄ μ¬μ©νλ€. νΈλ¦¬νκ³ κΈ°λ₯λ κ°μ₯ λ§μ aspectJ ννμμ κΈ°λ°μΌλ‘ μ¬μ©νλ AspectJExpressionPointcut μ μ£Όλ‘ μ¬μ©νκ² λ κ²μ΄λ€. aspectJ ννμκ³Ό μ¬μ© λ°©λ²μ μ€μν΄μ μ΄ν AOPλ₯Ό μ€λͺ
ν λ μμΈν μ€λͺ
νκ² λ€.
μ¬λ¬ μ΄λλ°μ΄μ μ ν¨κ» μ¬μ©
μ΄λλ°μ΄μ λ νλμ ν¬μΈνΈμ»·κ³Ό νλμ μ΄λλ°μ΄μ€λ₯Ό κ°μ§λ€κ³ νλλ°, μ¬λ¬ μ΄λλ°μ΄μ€λ₯Ό νλμ target μ μ μ©νλ κ²½μ°λ κ°λ₯ν κΉ? νλ‘μλ₯Ό μ¬λ¬κ° λ§λ€μ΄ chaining νλ λ°©λ²μ μ μ©ν΄λ³Ό μ μλ€.
κΈ°μ‘΄
public class Before_MultiAdvisorTest {
@Test
@DisplayName("μ¬λ¬κ°μ νλ‘μ μ¬μ©")
void multiAdvisorTest1() {
// client -> proxy2(advisor2) -> proxy1(advisor1) -> target
// νλ‘μ1 μμ±
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory1 = new ProxyFactory(target);
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
proxyFactory1.addAdvisor(advisor1);
ServiceInterface proxy1 = (ServiceInterface) proxyFactory1.getProxy();
//νλ‘μ2 μμ±, target -> proxy1 μ
λ ₯
ProxyFactory proxyFactory2 = new ProxyFactory(proxy1); // target μ΄ μλ proxy1 μ λ£μλ€.
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
proxyFactory2.addAdvisor(advisor2);
ServiceInterface proxy2 = (ServiceInterface) proxyFactory2.getProxy();
//μ€ν
proxy2.save();
}
@Slf4j
static class Advice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("advice1 νΈμΆ");
return invocation.proceed();
}
}
@Slf4j
static class Advice2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("advice2 νΈμΆ");
return invocation.proceed();
}
}
}
λ³κ²½ ν : νλμ νλ‘μμ μ¬λ¬ μ΄λλ°μ΄μ μ¬μ©
μ λ°©λ²μ proxyFactory λ₯Ό μ¬λ¬κ° μμ±νλ λ¬Έμ κ° μλ€. μ¬μ€ μλͺ»λ κ²μ μλμ§λ§, κ΅³μ΄ νλ‘μ ν©ν 리 κ°μ²΄λ₯Ό μ¬λ¬κ° μμ±ν νμκ° μλ€λ μ μ μΈμ§ν΄μΌ νλ€. μ΄λλ°μ΄μ€κ° λ§μμ§ μλ‘, κ·Έλ§νΌ νλ‘μ ν©ν 리λ λμ΄λκΈ° λλ¬Έμ΄λ€.
μλ λ°©λ²μ νλ‘μ ν©ν 리 νλμ μ¬λ¬κ°μ μ΄λλ°μ΄μ λ₯Ό μ μ©ν ννλ€.
public class MultiAdvisorTest {
@Test
@DisplayName("νλμ νλ‘μ, μ¬λ¬ μ΄λλ°μ΄μ ")
void multiAdvisorTest2() {
// κΈ°μ‘΄
// client -> proxy2(advisor2) -> proxy1(advisor1) -> target
// λ³κ²½ ν
// client -> proxy -> advisor2 -> advisor1 -> target
DefaultPointcutAdvisor advisor1 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice1());
DefaultPointcutAdvisor advisor2 = new DefaultPointcutAdvisor(Pointcut.TRUE, new Advice2());
//νλ‘μ1 μμ±
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvisor(advisor2);
proxyFactory.addAdvisor(advisor1);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
//μ€ν
proxy.save();
// νλμ νλ‘μμμ μ¬λ¬ μ΄λλ°μ΄μ λ₯Ό λ±λ‘ν μ μλ€.
// advisor2μμ λ¨Όμ ν¬μΈνΈμ»· μ μ©νκ³ , κ·Έλ€μμ advisor1μμ ν¬μΈνΈμ»· μ μ©νλ€.
// κ²°κ³Όμ μΌλ‘ μ¬λ¬ νλ‘μλ₯Ό μ¬μ©ν λμ λΉκ΅νλ©΄, κ²°κ³Όλ κ°κ³ μ±λ₯μ λ μ’λ€. (νλμ νλ‘μλ§ μ¬μ©ν κ²)
}
@Slf4j
static class Advice1 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("advice1 νΈμΆ");
return invocation.proceed();
}
}
@Slf4j
static class Advice2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
log.info("advice2 νΈμΆ");
return invocation.proceed();
}
}
}
νλμ proxyFactory μ addAdvisor() λ₯Ό λλ² νΈμΆν΄ μλ‘ λ€λ₯Έ advisor λ₯Ό μ μ©ν κ²μ νμΈν μ μλ€. μ€μν μ μ λ±λ‘νλ μμλλ‘ advisor κ° νΈμΆλκΈ° λλ¬Έμ, λ±λ‘ μμλ μ λ νμ¬ κ°λ°ν΄μΌνλ€.
νλ‘μ κ°μ²΄λ νλλ§ μμ±λλ€
λ§μ κ°λ°μλ€μ΄ μ°©κ°ν μ μλ μ§μ μ΄, AOP μ μ© μ λ§νΌ νλ‘μκ° μμ±λλ€κ³ μ€ν΄νλ€. μ€νλ§μ AOP λ₯Ό μ μ©ν λ, μ΅μ νλ₯Ό μ§ννμ¬ μ§κΈμ²λΌ νλ‘μλ₯Ό νλλ§ λ§λ€κ³ , νλμ νλ‘μμ μ¬λ¬ μ΄λλ°μ΄μ λ₯Ό μ μ©νλ€. μ 리νλ©΄ νλμ target μ μ¬λ¬ AOP κ° λμμ μ μ©λμ΄λ, μ€νλ§μ AOP λ νλμ νλ‘μλ§ μμ±νλ€.
'Backend > πΏ Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[Spring] @Aspect μ AOP νΊμ보기 (0) | 2022.06.05 |
---|---|
[Spring] λΉ νμ²λ¦¬κΈ°, Bean Postprocessor (0) | 2022.06.04 |
[Spring] ProxyFactory (0) | 2022.06.03 |
[Spring] λμ νλ‘μ κΈ°μ (3) - CGLIB (0) | 2022.06.02 |
[Spring] λμ νλ‘μ κΈ°μ (2) - JDK DynamicProxy (0) | 2022.06.02 |