diff --git a/core-java-modules/core-java-lang-oop-patterns-2/pom.xml b/core-java-modules/core-java-lang-oop-patterns-2/pom.xml
index 601b41d07ee4..9477b78b88f4 100644
--- a/core-java-modules/core-java-lang-oop-patterns-2/pom.xml
+++ b/core-java-modules/core-java-lang-oop-patterns-2/pom.xml
@@ -12,6 +12,10 @@
com.baeldung.core-java-modules
0.0.1-SNAPSHOT
+
+
+ logback.xml
+
@@ -24,6 +28,27 @@
jackson-databind
${jackson.version}
+
+ org.slf4j
+ slf4j-api
+ 2.0.7
+
+
+ ch.qos.logback
+ logback-classic
+ 1.4.11
+ runtime
+
+
+ org.springframework
+ spring-aop
+ 6.0.11
+
+
+ org.springframework
+ spring-context
+ 6.0.11
+
-
\ No newline at end of file
+
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/Calculator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/Calculator.java
new file mode 100644
index 000000000000..5db060fa4c82
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/Calculator.java
@@ -0,0 +1,6 @@
+package com.baeldung.overridemethod;
+
+public interface Calculator {
+ int add(int a, int b);
+ int subtract(int a, int b);
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/SimpleCalculator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/SimpleCalculator.java
new file mode 100644
index 000000000000..7aa57787a671
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/SimpleCalculator.java
@@ -0,0 +1,20 @@
+package com.baeldung.overridemethod;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SimpleCalculator implements Calculator {
+ private static final Logger log = LoggerFactory.getLogger(SimpleCalculator.class);
+
+ @Override
+ public int add(int a, int b) {
+ log.info("SimpleCalculator: Adding {} and {}", a, b); // Use parameterized logging {}
+ return a + b;
+ }
+
+ @Override
+ public int subtract(int a, int b) {
+ log.info("SimpleCalculator: Subtracting {} from {}", b, a);
+ return a - b;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/decorator/MeteredCalculatorDecorator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/decorator/MeteredCalculatorDecorator.java
new file mode 100644
index 000000000000..af94bc02862a
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/decorator/MeteredCalculatorDecorator.java
@@ -0,0 +1,37 @@
+package com.baeldung.overridemethod.decorator;
+
+import com.baeldung.overridemethod.Calculator;
+import java.util.HashMap;
+import java.util.Map;
+
+public class MeteredCalculatorDecorator implements Calculator {
+ private final Calculator wrappedCalculator;
+ private final Map methodCalls;
+
+ public MeteredCalculatorDecorator(Calculator calculator) {
+ this.wrappedCalculator = calculator;
+ this.methodCalls = new HashMap<>();
+ // Initialize counts for clarity
+ methodCalls.put("add", 0);
+ methodCalls.put("subtract", 0);
+ }
+
+ @Override
+ public int add(int a, int b) {
+ // Track the call count
+ methodCalls.merge("add", 1, Integer::sum);
+ return wrappedCalculator.add(a, b); // Delegation
+ }
+
+ @Override
+ public int subtract(int a, int b) {
+ // Track the call count
+ methodCalls.merge("subtract", 1, Integer::sum);
+ return wrappedCalculator.subtract(a, b); // Delegation
+ }
+
+ // Public method to expose the call counts for testing
+ public int getCallCount(String methodName) {
+ return methodCalls.getOrDefault(methodName, 0);
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/jdk/LoggingInvocationHandler.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/jdk/LoggingInvocationHandler.java
new file mode 100644
index 000000000000..06e33383aaf3
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/jdk/LoggingInvocationHandler.java
@@ -0,0 +1,27 @@
+package com.baeldung.overridemethod.proxy.jdk;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoggingInvocationHandler implements InvocationHandler {
+ private static final Logger log = LoggerFactory.getLogger(LoggingInvocationHandler.class);
+ private final Object target;
+
+ public LoggingInvocationHandler(Object target) {
+ this.target = target;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+
+ log.debug("PROXY LOG: Intercepting method: {}", method.getName());
+
+ Object result = method.invoke(target, args);
+
+ log.debug("PROXY LOG: Method {} executed.", method.getName());
+
+ return result;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/spring/LoggingMethodInterceptor.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/spring/LoggingMethodInterceptor.java
new file mode 100644
index 000000000000..c55e274ae3dd
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/spring/LoggingMethodInterceptor.java
@@ -0,0 +1,22 @@
+package com.baeldung.overridemethod.proxy.spring;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoggingMethodInterceptor implements MethodInterceptor {
+ private static final Logger log = LoggerFactory.getLogger(LoggingMethodInterceptor.class);
+
+ @Override
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+
+ log.debug("SPRING PROXY: Intercepting method: {}", invocation.getMethod().getName());
+
+ Object result = invocation.proceed();
+
+ log.debug("SPRING PROXY: Method {} completed.", invocation.getMethod().getName());
+
+ return result;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/subclass/LoggingCalculator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/subclass/LoggingCalculator.java
new file mode 100644
index 000000000000..e89c870ad1da
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/subclass/LoggingCalculator.java
@@ -0,0 +1,25 @@
+package com.baeldung.overridemethod.subclass;
+
+import com.baeldung.overridemethod.SimpleCalculator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LoggingCalculator extends SimpleCalculator {
+ private static final Logger log = LoggerFactory.getLogger(LoggingCalculator.class);
+
+ @Override
+ public int add(int a, int b) {
+ log.debug("LOG: Before addition.");
+ int result = super.add(a, b);
+ log.debug("LOG: After addition. Result: {}", result);
+ return result;
+ }
+
+ @Override
+ public int subtract(int a, int b) {
+ log.debug("LOG: Before subtraction.");
+ int result = super.subtract(a, b);
+ log.debug("LOG: After subtraction. Result: {}", result);
+ return result;
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/resources/logback.xml b/core-java-modules/core-java-lang-oop-patterns-2/src/main/resources/logback.xml
new file mode 100644
index 000000000000..bd3dce1b11e3
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/resources/logback.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/decorator/DecoratorPatternTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/decorator/DecoratorPatternTest.java
new file mode 100644
index 000000000000..1de5af0ab5bc
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/decorator/DecoratorPatternTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.overridemethod.decorator;
+
+import com.baeldung.overridemethod.Calculator;
+import com.baeldung.overridemethod.SimpleCalculator;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DecoratorPatternTest {
+
+ @Test
+ void givenACalculator_whenUsingMeteredDecorator_thenMethodCallsAreCountedCorrectly() {
+ // ARRANGE
+ Calculator simpleCalc = new SimpleCalculator();
+
+ // Use the MeteredCalculatorDecorator decorator
+ MeteredCalculatorDecorator decoratedCalc = new MeteredCalculatorDecorator(simpleCalc);
+
+ // ACT
+ // Call add twice
+ decoratedCalc.add(10, 5);
+ decoratedCalc.add(2, 3);
+
+ // Call subtract once
+ decoratedCalc.subtract(10, 5);
+
+ // ASSERT Core Functionality (optional, but good practice)
+ assertEquals(15, decoratedCalc.add(10, 5), "Core functionality must still work.");
+
+ // ASSERT the call counts
+ // 1. Assert 'add' was called 3 times (2 from ACT + 1 from ASSERT Core)
+ assertEquals(3, decoratedCalc.getCallCount("add"), "The 'add' method should have been called 3 times.");
+
+ // 2. Assert 'subtract' was called 1 time
+ assertEquals(1, decoratedCalc.getCallCount("subtract"), "The 'subtract' method should have been called 1 time.");
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/jdk/DynamicProxyTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/jdk/DynamicProxyTest.java
new file mode 100644
index 000000000000..248af8edba13
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/jdk/DynamicProxyTest.java
@@ -0,0 +1,26 @@
+package com.baeldung.overridemethod.proxy.jdk;
+
+import com.baeldung.overridemethod.Calculator;
+import com.baeldung.overridemethod.SimpleCalculator;
+import org.junit.jupiter.api.Test;
+import java.lang.reflect.Proxy;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class DynamicProxyTest {
+
+ @Test
+ void givenACalculator_whenUsingJdkDynamicProxy_thenJdkDynamicProxyCanBeUsed() {
+ Calculator simpleCalc = new SimpleCalculator();
+ LoggingInvocationHandler handler = new LoggingInvocationHandler(simpleCalc);
+
+ Calculator proxyCalc = (Calculator) Proxy.newProxyInstance(
+ Calculator.class.getClassLoader(),
+ new Class>[] { Calculator.class },
+ handler
+ );
+
+ assertEquals(30, proxyCalc.add(20, 10));
+ assertEquals(10, proxyCalc.subtract(20, 10));
+ }
+}
+
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/spring/SpringProxyFactoryTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/spring/SpringProxyFactoryTest.java
new file mode 100644
index 000000000000..0438e1f58f47
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/spring/SpringProxyFactoryTest.java
@@ -0,0 +1,24 @@
+package com.baeldung.overridemethod.proxy.spring;
+
+import com.baeldung.overridemethod.Calculator;
+import com.baeldung.overridemethod.SimpleCalculator;
+import org.junit.jupiter.api.Test;
+import org.springframework.aop.framework.ProxyFactory;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SpringProxyFactoryTest {
+
+ @Test
+ void givenACalculator_whenUsingSpringProxyFactory_thenSpringProxyFactoryCanBeUsed() {
+ SimpleCalculator simpleCalc = new SimpleCalculator();
+ ProxyFactory factory = new ProxyFactory();
+
+ factory.setTarget(simpleCalc);
+ factory.addAdvice(new LoggingMethodInterceptor());
+
+ Calculator proxyCalc = (Calculator) factory.getProxy();
+
+ assertEquals(60, proxyCalc.add(50, 10));
+ assertEquals(40, proxyCalc.subtract(50, 10));
+ }
+}
diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/subclass/SubclassingTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/subclass/SubclassingTest.java
new file mode 100644
index 000000000000..6718b4da9198
--- /dev/null
+++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/subclass/SubclassingTest.java
@@ -0,0 +1,15 @@
+package com.baeldung.overridemethod.subclass;
+
+import com.baeldung.overridemethod.Calculator;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SubclassingTest {
+
+ @Test
+ void givenACalculatorClass_whenSubclassingToAddLogging_thenLoggingCalculatorCanBeUsed() {
+ Calculator calculator = new LoggingCalculator();
+ assertEquals(8, calculator.add(5, 3));
+ assertEquals(2, calculator.subtract(5, 3));
+ }
+}