2022. 6. 2. 12:28γBackend/πΏ Spring
λμ νλ‘μ κΈ°μ μ μΈμ μ¬μ©λ κΉ?
νλ‘μ κΈ°μ μ μ μ©νκΈ° μν¨μΈλ°, μμ λμ
μ΄λΌλ λ¨μ΄κ° μ λΆμλμ§ μκ°ν΄λ³΄μ.
λ§μ½ 100κ°μ ν΄λμ€κ° νλ‘μ μ μ© λμμ΄λΌκ³ νμ. κ·ΈλΌ 100κ°μ ν΄λμ€μ μ μ©ν 100κ°μ νλ‘μλ₯Ό κ°λ³μ μΌλ‘ λ§λ€μ΄μΌν κΉ? λ¨μ λ‘κ·Έλ₯Ό μ μ©νλ κΈ°λ₯μ νλ‘μλΌλ©΄, λ‘μ§μ κ°μλ° μ μ© λμμλ§ μ°¨μ΄κ° μλ κ²μ΄λ€.
μ΄ λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ κ²μ΄ λμ νλ‘μ κΈ°μ
μ΄λ€. λμ νλ‘μ κΈ°μ μ νμ©νλ©΄ κ°λ°μκ° λ§€λ² νλ‘μ ν΄λμ€λ₯Ό λ§λ€μ§ μμλ λλ€. μ΄λ¦λλ‘ νλ‘μ κ°μ²΄λ₯Ό λμ μΌλ‘ λ°νμμ (κ°λ°μ λμ ) λ§λ€μ΄μ€λ€. κ·ΈλΌ μ½λλ₯Ό 보면μ μ΄λ€μμΌλ‘ λμ νλ‘μκ° μμ±λκ³ μ μ©λλμ§ νμΈν΄λ³΄μ. μ°Έκ³ λ‘ JDK λμ νλ‘μλ μΈν°νμ΄μ€
λ₯Ό κΈ°λ°μΌλ‘ λμ νλ‘μλ₯Ό μμ±νκΈ° λλ¬Έμ, μΈν°νμ΄μ€κ° νμλ€.
AImpl κ³Ό BImpl μ κ°κ° AInterface μ BInterfaceλ₯Ό ꡬννλ€. λ μΈν°νμ΄μ€ λͺ¨λ call() μΆμ λ©μλλ₯Ό κ°λλ€.
// AInterface
public interface AInterface {
String call();
}
@Slf4j
public class AImpl implements AInterface {
@Override
public String call() {
log.info("A νΈμΆ");
return "a";
}
}
// BInterface
public interface BInterface {
String call();
}
@Slf4j
public class BImpl implements BInterface {
@Override
public String call() {
log.info("B νΈμΆ");
return "b";
}
}
InvocationHandler
JDK λμ νλ‘μκ° μ 곡νλ InvocationHander λ₯Ό ꡬννκΈ° μν΄μ InvocationHandler μΈν°νμ΄μ€λ₯Ό ꡬννλ©΄ λλ€.
μλ InvocationHandler μΈν°νμ΄μ€λ JDKμμ μ 곡νλ κΈ°λ³Έ ν¨ν€μ§λ€.
package java.lang.reflect; // μλ°μμ κΈ°λ³ΈμΌλ‘ μ 곡νλ reflect ν¨ν€μ§
public interface InvocationHandler {
Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
}
TimeInvocationHandler
InvocationHander μΈν°νμ΄μ€λ₯Ό ꡬννλ€. μμ±μλ‘ νλ‘μλ₯Ό μ μ©ν λμμΈ target κ°μ²΄λ₯Ό μ£Όμ λ°λλ€. νλ‘μλ λ리μλΌλ μλ―Έλ₯Ό κ°μ§κΈ°μ νμ μλ³Έ target μ΄ νμνλ€κ³ μ΄ν΄ν μ μλ€.
@Slf4j // μ€ν μκ°μ 체ν¬νκΈ° μν λ‘κ·Έ -> μ΄κ±Έ λμ ν΄λμ€μ μ½μ
νκ² λ€.
public class TimeInvocationHandler implements InvocationHandler { // JDK λμ νλ‘μμ μ μ©ν κ³΅ν΅ λ‘μ§μ λ΄λ ν΄λμ€
private final Object target; // Proxy λ νμ νλ‘μκ° νΈμΆν λμμ΄ νμ
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override // Proxy κ°μ²΄κ° call νλ©΄ μ¬κΈ°(invoke)κ° μνλ¨
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("TimeProxy μ€ν");
long startTime = System.currentTimeMillis();
// λ©μλλ₯Ό νΈμΆνλ λΆλΆμ΄ λμ μ΄λ€ => μ΄λ ν ν΄λμ€λ κ·Έμ μνλ λ©μλλ₯Ό λ€ μ¬μ©ν μ μλ€λ λ»
Object result = method.invoke(target, args); // AImpl or BImpl μ call() μ€ν. κ²°κ³Όλ₯Ό result μ λ΄μ.
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy μ’
λ£ resultTime={}", resultTime);
return result;
}
}
ν μ€νΈ μ½λλ‘ TimeInvocationHandler λ₯Ό μ€μ κ°μ²΄μ μ μ©ν΄λ³΄μ.
@Slf4j
public class JdkDynamicProxyTest {
@Test
void dynamicA() {
AInterface target = new AImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
// handler : λμ νλ‘μμ μ μ©ν λ‘μ§μ΄ λ€λ€.
// Interface κ° μ¬λ¬κ° μμ μ μμΌλ, λ°°μ΄λ‘ λ°λλ€.
AInterface proxy = (AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler);
// λμ μΌλ‘ νλ‘μ κ°μ²΄λ₯Ό μμ±ν΄λΈλ€.
// client λ JDK λμ νλ‘μμ call μ μ€ν
proxy.call(); // AInterface μ call() λ©μλ μ‘΄μ¬
// call() νλ©΄ proxy λ handler μ λ‘μ§μ μννλ€.
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
}
@Test
void dynamicB() {
BInterface target = new BImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
BInterface proxy = (BInterface) Proxy.newProxyInstance(BInterface.class.getClassLoader(), new Class[]{BInterface.class}, handler);
proxy.call();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
}
}
new TimeInvocationHandler(target)
: λμ νλ‘μμ μ μ©ν νΈλ€λ¬ λ‘μ§μ 리ν΄νλ€.Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler)
- λμ νλ‘μλ java.lang.reflect.Proxy λ₯Ό ν΅ν΄ μμ±ν μ μλ€.
- μΈμλ‘ 1) ν΄λμ€ λ‘λ μ 보, 2) μΈν°νμ΄μ€, 3) νΈλ€λ¬ λ‘μ§μ λ£μ΄μ£Όλ©΄ λλ€. ν΄λΉ μΈν°νμ΄μ€λ₯Ό κΈ°λ°μΌλ‘ λμ νλ‘μλ₯Ό μμ±νκ³ κ·Έ κ²°κ³Όλ₯Ό λ°ννλ€.
dynamicA() ν μ€νΈ κ²°κ³Ό
μ€ν μμ
1. ν΄λΌμ΄μΈνΈλ JDK λμ νλ‘μμ call() μ μ€ννλ€. μ΄ call() μ AInterface / Binterface μ μ μΈλ μΆμ λ©μλλ₯Ό μλ―Ένλ€.
νμ§λ§ νλ‘μμ call() λ©μλλ₯Ό νΈμΆνμΌλ―λ‘, AImpl, BImplμ ꡬνλ call() λ©μλλ₯Ό λ°λ‘ νΈμΆνμ§ μκ³ , proxy κ°μ²΄μ call() μ νΈμΆνλ€.
2. InvocationHandler μ invoke() λ©μλλ₯Ό νΈμΆνλλ°, TimeInvocationHandler
κ° κ΅¬νμ²΄λ‘ μμΌλ―λ‘ TimeInvocationHandler.invoke()
κ° νΈμΆλλ€.
3. TimeInvocationHandler μ μ μλ λ΄λΆ λ‘μ§μ μννκ³ , method.invoke(target, args)
λ₯Ό νΈμΆν΄μ targetμ ν΄λΉλλ μ€μ κ°μ²΄(AImpl / BImpl) μ νΈμΆνλ€.
4. AImpl / BImpl μ call() λ©μλκ° μ€νλλ€.
5. call() λ©μλκ° μ’ λ£λλ©΄ λ€μ TimeInvocationHandler λ‘ μλ΅μ΄ λμμ€κ³ , μκ° λ‘κ·Έλ₯Ό μΆλ ₯νμ¬ κ²°κ³Όλ₯Ό λ°ννλ€.
μ€κ° μ 리
JDK λμ νλ‘μ κΈ°μ λλΆμ, AImpl, BImpl κ°κ°μ νλ‘μλ₯Ό λ§λ€μ§ μμλ, νλ‘μ κΈ°μ μ μ μ©ν μ μμλ€. μλμμ μΈκΈν 100κ°μ ν΄λμ€κ° μ‘΄μ¬ν΄λ, λμ νλ‘μ κΈ°μ μ μ μ©νλ©΄ SRP λ₯Ό μ§ν€λ©΄μ νλ‘μλ₯Ό μ μ©ν μ μλ€.
JDK λμ νλ‘μ - νν°
λ‘κ·Έλ₯Ό λ¨κΈ°λ νλ‘μλ₯Ό μ μ©ν λ, νΉμ λ©μλμλ λ‘κ·Έ κΈ°λ₯μ μ μΈνκ³ μΆμ μ μλ€. κ·Έλ° κ²½μ°, PatternMatchUtils.simpleMatch() κ°μ λ§€μΉ λ‘μ§μ μ μ©νμ¬ κ³΅ν΅ λ‘μ§(λ‘κΉ )μ μ μΈν μ μλ€. FilterInvocationHandler λ if λ¬Έμμ λΆκΈ°μ²λ¦¬νμ¬ λ‘κ·Έλ₯Ό μ μ©ν λμμ κ±°λ₯Έλ€.
@Slf4j
public class FilterInvocationHandler implements InvocationHandler {
private final Object target;
private final String[] patterns;
public FilterInvocationHandler(Object target, String[] patterns) {
this.target = target;
this.patterns = patterns;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (!PatternMatchUtils.simpleMatch(patterns, methodName)) {
return method.invoke(target, args);
}
log.info("TimeProxy μ€ν");
long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy μ’
λ£ resultTime={}", resultTime);
return result;
}
}
// AImpl λ μλμ κ°μ ꡬ쑰λ₯Ό κ°μ§λ©°, λ‘κ·Έ λ΄μ©λ§ λ€λ₯΄λ€.
@Slf4j
public class BImpl implements BInterface {
@Override
public String call() {
log.info("B νΈμΆ");
return "b";
}
@Override
public String filterCall() {
log.info("B νν°");
return "filter";
}
}
@Slf4j
public class JDKDynamicProxyFilterTest {
public static final String[] PATTERNS = {"call*"};
@Test
void dynamicA() {
AInterface target = new AImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
AInterface proxy = (AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler);
proxy.call();
proxy.filterCall();
}
@Test
void dynamicB() {
BInterface target = new BImpl();
FilterInvocationHandler handler = new FilterInvocationHandler(target, PATTERNS); // λμ νλ‘μμ μ μ©ν λ‘μ§μ΄ λ€λ€.
BInterface proxy = (BInterface) Proxy.newProxyInstance(BInterface.class.getClassLoader(), new Class[]{BInterface.class}, handler);
proxy.call();
proxy.filterCall();
}
}
dynamicA() λ©μλμμλ κΈ°μ‘΄ TimeInvocationHandler() λ₯Ό νΈμΆνλ€. proxy.call(), proxy.filterCall() λ©μλ λͺ¨λ time logging μ΄ ν¨κ» μΆλ ₯λλ κ²μ λ³Ό μ μλ€.
dynamicB() λ©μλμμλ, νν°μ²λ¦¬λ₯Ό ν FilterInvocationHandler() λ₯Ό νΈμΆνκ³ , PATTERNS κΉμ§ μμ±μ μ£Όμ μ λ°λλ€. κ·Έ κ²°κ³Ό proxy.call() μμλ νμ λ‘κΉ μ΄ μ°νμ§λ§, proxy.filterCall() μμλ νμ λ‘κΉ μ΄ μ°νμ§ μλ κ²μ νμΈν μ μλ€.
μ 리
JDK λμ νλ‘μλ μΈν°νμ΄μ€κ° νμλ€. μ¦ κ΅¬ν체λ‘λ§ μ΄λ£¨μ΄μ§ ν΄λμ€μλ λμ νλ‘μλ₯Ό μ μ©ν μ μλ€λ λ»μΈλ°, μ΄λ° κ²½μ°μλ CGLIB λΌλ λ°μ΄νΈμ½λ μ‘°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©ν΄μΌνλ€. λ€μ ν¬μ€ν μμ CGLIB λ₯Ό νμ©ν λμ νλ‘μλ₯Ό μ μ©ν΄λ³΄μ .
μ½λ λ° μλ£ μΆμ²
'Backend > πΏ Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[Spring] ProxyFactory (0) | 2022.06.03 |
---|---|
[Spring] λμ νλ‘μ κΈ°μ (3) - CGLIB (0) | 2022.06.02 |
[Spring] λμ νλ‘μ κΈ°μ (1) - 리νλ μ (0) | 2022.06.02 |
[Spring] μμ λ‘ μμ보λ [ μ λ΅ ν¨ν΄ ] (0) | 2022.05.31 |
[Spring] μμ λ‘ μμ보λ [ ν νλ¦Ώ λ©μλ ν¨ν΄ ] (0) | 2022.05.31 |