[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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 295 additions and 0 deletions

View File

@ -0,0 +1 @@
### Relevant Articles:

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>monkey-patching</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>monkey-patching</name>
<packaging>jar</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>patterns-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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());
}
}

View File

@ -0,0 +1,5 @@
package com.baeldung.monkey.patching.converter;
public interface MoneyConverter {
double convertEURtoUSD(double amount);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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"));
}
}

View File

@ -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);
}
}

View File

@ -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"));
}
}

View File

@ -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"));
}
}

View File

@ -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);
}
}

View File

@ -35,6 +35,7 @@
<module>idd</module>
<module>intercepting-filter</module>
<module>solid</module>
<module>monkey-patching</module>
</modules>
</project>