[BAEL-7257] Monkey-Patching in Java (#15700)

This commit is contained in:
ovidiumihaitacu
2024-01-22 20:09:24 +02:00
committed by GitHub
parent 7ff914a1b3
commit f6806580ae
15 changed files with 295 additions and 0 deletions
@@ -0,0 +1,12 @@
package com.baeldung.monkey.patching;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@@ -0,0 +1,15 @@
package com.baeldung.monkey.patching.aop;
import com.baeldung.monkey.patching.converter.MoneyConverter;
import com.baeldung.monkey.patching.converter.MoneyConverterImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean
public MoneyConverter moneyConverter() {
return new MoneyConverterImpl();
}
}
@@ -0,0 +1,22 @@
package com.baeldung.monkey.patching.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.baeldung.monkey.patching.converter.MoneyConverter.convertEURtoUSD(..))")
public void beforeConvertEURtoUSD(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
@After("execution(* com.baeldung.monkey.patching.converter.MoneyConverter.convertEURtoUSD(..))")
public void afterConvertEURtoUSD(JoinPoint joinPoint) {
System.out.println("After method: " + joinPoint.getSignature().getName());
}
}
@@ -0,0 +1,5 @@
package com.baeldung.monkey.patching.converter;
public interface MoneyConverter {
double convertEURtoUSD(double amount);
}
@@ -0,0 +1,15 @@
package com.baeldung.monkey.patching.converter;
public class MoneyConverterImpl implements MoneyConverter {
private final double conversionRate;
public MoneyConverterImpl() {
this.conversionRate = 1.10;
}
@Override
public double convertEURtoUSD(double amount) {
return amount * conversionRate;
}
}
@@ -0,0 +1,21 @@
package com.baeldung.monkey.patching.decorator;
import com.baeldung.monkey.patching.converter.MoneyConverter;
public class MoneyConverterDecorator implements MoneyConverter {
private final MoneyConverter moneyConverter;
public MoneyConverterDecorator(MoneyConverter moneyConverter) {
this.moneyConverter = moneyConverter;
}
@Override
public double convertEURtoUSD(double amount) {
System.out.println("Before method: convertEURtoUSD");
double result = moneyConverter.convertEURtoUSD(amount);
System.out.println("After method: convertEURtoUSD");
return result;
}
}
@@ -0,0 +1,21 @@
package com.baeldung.monkey.patching.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingInvocationHandler implements InvocationHandler {
private final Object target;
public LoggingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
@@ -0,0 +1,35 @@
package com.baeldung.monkey.patching.aop;
import com.baeldung.monkey.patching.converter.MoneyConverter;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import static org.junit.Assert.assertTrue;
@SpringBootTest
@RunWith(SpringRunner.class)
public class LoggingAspectIntegrationTest {
@Autowired
private MoneyConverter moneyConverter;
@Test
public void whenMethodCalled_thenSurroundedByLogs() {
ByteArrayOutputStream logOutputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(logOutputStream));
double result = moneyConverter.convertEURtoUSD(10);
Assertions.assertEquals(11, result);
String logOutput = logOutputStream.toString();
assertTrue(logOutput.contains("Before method: convertEURtoUSD"));
assertTrue(logOutput.contains("After method: convertEURtoUSD"));
}
}
@@ -0,0 +1,17 @@
package com.baeldung.monkey.patching.converter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class MoneyConverterUnitTest {
@Test
void whenMoneyConverter_thenResultIsCorrect() {
MoneyConverterImpl moneyConverter = new MoneyConverterImpl();
double result = moneyConverter.convertEURtoUSD(10);
assertEquals(11, result);
}
}
@@ -0,0 +1,28 @@
package com.baeldung.monkey.patching.decorator;
import com.baeldung.monkey.patching.converter.MoneyConverter;
import com.baeldung.monkey.patching.converter.MoneyConverterImpl;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import static org.junit.Assert.assertTrue;
public class MoneyConverterDecoratorUnitTest {
@Test
public void whenMethodCalled_thenSurroundedByLogs() {
ByteArrayOutputStream logOutputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(logOutputStream));
MoneyConverter moneyConverter = new MoneyConverterDecorator(new MoneyConverterImpl());
double result = moneyConverter.convertEURtoUSD(10);
Assertions.assertEquals(11, result);
String logOutput = logOutputStream.toString();
assertTrue(logOutput.contains("Before method: convertEURtoUSD"));
assertTrue(logOutput.contains("After method: convertEURtoUSD"));
}
}
@@ -0,0 +1,34 @@
package com.baeldung.monkey.patching.proxy;
import com.baeldung.monkey.patching.converter.MoneyConverter;
import com.baeldung.monkey.patching.converter.MoneyConverterImpl;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Proxy;
import static org.junit.Assert.assertTrue;
public class LoggingInvocationHandlerUnitTest {
@Test
public void whenMethodCalled_thenSurroundedByLogs() {
ByteArrayOutputStream logOutputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(logOutputStream));
MoneyConverter moneyConverter = new MoneyConverterImpl();
MoneyConverter proxy = (MoneyConverter) Proxy.newProxyInstance(
MoneyConverter.class.getClassLoader(),
new Class[]{MoneyConverter.class},
new LoggingInvocationHandler(moneyConverter)
);
double result = proxy.convertEURtoUSD(10);
Assertions.assertEquals(11, result);
String logOutput = logOutputStream.toString();
assertTrue(logOutput.contains("Before method: convertEURtoUSD"));
assertTrue(logOutput.contains("After method: convertEURtoUSD"));
}
}
@@ -0,0 +1,24 @@
package com.baeldung.monkey.patching.reflection;
import com.baeldung.monkey.patching.converter.MoneyConverter;
import com.baeldung.monkey.patching.converter.MoneyConverterImpl;
import org.junit.Test;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ReflectionUnitTest {
@Test
public void givenPrivateField_whenUsingReflection_thenBehaviorCanBeChanged() throws IllegalAccessException, NoSuchFieldException {
MoneyConverter moneyConvertor = new MoneyConverterImpl();
Field conversionRate = MoneyConverterImpl.class.getDeclaredField("conversionRate");
conversionRate.setAccessible(true);
conversionRate.set(moneyConvertor, 1.2);
double result = moneyConvertor.convertEURtoUSD(10);
assertEquals(12, result);
}
}