脚本宝典收集整理的这篇文章主要介绍了慌,老大突然问我基于JDK和CGLib实现动态代理的区别和适用场景,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
在日常的开发中,SPRing AOP 是一个非常常用的功能。谈到 AOP,自然离不开动态代理。
那么,基于 JDK 和 CGLib 如何实现动态代理,他们之间的区别和适用场景是什么呢F1f;接下来,我们一起来探讨一下这个问题。
话不多说,我们直接对照着代码来查看。
public interface HelloInterface {
/**
* 代理的目标方法
*/
void sayHello();
/**
* 未被代理处理的方法
*/
void noProxyMethod();
}
public class HelloImpl implements HelloInterface {
@override
public void sayHello() {
System.out.println("proxyMethod:sayHello");
}
@Override
public void noProxyMethod() {
System.out.println("noProxyMethod");
}
}
public class MyInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
/**
* 构造方法
*
* @param target
*/
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ("sayHello".equals(methodName)) {
// 比方说,;mybaitis 中的 PooledConnection 利用 jdk 动态代理重新实现了 close 方法
System.out.println("change method");
return null;
}
System.out.println("invoke method");
Object result = method.invoke(target, args);
return result;
}
}
动态代理神奇的地方就是:
⚠️注意:从 Object 中继承的方法,JDK Proxy 会把hashCode()、equals()、toString()这三个非接口方法转发给 InvocationHandler,其余的 Object 方法则不会被转发。详见 JDK Proxy官方文档。
public class MyDynamicProxytest {
public static void main(String[] args) {
HelloInterface hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
// 构造代码实例
HelloInterface proxyInstance = (HelloInterface) Proxy.newProxyInstance(
HelloImpl.class.getClassLoader(),
HelloImpl.class.getInterfaces(),
handler);
// 代理调用方法
proxyInstance.sayHello();
proxyInstance.noProxyMethod();
}
}
打印的日志信息如下:
结合上面的演示,我们小结一下 JDK 动态代理的实现,包括三个步骤:
无需定义和实现接口。
public class Hello {
public String sayHello(String name) {
System.out.println("Hello," + name);
return "Hello," + name;
}
}
/**
* 实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
*/
public class CglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("intercept param is " + Arrays.toString(args));
System.out.println("before===============" + method);
// 这里可以实现增强的逻辑处理s
Object result = methodProxy.invokeSuPEr(obj, args);
// 这里可以实现增强的逻辑处理
System.out.println("after===============" + method);
return result;
}
}
⚠️注意:对于从Object中继承的方法,CGLIB代理也会进行代理,如hashCode()、equals()、toString()等,但是getClass()、waIT()等方法不会(因为其他方法是 final,无法被代理),CGLIB 无法代理她们。
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
</dependencies>
public class CgliBTest {
/**
* 在需要使用 Hello 的时候,通过CGLIB动态代理获取代理对象
*/
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback(new CglibMethodInterceptor());
// 给目标对象创建一个代理对象
Hello hello = (Hello) enhancer.create();
hello.sayHello("Alan");
}
打印的日志如下:
结合上面的演示,我们小结一下 CGLIB 动态代理的实现:
Java 1.3 后,提供了动态代理技术,允许我们开发者在运行期创建接口的代理实例,后来这项技术被用到了很多地方(比如 Spring AOP)。
JDK 动态代理主要对应到 java.lang.reflect 包下边的两个类:Proxy 和 InvocationHandler。
其中 InvocationHandler 是一个接口,可以通过实现该接口定义横切逻辑。
举个例子,在方法执行前后打印的日志(这里只是为了说明,实际应用一般不会只是简单地打印日志,一般用于日志、安全、事务等场景),并通过「反射机制」调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
JDK 动态代理是面向接口的代理模式,如果被代理目标没有接口则无能为力。
例如,Spring 通过 Java 的反射机制生产被代理接口的新的匿名实现类,重写了 AOP 的增强方法。
利用 ASM 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。
网上有人对于不同版本的 jdk 进行了测试,经过多次试验,测试结果大致如下:
[idea] 很多时候,性能差异不一定是我们选择某种方式的绝对因素,我们更应该去考虑该技术适用的场景。
例如,我们应用中绝大多数的性能差异可能主要在集中在磁盘 I/O,网络带宽等因素,动态代理这点性能差异可能只是占了非常小的比例。
如果觉得本文对你有帮助,可以关注一下我公众号,回复关键字【技术】即可各种编程资料以及面试题大礼包!还有更多技术干货文章以及架构资料共享,大家一起学习进步!
以上是脚本宝典为你收集整理的慌,老大突然问我基于JDK和CGLib实现动态代理的区别和适用场景全部内容,希望文章能够帮你解决慌,老大突然问我基于JDK和CGLib实现动态代理的区别和适用场景所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。