From 51bdbe2fcb81c558ae1269a311fc6d7cf80bf585 Mon Sep 17 00:00:00 2001 From: ahucoder <1095765855@qq.com> Date: Fri, 6 Jun 2025 22:00:05 +0800 Subject: [PATCH] The Proxy Pattern --- pom.xml | 12 +++-- src/main/java/proxy/dynamicproxy/App.java | 25 +++++++++ .../OrderServiceCglibProxyFactory.java | 50 ++++++++++++++++++ .../OrderServiceJdkProxyFactory.java | 50 ++++++++++++++++++ .../OrderServiceProxyProcess.java | 40 ++++++++++++++ src/main/java/proxy/service/AuthService.java | 5 ++ src/main/java/proxy/service/LogService.java | 5 ++ src/main/java/proxy/service/OrderService.java | 9 ++++ .../proxy/service/impl/AuthServiceImpl.java | 11 ++++ .../proxy/service/impl/LogServiceImpl.java | 12 +++++ .../proxy/service/impl/OrderServiceImpl.java | 19 +++++++ src/main/java/proxy/staticproxy/App.java | 14 +++++ .../staticproxy/OrderServiceStaticProxy.java | 52 +++++++++++++++++++ .../DoubleCheckedLockingSingleton.java | 2 +- .../EnumSingleton.java | 2 +- .../StaticInnerClassSingleton.java | 2 +- .../DoubleCheckedLockingSingletonTest.java | 4 +- 17 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 src/main/java/proxy/dynamicproxy/App.java create mode 100644 src/main/java/proxy/dynamicproxy/OrderServiceCglibProxyFactory.java create mode 100644 src/main/java/proxy/dynamicproxy/OrderServiceJdkProxyFactory.java create mode 100644 src/main/java/proxy/dynamicproxy/OrderServiceProxyProcess.java create mode 100644 src/main/java/proxy/service/AuthService.java create mode 100644 src/main/java/proxy/service/LogService.java create mode 100644 src/main/java/proxy/service/OrderService.java create mode 100644 src/main/java/proxy/service/impl/AuthServiceImpl.java create mode 100644 src/main/java/proxy/service/impl/LogServiceImpl.java create mode 100644 src/main/java/proxy/service/impl/OrderServiceImpl.java create mode 100644 src/main/java/proxy/staticproxy/App.java create mode 100644 src/main/java/proxy/staticproxy/OrderServiceStaticProxy.java rename src/main/java/{Singleton => singleton}/DoubleCheckedLockingSingleton.java (98%) rename src/main/java/{Singleton => singleton}/EnumSingleton.java (85%) rename src/main/java/{Singleton => singleton}/StaticInnerClassSingleton.java (94%) diff --git a/pom.xml b/pom.xml index 04e965c..671370c 100644 --- a/pom.xml +++ b/pom.xml @@ -14,24 +14,28 @@ 21 UTF-8 5.8.2 + 1.18.36 + 3.4.5 + 33.4.0-jre + 3.12.0 org.projectlombok lombok - 1.18.36 + ${lombok.version} provided org.springframework.boot spring-boot-starter-web - 3.4.5 + ${spring-boot-starter-web.version} com.google.guava guava - 33.4.0-jre + ${guava.version} @@ -49,7 +53,7 @@ org.apache.commons commons-lang3 - 3.12.0 + ${commons-lang3.version} test diff --git a/src/main/java/proxy/dynamicproxy/App.java b/src/main/java/proxy/dynamicproxy/App.java new file mode 100644 index 0000000..1d85359 --- /dev/null +++ b/src/main/java/proxy/dynamicproxy/App.java @@ -0,0 +1,25 @@ +package proxy.dynamicproxy; + +import proxy.service.OrderService; + +import java.math.BigDecimal; + +public class App { + + public static void main(String[] args) { + // jdk dynamic proxy + OrderService proxy = new OrderServiceJdkProxyFactory().createProxy(); + proxy.createOrder("user123", "prod456", new BigDecimal("199.99")); + System.out.println(); + proxy.cancelOrder("order789"); + + System.out.println("========================================================"); + + // cglib dynamic proxy + proxy = new OrderServiceCglibProxyFactory().createProxy(); + proxy.createOrder("user123", "prod456", new BigDecimal("199.99")); + System.out.println(); + proxy.cancelOrder("order789"); + } + +} diff --git a/src/main/java/proxy/dynamicproxy/OrderServiceCglibProxyFactory.java b/src/main/java/proxy/dynamicproxy/OrderServiceCglibProxyFactory.java new file mode 100644 index 0000000..5554036 --- /dev/null +++ b/src/main/java/proxy/dynamicproxy/OrderServiceCglibProxyFactory.java @@ -0,0 +1,50 @@ +package proxy.dynamicproxy; + +import org.springframework.cglib.proxy.Enhancer; +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; +import proxy.service.AuthService; +import proxy.service.LogService; +import proxy.service.OrderService; +import proxy.service.impl.AuthServiceImpl; +import proxy.service.impl.LogServiceImpl; +import proxy.service.impl.OrderServiceImpl; + +import java.lang.reflect.Method; + +public class OrderServiceCglibProxyFactory { + private final OrderService orderService; + private final AuthService authService; + private final LogService logService; + private final OrderServiceProxyProcess orderServiceProxyProcess; + + public OrderServiceCglibProxyFactory() { + this.orderService = new OrderServiceImpl(); + this.authService = new AuthServiceImpl(); + this.logService = new LogServiceImpl(); + this.orderServiceProxyProcess = new OrderServiceProxyProcess(); + } + + public OrderService createProxy() { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(orderService.getClass()); + enhancer.setCallback(new OrderServiceMethodInterceptor()); + return (OrderService) enhancer.create(); + } + + private class OrderServiceMethodInterceptor implements MethodInterceptor { + @Override + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + // before + orderServiceProxyProcess.processBefore(method, args, authService, logService); + + // invoke real objects + Object result = proxy.invokeSuper(obj, args); + + // after + orderServiceProxyProcess.processAfter(method, args, authService, logService); + + return result; + } + } +} diff --git a/src/main/java/proxy/dynamicproxy/OrderServiceJdkProxyFactory.java b/src/main/java/proxy/dynamicproxy/OrderServiceJdkProxyFactory.java new file mode 100644 index 0000000..8b722cb --- /dev/null +++ b/src/main/java/proxy/dynamicproxy/OrderServiceJdkProxyFactory.java @@ -0,0 +1,50 @@ +package proxy.dynamicproxy; + +import proxy.service.AuthService; +import proxy.service.LogService; +import proxy.service.OrderService; +import proxy.service.impl.AuthServiceImpl; +import proxy.service.impl.LogServiceImpl; +import proxy.service.impl.OrderServiceImpl; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +public class OrderServiceJdkProxyFactory { + private final OrderService orderService; + private final AuthService authService; + private final LogService logService; + private final OrderServiceProxyProcess orderServiceProxyProcess; + + public OrderServiceJdkProxyFactory() { + this.orderService = new OrderServiceImpl(); + this.authService = new AuthServiceImpl(); + this.logService = new LogServiceImpl(); + this.orderServiceProxyProcess = new OrderServiceProxyProcess(); + } + + public OrderService createProxy() { + return (OrderService) Proxy.newProxyInstance( + orderService.getClass().getClassLoader(), + orderService.getClass().getInterfaces(), + new OrderServiceInvocationHandler() + ); + } + + private class OrderServiceInvocationHandler implements InvocationHandler { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + // before + orderServiceProxyProcess.processBefore(method, args, authService, logService); + + // invoke real objects + Object result = method.invoke(orderService, args); + + // after + orderServiceProxyProcess.processAfter(method, args, authService, logService); + + return result; + } + } +} diff --git a/src/main/java/proxy/dynamicproxy/OrderServiceProxyProcess.java b/src/main/java/proxy/dynamicproxy/OrderServiceProxyProcess.java new file mode 100644 index 0000000..cd9f294 --- /dev/null +++ b/src/main/java/proxy/dynamicproxy/OrderServiceProxyProcess.java @@ -0,0 +1,40 @@ +package proxy.dynamicproxy; + +import proxy.service.AuthService; +import proxy.service.LogService; + +import java.lang.reflect.Method; + +public class OrderServiceProxyProcess { + + public void processBefore(Method method, Object[] args, AuthService authService, LogService logService) { + String methodName = method.getName(); + if ("createOrder".equals(methodName)) { + String userId = (String) args[0]; + authService.checkPermission(userId, "CREATE_ORDER"); + logService.log("Begin to create order", userId, (String) args[1], args[2].toString()); + } else if ("cancelOrder".equals(methodName)) { + String orderId = (String) args[0]; + String userId = getUserIdByOrderId(orderId); + authService.checkPermission(userId, "CANCEL_ORDER"); + logService.log("Start canceling orders", userId, orderId); + } + } + + public void processAfter(Method method, Object[] args, AuthService authService, LogService logService) { + String methodName = method.getName(); + if ("createOrder".equals(methodName)) { + String userId = (String) args[0]; + logService.log("Order creation completed", userId, (String) args[1], args[2].toString()); + } else if ("cancelOrder".equals(methodName)) { + String orderId = (String) args[0]; + String userId = getUserIdByOrderId(orderId); + logService.log("Order cancellation completed", userId, orderId); + } + } + + private String getUserIdByOrderId(String orderId) { + return STR."user_\{orderId.hashCode()}"; // Mock + } + +} diff --git a/src/main/java/proxy/service/AuthService.java b/src/main/java/proxy/service/AuthService.java new file mode 100644 index 0000000..5c5e96b --- /dev/null +++ b/src/main/java/proxy/service/AuthService.java @@ -0,0 +1,5 @@ +package proxy.service; + +public interface AuthService { + void checkPermission(String userId, String permission); +} diff --git a/src/main/java/proxy/service/LogService.java b/src/main/java/proxy/service/LogService.java new file mode 100644 index 0000000..5ed36e0 --- /dev/null +++ b/src/main/java/proxy/service/LogService.java @@ -0,0 +1,5 @@ +package proxy.service; + +public interface LogService { + void log(String action, String... params); +} diff --git a/src/main/java/proxy/service/OrderService.java b/src/main/java/proxy/service/OrderService.java new file mode 100644 index 0000000..7a5a39a --- /dev/null +++ b/src/main/java/proxy/service/OrderService.java @@ -0,0 +1,9 @@ +package proxy.service; + +import java.math.BigDecimal; + +public interface OrderService { + void createOrder(String userId, String productId, BigDecimal amount); + + void cancelOrder(String orderId); +} diff --git a/src/main/java/proxy/service/impl/AuthServiceImpl.java b/src/main/java/proxy/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..6b8ef6f --- /dev/null +++ b/src/main/java/proxy/service/impl/AuthServiceImpl.java @@ -0,0 +1,11 @@ +package proxy.service.impl; + +import proxy.service.AuthService; + +public class AuthServiceImpl implements AuthService { + @Override + public void checkPermission(String userId, String permission) { + System.out.printf("[validate permission] user[%s] permission[%s]\n", userId, permission); + //do sth... + } +} diff --git a/src/main/java/proxy/service/impl/LogServiceImpl.java b/src/main/java/proxy/service/impl/LogServiceImpl.java new file mode 100644 index 0000000..0060428 --- /dev/null +++ b/src/main/java/proxy/service/impl/LogServiceImpl.java @@ -0,0 +1,12 @@ +package proxy.service.impl; + +import proxy.service.LogService; + +import java.util.Arrays; + +public class LogServiceImpl implements LogService { + @Override + public void log(String action, String... params) { + System.out.printf("[log record] operation[%s] params%s\n", action, Arrays.toString(params)); + } +} diff --git a/src/main/java/proxy/service/impl/OrderServiceImpl.java b/src/main/java/proxy/service/impl/OrderServiceImpl.java new file mode 100644 index 0000000..326ccbb --- /dev/null +++ b/src/main/java/proxy/service/impl/OrderServiceImpl.java @@ -0,0 +1,19 @@ +package proxy.service.impl; + +import proxy.service.OrderService; + +import java.math.BigDecimal; + +public class OrderServiceImpl implements OrderService { + @Override + public void createOrder(String userId, String productId, BigDecimal amount) { + System.out.printf("create order: user[%s] product[%s] amount[%s]\n", userId, productId, amount); + // do sth... + } + + @Override + public void cancelOrder(String orderId) { + System.out.printf("cancel order: [%s]\n", orderId); + // do sth... + } +} diff --git a/src/main/java/proxy/staticproxy/App.java b/src/main/java/proxy/staticproxy/App.java new file mode 100644 index 0000000..e759b22 --- /dev/null +++ b/src/main/java/proxy/staticproxy/App.java @@ -0,0 +1,14 @@ +package proxy.staticproxy; + +import java.math.BigDecimal; + +public class App { + public static void main(String[] args) { + OrderServiceStaticProxy proxy = new OrderServiceStaticProxy(); + + proxy.createOrder("user123", "prod456", new BigDecimal("199.99")); + System.out.println(); + proxy.cancelOrder("order789"); + } + +} diff --git a/src/main/java/proxy/staticproxy/OrderServiceStaticProxy.java b/src/main/java/proxy/staticproxy/OrderServiceStaticProxy.java new file mode 100644 index 0000000..848259d --- /dev/null +++ b/src/main/java/proxy/staticproxy/OrderServiceStaticProxy.java @@ -0,0 +1,52 @@ +package proxy.staticproxy; + +import proxy.service.AuthService; +import proxy.service.LogService; +import proxy.service.OrderService; +import proxy.service.impl.AuthServiceImpl; +import proxy.service.impl.LogServiceImpl; +import proxy.service.impl.OrderServiceImpl; + +import java.math.BigDecimal; + +public class OrderServiceStaticProxy { + private final OrderService orderService; + private final AuthService authService; + private final LogService logService; + + public OrderServiceStaticProxy() { + this.orderService = new OrderServiceImpl(); + this.authService = new AuthServiceImpl(); + this.logService = new LogServiceImpl(); + } + + public void createOrder(String userId, String productId, BigDecimal amount) { + // before + authService.checkPermission(userId, "CREATE_ORDER"); + logService.log("Begin to create order", userId, productId, amount.toString()); + + // invoke real objects + orderService.createOrder(userId, productId, amount); + + // after + logService.log("Order creation completed", userId, productId, amount.toString()); + } + + public void cancelOrder(String orderId) { + // before + String userId = getUserIdByOrderId(orderId); + authService.checkPermission(userId, "CANCEL_ORDER"); + logService.log("Start canceling orders", userId, orderId); + + // invoke real objects + orderService.cancelOrder(orderId); + + // after + logService.log("Order cancellation completed", userId, orderId); + } + + private String getUserIdByOrderId(String orderId) { + return STR."user_\{orderId.hashCode()}"; // Mock + } + +} diff --git a/src/main/java/Singleton/DoubleCheckedLockingSingleton.java b/src/main/java/singleton/DoubleCheckedLockingSingleton.java similarity index 98% rename from src/main/java/Singleton/DoubleCheckedLockingSingleton.java rename to src/main/java/singleton/DoubleCheckedLockingSingleton.java index 5d608c6..2c29a0a 100644 --- a/src/main/java/Singleton/DoubleCheckedLockingSingleton.java +++ b/src/main/java/singleton/DoubleCheckedLockingSingleton.java @@ -1,4 +1,4 @@ -package Singleton; +package singleton; import java.io.Serial; import java.io.Serializable; diff --git a/src/main/java/Singleton/EnumSingleton.java b/src/main/java/singleton/EnumSingleton.java similarity index 85% rename from src/main/java/Singleton/EnumSingleton.java rename to src/main/java/singleton/EnumSingleton.java index 7ce6b83..cc6cf35 100644 --- a/src/main/java/Singleton/EnumSingleton.java +++ b/src/main/java/singleton/EnumSingleton.java @@ -1,4 +1,4 @@ -package Singleton; +package singleton; public enum EnumSingleton { INSTANCE diff --git a/src/main/java/Singleton/StaticInnerClassSingleton.java b/src/main/java/singleton/StaticInnerClassSingleton.java similarity index 94% rename from src/main/java/Singleton/StaticInnerClassSingleton.java rename to src/main/java/singleton/StaticInnerClassSingleton.java index f039ef2..a6e3918 100644 --- a/src/main/java/Singleton/StaticInnerClassSingleton.java +++ b/src/main/java/singleton/StaticInnerClassSingleton.java @@ -1,4 +1,4 @@ -package Singleton; +package singleton; public final class StaticInnerClassSingleton { private StaticInnerClassSingleton() {} diff --git a/src/test/java/singleton/DoubleCheckedLockingSingletonTest.java b/src/test/java/singleton/DoubleCheckedLockingSingletonTest.java index b845dd2..824be80 100644 --- a/src/test/java/singleton/DoubleCheckedLockingSingletonTest.java +++ b/src/test/java/singleton/DoubleCheckedLockingSingletonTest.java @@ -1,10 +1,10 @@ package singleton; -import Singleton.DoubleCheckedLockingSingleton; import common.utils.SerializationUtils; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import static org.junit.jupiter.api.Assertions.*; @@ -32,7 +32,7 @@ public void testReflectionAttack() { DoubleCheckedLockingSingleton instance1 = DoubleCheckedLockingSingleton.getInstance(); //Attempt to create a second instance through reflection - assertThrows(RuntimeException.class, () -> { + assertThrows(InvocationTargetException.class, () -> { DoubleCheckedLockingSingleton instance2 = constructor.newInstance(); }, "An exception should be thrown to prevent reflection from creating an instance");