本文分享自华为云社区《Spring高手之路17——动态代理的艺术与实践》,作者: 砖业洋__。

1. 背景

动态代理是一种强大的设计模式,它允许开发者在运行时创建代理对象,用于拦截对真实对象的方法调用。这种技术在实现面向切面编程(AOP)、事务管理、权限控制等功能时特别有用,因为它可以在不修改原有代码结构的前提下,为程序动态地注入额外的逻辑。

2. JDK动态代理

2.1 定义和演示

JDK动态代理是Java语言提供的一种基于接口的代理机制,允许开发者在运行时动态地创建代理对象,而无需为每个类编写具体的代理实现。

这种机制主要通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。下面是JDK动态代理的核心要点和如何使用它们的概述。

使用步骤

  1. 定义接口:首先定义一个或多个接口,代理对象将实现这些接口。

  2. 实现接口:创建一个类,它实现上述接口,提供具体的实现逻辑。

  3. 创建 InvocationHandler 实现:定义一个 InvocationHandler 的实现,这个实现中的 invoke 方法可以包含自定义逻辑。

  4. 创建代理对象:使用 Proxy.newProxyInstance 方法,传入目标对象的类加载器、需要代理的接口数组以及 InvocationHandler 的实现,来创建一个实现了指定接口的代理对象。

用简单的例子来说明这个过程,全部代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; interface HelloWorld {
void sayHello();
} class HelloWorldImpl implements HelloWorld {
public void sayHello() {
System.out.println("Hello world!");
}
} public class DemoApplication {
public static void main(String[] args) {
HelloWorldImpl realObject = new HelloWorldImpl();
HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance(
HelloWorldImpl.class.getClassLoader(), // 使用目标类的类加载器
new Class[]{HelloWorld.class}, // 代理类需要实现的接口列表
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用目标方法前可以插入自定义逻辑
System.out.println("Before method call");
// 调用目标对象的方法
Object result = method.invoke(realObject, args);
// 在调用目标方法后可以插入自定义逻辑
System.out.println("After method call");
return result;
}
}); proxyInstance.sayHello();
}
}

运行结果如下:

InvocationHandler 是动态代理的核心接口之一,当我们使用动态代理模式创建代理对象时,任何对代理对象的方法调用都会被转发到一个实现了 InvocationHandler 接口的实例的 invoke 方法上。

我们经常看到InvocationHandler 动态代理的匿名内部类,这会在代理对象的相应方法被调用时执行。具体地说,每当对代理对象执行方法调用时,调用的方法不会直接执行,而是转发到实现了InvocationHandler 的 invoke 方法上。在这个 invoke 方法内部,我们可以定义拦截逻辑、调用原始对象的方法、修改返回值等操作。

在这个例子中,当调用 proxyInstance.sayHello() 方法时,实际上执行的是 InvocationHandler 的匿名内部类中的 invoke 方法。这个方法中,我们可以在调用实际对象的 sayHello 方法前后添加自定义逻辑(比如这里的打印消息)。这就是动态代理和 InvocationHandler 的工作原理。

我们来看关键的一句代码

Object result = method.invoke(realObject, args);

Java的动态代理中,method.invoke(realObject, args) 这句代码扮演着核心的角色,因为它实现了代理对象方法调用的转发机制。下面分别解释一下这行代码的两个主要部分:method.invoke() 方法和 args 参数。

method.invoke(realObject, args)

  • 作用:这行代码的作用是调用目标对象(realObject)的具体方法。在动态代理的上下文中,invoke 方法是在代理实例上调用方法时被自动调用的。通过这种方式可以在实际的方法执行前后加入自定义的逻辑,比如日志记录、权限检查等。

  • method:method 是一个 java.lang.reflect.Method 类的实例,代表了正在被调用的方法。在 invoke 方法中,这个对象用来标识代理对象上被调用的具体方法。

注意:如果尝试直接在invoke方法内部使用method.invoke(proxy, args)调用代理对象的方法,而不是调用原始目标对象的方法,则会导致无限循环。这是因为调用proxy实例上的方法会再次被代理拦截,从而无限调用invoke方法,传参可别传错了。

  • invoke:Method 类的 invoke 方法用于执行指定方法。第一个参数是指明方法应该在哪个对象上调用(在这个例子中是 realObject),后续的参数 args 是调用方法时传递的参数。

args

  • 定义:args 是一个对象数组,包含了调用代理方法时传递给方法的参数值。如果被调用的方法没有参数,args 将会是 null 或者空数组。

  • 作用:args 允许在 invoke 方法内部传递参数给实际要执行的方法。这意味着可以在动态代理中不仅控制是否调用某个方法,还可以修改调用该方法时使用的参数。

2.2 不同方法分别代理

我们继续通过扩展 HelloWorld 接口来包含多个方法,并通过JDK动态代理演示权限控制和功能开关操作的一种实现方式

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; interface HelloWorld {
void sayHello();
void sayGoodbye();
} class HelloWorldImpl implements HelloWorld {
public void sayHello() {
System.out.println("Hello world!");
} public void sayGoodbye() {
System.out.println("Goodbye world!");
}
} public class DemoApplication {
public static void main(String[] args) {
HelloWorld realObject = new HelloWorldImpl();
HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance(
HelloWorldImpl.class.getClassLoader(),
new Class[]{HelloWorld.class},
new InvocationHandler() {
// 添加一个简单的权限控制演示
private boolean accessAllowed = true; // 简单的功能开关
private boolean goodbyeFunctionEnabled = true; @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 权限控制
if (!accessAllowed) {
System.out.println("Access denied");
return null; // 在实际场景中,可以抛出一个异常
} // 功能开关
if (method.getName().equals("sayGoodbye") && !goodbyeFunctionEnabled) {
System.out.println("Goodbye function is disabled");
return null;
} // 方法执行前的通用逻辑
System.out.println("Before method: " + method.getName()); // 执行方法
Object result = method.invoke(realObject, args); // 方法执行后的通用逻辑
System.out.println("After method: " + method.getName()); return result;
}
});
// 正常执行
proxyInstance.sayHello(); // 可以根据goodbyeFunctionEnabled变量决定是否执行
proxyInstance.sayGoodbye();
}
}

运行如下:

如果accessAllowed 变量为false

如果goodbyeFunctionEnabled 变量为false

在这个例子中:

  • 权限控制:通过检查 accessAllowed 变量,我们可以模拟简单的权限控制。如果没有权限,可以直接返回或抛出异常,避免执行方法。

  • 功能开关:通过检查方法名称和 goodbyeFunctionEnabled 变量,我们可以控制 sayGoodbye 方法是否被执行。这可以用来根据配置启用或禁用特定功能。

这个例子展示了JDK动态代理在实际应用中如何进行方法级别的细粒度控制,同时保持代码的灵活性和可维护性。通过动态代理,我们可以在不修改原始类代码的情况下,为对象动态地添加额外的行为。

2.3 熔断限流和日志监控

为了更全面地展示JDK动态代理的能力,我们在先前的示例中添加熔断限流和日志监控的逻辑。这些是在高并发和分布式系统中常见的需求,可以通过动态代理以非侵入式的方式实现。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; interface HelloWorld {
void sayHello();
} class HelloWorldImpl implements HelloWorld {
public void sayHello() {
System.out.println("Hello world!");
}
} public class DemoApplication {
public static void main(String[] args) {
HelloWorld realObject = new HelloWorldImpl();
HelloWorld proxyInstance = (HelloWorld) Proxy.newProxyInstance(
HelloWorldImpl.class.getClassLoader(),
new Class[]{HelloWorld.class},
new AdvancedInvocationHandler(realObject)); // 模拟多次调用以观察限流和熔断效果
for (int i = 0; i < 10; i++) {
proxyInstance.sayHello();
}
}
static class AdvancedInvocationHandler implements InvocationHandler {
private final Object target;
private AtomicInteger requestCount = new AtomicInteger(0);
private AtomicLong lastTimestamp = new AtomicLong(System.currentTimeMillis());
private volatile boolean circuitBreakerOpen = false;
private final long cooldownPeriod = 10000; // 冷却时间10秒 public AdvancedInvocationHandler(Object target) {
this.target = target;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long now = System.currentTimeMillis(); // 检查熔断器是否应该被重置
if (circuitBreakerOpen && (now - lastTimestamp.get() > cooldownPeriod)) {
circuitBreakerOpen = false; // 重置熔断器
requestCount.set(0); // 重置请求计数
System.out.println("Circuit breaker has been reset.");
} // 熔断检查
if (circuitBreakerOpen) {
System.out.println("Circuit breaker is open. Blocking method execution for: " + method.getName());
return null; // 在实际场景中,可以返回一个兜底的响应或抛出异常
} // 限流检查
if (requestCount.incrementAndGet() > 5) {
if (now - lastTimestamp.get() < cooldownPeriod) { // 10秒内超过5次请求,触发熔断
circuitBreakerOpen = true;
lastTimestamp.set(now); // 更新时间戳
System.out.println("Too many requests. Opening circuit breaker.");
return null; // 触发熔断时的处理
} else {
// 重置计数器和时间戳
requestCount.set(0);
lastTimestamp.set(now);
}
} // 执行实际方法
Object result = method.invoke(target, args); // 方法执行后的逻辑
System.out.println("Executed method: " + method.getName()); return result;
}
}
}

在这个扩展示例中,我们实现了:

  • 熔断机制:通过一个简单的计数器和时间戳来模拟。如果在10秒内对任一方法的调用次数超过5次,我们就"打开"熔断器,阻止进一步的方法调用。在实际应用中,熔断逻辑可能更加复杂,可能包括错误率的检查、调用延迟的监控等。

  • 限流:这里使用的限流策略很简单,通过计数和时间戳来判断是否在短时间内请求过多。在更复杂的场景中,可以使用令牌桶或漏桶算法等更高级的限流策略。

  • 日志监控:在方法调用前后打印日志,这对于监控系统的行为和性能是非常有用的。在实际项目中,这些日志可以集成到日志管理系统中,用于问题诊断和性能分析。

  通过在 invoke 方法中加入这些逻辑,我们能够在不修改原有业务代码的情况下,为系统添加复杂的控制和监控功能。如果到达流量阈值或系统处于熔断状态,可以阻止对后端服务的进一步调用,直接返回一个默认值或错误响应,避免系统过载。

3. CGLIB动态代理

CGLIBCode Generation Library)是一个强大的高性能代码生成库,它在运行时动态生成新的类。与JDK动态代理不同,CGLIB能够代理那些没有实现接口的类。这使得CGLIB成为那些因为设计限制或其他原因不能使用接口的场景的理想选择。

3.1 定义和演示

工作原理

CGLIB通过继承目标类并在运行时生成子类来实现动态代理。代理类覆盖了目标类的非final方法,并在调用方法前后提供了注入自定义逻辑的能力。这种方法的一个关键优势是它不需要目标对象实现任何接口。

使用CGLIB的步骤

添加CGLIB依赖:首先,需要在项目中添加CGLIB库的依赖。

如果使用Maven,可以添加如下依赖到pom.xml中:

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version> <!-- 目前最新的版本 -->
</dependency>

创建MethodInterceptor:实现MethodInterceptor接口,这是CGLIB提供的回调类型,用于定义方法调用的拦截逻辑。

生成代理对象:使用Enhancer类来创建代理对象。EnhancerCGLIB中用于生成新类的类。

改造一下1.1节的例子,可以对比看看,全部示例代码如下:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class HelloWorld {
public void sayHello() {
System.out.println("Hello world!");
}
} public class DemoApplication {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
// 设置需要代理的类
enhancer.setSuperclass(HelloWorld.class); enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method call");
Object result = proxy.invokeSuper(obj, args); // 调用父类的方法
System.out.println("After method call");
return result;
}
}); HelloWorld proxy = (HelloWorld) enhancer.create(); // 创建代理对象
proxy.sayHello(); // 通过代理对象调用方法
}
}

运行结果如下:

CGLIB vs JDK动态代理

  • 接口要求:JDK动态代理只能代理实现了接口的对象,而CGLIB能够直接代理类。
  • 性能:CGLIB在生成代理对象时通常比JDK动态代理要慢,因为它需要动态生成新的类。但在调用代理方法时,CGLIB通常会提供更好的性能。
  • 方法限制:CGLIB不能代理final方法,因为它们不能被子类覆盖。

CGLIB是一个强大的工具,特别适用于需要代理没有实现接口的类的场景。然而,选择JDK动态代理还是CGLIB主要取决于具体的应用场景和性能要求。

注意:在CGLIB中,如果使用MethodProxy.invoke(obj, args) ,而不是MethodProxy.invokeSuper(obj, args),并且obj是代理实例本身(CGLIB通过Enhancer创建的代理对象,而不是原始的被代理的目标对象),就会导致无限循环。invoke方法实际上是尝试在传递的对象上调用方法,如果该对象是代理对象,则调用会再次被拦截,造成无限循环。

  • JDK动态代理中,确保调用method.invoke时使用的是目标对象,而不是代理对象。

  • CGLIB代理中,使用MethodProxy.invokeSuper而不是MethodProxy.invoke来调用被代理的方法,以避免无限循环。

3.2 不同方法分别代理(对比JDK动态代理写法)

我们改写1.2节的例子

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method;
class HelloWorldImpl {
public void sayHello() {
System.out.println("Hello world!");
} public void sayGoodbye() {
System.out.println("Goodbye world!");
}
}
public class DemoApplication {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWorldImpl.class); // 设置被代理的类
enhancer.setCallback(new MethodInterceptor() {
// 添加一个简单的权限控制演示
private boolean accessAllowed = true; // 简单的功能开关
private boolean goodbyeFunctionEnabled = true; @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 权限控制
if (!accessAllowed) {
System.out.println("Access denied");
return null; // 在实际场景中,可以抛出一个异常
} // 功能开关
if (method.getName().equals("sayGoodbye") && !goodbyeFunctionEnabled) {
System.out.println("Goodbye function is disabled");
return null;
} // 方法执行前的通用逻辑
System.out.println("Before method: " + method.getName()); // 执行方法
Object result = proxy.invokeSuper(obj, args); // 方法执行后的通用逻辑
System.out.println("After method: " + method.getName()); return result;
}
}); HelloWorldImpl proxyInstance = (HelloWorldImpl) enhancer.create(); // 创建代理对象
proxyInstance.sayHello(); // 正常执行
proxyInstance.sayGoodbye(); // 可以根据goodbyeFunctionEnabled变量决定是否执行
}
}

运行结果如下:

我们需要注意几点更改:

  1. 因为CGLIB不是基于接口的代理,而是通过生成目标类的子类来实现代理,所以我们不再需要接口HelloWorld

  2. 我们将使用Enhancer类来创建代理实例,并提供一个MethodInterceptor来处理方法调用。

3.3 熔断限流和日志监控(对比JDK动态代理写法)

我们改写1.3节的例子

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; class HelloWorld {
void sayHello() {
System.out.println("Hello world!");
}
} public class DemoApplication {
public static void main(String[] args) {
HelloWorld realObject = new HelloWorld();
HelloWorld proxyInstance = (HelloWorld) createProxy(realObject); // 模拟多次调用以观察限流和熔断效果
for (int i = 0; i < 10; i++) {
proxyInstance.sayHello();
}
} public static Object createProxy(final Object realObject) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloWorld.class);
enhancer.setCallback(new AdvancedMethodInterceptor(realObject));
return enhancer.create();
} static class AdvancedMethodInterceptor implements MethodInterceptor {
private final Object target;
private final AtomicInteger requestCount = new AtomicInteger(0);
private final AtomicLong lastTimestamp = new AtomicLong(System.currentTimeMillis());
private volatile boolean circuitBreakerOpen = false;
private final long cooldownPeriod = 10000; // 冷却时间10秒 public AdvancedMethodInterceptor(Object target) {
this.target = target;
} @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
long now = System.currentTimeMillis(); // 检查熔断器是否应该被重置
if (circuitBreakerOpen && (now - lastTimestamp.get() > cooldownPeriod)) {
circuitBreakerOpen = false; // 重置熔断器
requestCount.set(0); // 重置请求计数
System.out.println("Circuit breaker has been reset.");
} // 熔断检查
if (circuitBreakerOpen) {
System.out.println("Circuit breaker is open. Blocking method execution for: " + method.getName());
return null; // 在实际场景中,可以返回一个兜底的响应或抛出异常
} // 限流检查
if (requestCount.incrementAndGet() > 5) {
if (now - lastTimestamp.get() < cooldownPeriod) { // 10秒内超过5次请求,触发熔断
circuitBreakerOpen = true;
lastTimestamp.set(now); // 更新时间戳
System.out.println("Too many requests. Opening circuit breaker.");
return null; // 触发熔断时的处理
} else {
// 重置计数器和时间戳
requestCount.set(0);
lastTimestamp.set(now);
}
} // 执行实际方法
Object result = proxy.invokeSuper(obj, args); // 注意这里调用的是invokeSuper // 方法执行后的逻辑
System.out.println("Executed method: " + method.getName()); return result;
}
}
}

运行结果

在这个改写中,我们使用CGLIBEnhancerMethodInterceptor来代替了JDKProxyInvocationHandlerMethodInterceptorintercept方法与InvocationHandlerinvoke方法在概念上是相似的,但它使用MethodProxyinvokeSuper方法来调用原始类的方法,而不是使用反射。这允许CGLIB在运行时生成代理类的字节码,而不是依赖于反射,从而提高了性能。此外,circuitBreakerOpen被声明为volatile,是确保其在多线程环境中的可见性。

4. 动态代理图示

方法调用拦截:

客户端通过代理对象调用方法,此时方法调用被代理对象拦截。

转发给处理器或方法拦截器:

代理对象将方法调用转发给一个特定的处理器,这取决于所使用的代理类型。对于JDK动态代理,这个处理器是InvocationHandler;对于CGLIB代理,是MethodInterceptor

执行额外操作(调用前):

在实际执行目标对象的方法之前,处理器有机会执行一些额外的操作,例如日志记录、安全检查或事务管理等。

调用目标对象的方法:

处理器在必要时直接调用目标对象的方法。在JDK动态代理中,这通常通过反射实现;而在CGLIB中,可以通过MethodProxy.invokeSuper方法调用。

执行额外操作(调用后):

方法调用完成后,处理器再次有机会执行额外操作,比如修改返回值、记录执行时间或进行事务的提交或回滚。

返回给客户端:

最终,方法的返回值被通过代理对象返回给客户端。

5. JDK动态代理 VS CGLIB动态代理对比

JDK动态代理

JDK动态代理是Java自带的代理机制,它直接使用反射API来调用方法。

优点:

  • 无需第三方依赖:作为Java标准API的一部分,使用JDK动态代理不需要添加额外的库或依赖。

  • 接口导向:强制使用接口进行代理,这符合面向接口编程的原则,有助于保持代码的清晰和灵活。

缺点:

  • 仅限接口:只能代理实现了接口的类,这在某些情况下限制了它的使用。

  • 性能开销:由于使用反射API进行方法调用,可能会有一定的性能开销,尤其是在大量调用时。

CGLIB动态代理

CGLIBCode Generation Library)通过在运行时生成被代理对象的子类来实现代理。

优点:

  • 不需要接口:可以代理没有实现任何接口的类,这提供了更大的灵活性。

  • 性能较好:通常认为CGLIB的性能比JDK动态代理要好,特别是在代理方法的调用上,因为CGLIB使用了字节码生成技术,减少了使用反射的需要。

缺点:

  • 第三方库:需要添加CGLIB库作为项目依赖。

  • 无法代理final方法:由于CGLIB是通过生成子类的方式来代理的,所以无法代理那些被声明为final的方法。

性能比较

  • 调用速度:CGLIB在代理方法调用方面通常比JDK动态代理更快。这是因为CGLIB通过直接操作字节码来生成新的类,避免了反射带来的性能开销。

  • 启动性能:CGLIB在生成代理对象时可能会比JDK动态代理慢,因为它需要在运行时生成新的字节码。如果代理对象在应用启动时就被创建,这可能会略微影响启动时间。

选择建议

  • 如果类已经实现了接口,或者希望强制使用接口编程,那么JDK动态代理是一个好选择。

  • 如果需要代理没有实现接口的类,或者对性能有较高的要求,特别是在代理方法的调用上,CGLIB可能是更好的选择。

  • 在现代的Java应用中,很多框架(如Spring)都提供了对这两种代理方式的透明支持,并且可以根据实际情况自动选择使用哪一种。例如,Spring AOP默认会使用JDK动态代理,但如果遇到没有实现接口的类,它会退回到CGLIB

6. 动态代理的实际应用场景

面向切面编程(AOP):

  • 问题解决:在不改变原有业务逻辑代码的情况下,为程序动态地添加额外的行为(如日志记录、性能监测、事务管理等)。

  • 应用实例:Spring AOP 使用动态代理为方法调用提供了声明式事务管理、安全性检查和日志记录等服务。根据目标对象是否实现接口,Spring AOP可以选择使用JDK动态代理或CGLIB代理。

事务管理:

  • 问题解决:自动化处理数据库事务的边界,如开始、提交或回滚事务。

  • 应用实例:Spring框架中的声明式事务管理使用代理技术拦截那些被@Transactional注解标记的类或方法,确保方法执行在正确的事务管理下进行。

权限控制和安全性:

  • 问题解决:在执行敏感操作之前自动检查用户权限,确保只有拥有足够权限的用户才能执行某些操作。

  • 应用实例:企业应用中,使用代理技术拦截用户的请求,进行权限验证后才允许访问特定的服务或执行操作。

延迟加载:

  • 问题解决:对象的某些属性可能加载成本较高,通过代理技术,可以在实际使用这些属性时才进行加载。

  • 应用实例:Hibernate和其他ORM框架使用代理技术实现了延迟加载(懒加载),以提高应用程序的性能和资源利用率。

服务接口调用的拦截和增强:

  • 问题解决:对第三方库或已有服务进行包装,添加额外的逻辑,如缓存结果、参数校验等。

  • 应用实例:在微服务架构中,可以使用代理技术对服务客户端进行增强,实现如重试、熔断、限流等逻辑。

在现代框架中的应用

  • Spring框架:SpringAOP模块和事务管理广泛使用了动态代理技术。根据目标对象的类型(是否实现接口),Spring可以自动选择JDK动态代理或CGLIB代理。

  • Hibernate:Hibernate使用动态代理技术实现懒加载,代理实体类的关联对象,在实际访问这些对象时才从数据库中加载它们的数据。

  • MyBatis:MyBatis框架使用动态代理技术映射接口和SQL语句,允许开发者通过接口直接与数据库交互,而无需实现类。

欢迎一键三连~

有问题请留言,大家一起探讨学习

点击关注,第一时间了解华为云新鲜技术~

Spring开发:动态代理的艺术与实践的更多相关文章

  1. spring AOP 动态代理和静态代理以及事务

    AOP(Aspect Oriented Programming),即面向切面编程 AOP技术,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装 ...

  2. 解决spring使用动态代理

    解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ... 转 ...

  3. 正向代理和反向代理和spring的动态代理模式有几种?默认是那种?如何切换?

    spring的动态代理模式有几种?默认是那种?如何切换? spring的动态的代理模式有两种 JDK动态代理,基于接口(默认代理模式),CGLIB动态代理(若要使用需要进行配置) JDK动态代理是由j ...

  4. 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring CGLlB动态代理

    JDK 动态代理使用起来非常简单,但是它也有一定的局限性,这是因为 JDK 动态代理必须要实现一个或多个接口,如果不希望实现接口,则可以使用 CGLIB 代理. CGLIB(Code Generati ...

  5. 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring JDK动态代理

    JDK 动态代理是通过 JDK 中的 java.lang.reflect.Proxy 类实现的.下面通过具体的案例演示 JDK 动态代理的使用. 1. 创建项目 在 MyEclipse 中创建一个名称 ...

  6. spring aop 动态代理批量调用方法实例

    今天项目经理发下任务,需要测试 20 个接口,看看推送和接收数据是否正常.因为对接传输的数据是 xml 格式的字符串,所以我拿现成的数据,先生成推送过去的数据并存储到文本,以便验证数据是否正确,这时候 ...

  7. Spring CGLlB动态代理

    JDK 动态代理使用起来非常简单,但是它也有一定的局限性,这是因为 JDK 动态代理必须要实现一个或多个接口,如果不希望实现接口,则可以使用 CGLIB 代理. CGLIB(Code Generati ...

  8. Spring AOP 动态代理 缓存

    Spring AOP应用:xml配置及注解实现. 动态代理:jdk.cglib.javassist 缓存应用:高速缓存提供程序ehcache,页面缓存,session缓存 项目地址:https://g ...

  9. Spring AOP动态代理原理与实现方式

    AOP:面向切面.面向方面.面向接口是一种横切技术横切技术运用:1.事务管理: (1)数据库事务:(2)编程事务(3)声明事物:Spring AOP-->声明事物   2.日志处理:3.安全验证 ...

  10. Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

    Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...

随机推荐

  1. 如何避免MYSQL主从延迟带来的读写问题?

    在MYSQL 部署架构选型上,许多公司都会用到主从读写分离的架构,如下是一个一主一从的架构,主库master负责写入,从库slave进行读取. 但是既然是读写分离,必然会面临这样一个问题,当在主库上进 ...

  2. vite + vue3 打包后 本地直接运行 type="module" crossorigin 替换为defer - 多个vue文件就不好使了

    vite + vue3 打包后 本地直接运行 type="module" crossorigin 替换为defer 需求: 想打包后,双击运行,不启动服务 修改 vite.conf ...

  3. Nginx 打不开 80端口占用 netstat -aon|findstr "80" 看有没有80占用 有的话 net stop http

    Nginx 打不开 80端口占用 netstat -aon|findstr "80" 看有没有80占用 有的话 net stop http

  4. k8s资源管理中request和limit的区别

    在 Kubernetes(K8s)中,request和limit是两个重要的概念,用于控制和管理容器的资源使用. Request(请求): request定义了容器启动时需要保证的最小资源量.这表示K ...

  5. struts1之global-forwards

    当你的某个转发要经常用,并且要携带某些数据(request)的时候用全局转发,也就是global-forwards,例如我们在分页的时候,或者得到数据列表的时候.. ForwardAction呢,是为 ...

  6. Android 开发Day2

    我的是小刺猬版本,算是比较新的版本了,还有火烈鸟和蜻蜓版啥的 新建项目(project)点击加号新建就行了.这时我们会选择一个模板作为开发的辅助起点,看上哪个就选哪个就行了.推荐新手选空项目(Empt ...

  7. ViewStub你真的了解吗

    目录介绍 01.什么是ViewStub 02.ViewStub构造方法 03.inflate()方法解析 04.WeakReference使用 05.ViewStub为何无大小 06.ViewStub ...

  8. MongoDB4.0.11服务没有响应控制功能解决办法

    如图 MongDB安装好后启动服务失败 解决办法 进入到你的MongDB bin目录下执行 mongod.exe --remove --serviceName "MongoDB" ...

  9. vscode 屏蔽某些文件夹

    vscode 屏蔽某些文件夹 File--Preferences--Settings(Ctrl + ,) 搜索框里搜索 Files:Exclude 点击 Add Pattern ,如果想屏蔽掉所有 . ...

  10. 【OpenCV】OpenCV (C++) 与 OpenCvSharp (C#) 之间数据通信

      OpenCV是一个基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux.Windows.Android和Mac OS操作系统上. 它轻量级而且高效--由一 ...