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