听说:只有掌握了这三种代理模式,才能进军Spring AOP,思维图+核心+架构让你一步到位

如何解决这些缺点呢?这时候就需要动态代理方法了

JDK动态代理


其实动态代理和静态代理的本质是一样的,最终程序运行时都需要生成一个代理对象实例,通过它来完成相关增强以及业务逻辑,只不过静态代理需要硬编码的方式指定,而动态代理支持运行时动态生成这种实现方式。

JDK本身帮我们实现了动态代理,只需要使用newProxyInstance方法:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

  • ClassLoader loader,:指定当前目标对象使用类加载器

  • Class<?>[] interfaces,:代理类需要实现的接口列表

  • InvocationHandler h:调用处理程序,将目标对象的方法分派到该调用处理程序

代码示例:

public class DynamicProxy implements InvocationHandler {

private Object target; // 目标对象

public Object bind(Object target) {

this.target = target;

return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

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

System.out.println(“拿个小本本记录一下”);

return result;

}

}

上文的invoke方法,负责增强目标对象的方法,接口类的所有方法都会走这个invoke方法。另外bind方法简单封装了JDK的代理方法newProxyInstance,负责返回接口类。

测试类:

public static void main(String[] args) {

DynamicProxy dynamicProxy = new DynamicProxy();

UserService userService = (UserService)dynamicProxy.bind(new UserServiceImpl());

userService.addUser();

userService.updateUser();

}

运行结果如下:

如图UserService接口里的所有方法都已经加上了日志逻辑了,此外,我们看一下UserDynamicProxy这个类里的target属性是Object类型的。所以,这个动态代理的方法同样可以给其他Service复用。可以这样调用:

DynamicProxy dynamicProxy = new DynamicProxy();

TeacherService teacherService = (TeacherService)dynamicProxy.bind(new TeacherServiceImpl());

综上,动态代理解决了静态代理的缺点

用arthas查看JDK动态代理生成的类

动态代理是运行时候动态生成代理类的,这个类放在内存中,我们要怎么才能看到这个类呢?

artias是阿里开源的一个牛逼闪闪的Java诊断工具,不懂的可以看看这篇文章http://www.dblearn.cn/article/5,用它就可以线上反编译代码。

这里我们添加一个断点:

public static void main(String[] args) throws IOException {

DynamicProxy dynamicProxy = new DynamicProxy();

UserService userService = (UserService)dynamicProxy.bind(new UserServiceImpl());

userService.addUser();

userService.updateUser();

System.in.read();

}

运行 arthas

用_jad_命令反编译,java生成的代理类都在com.sun.proxy目录下。因此反编译命令如下

jad com.sun.proxy.$Proxy0

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

import proxy.UserService;

public final class $Proxy0 extends Proxy implements UserService {

private static Method m1;

private static Method m6;

private static Method m2;

private static Method m7;

private static Method m0;

private static Method m3;

private static Method m4;

private static Method m5;

public final void addUser() {

try {

this.h.invoke(this, m3, null);

return;

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public final void updateUser() {

try {

this.h.invoke(this, m4, null);

return;

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public final void deleteUser() {

try {

this.h.invoke(this, m5, null);

return;

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public final void batchUpdateUser() {

try {

this.h.invoke(this, m6, null);

return;

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public final void batchDeleteUser() {

try {

this.h.invoke(this, m7, null);

return;

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public $Proxy0(InvocationHandler invocationHandler) {

super(invocationHandler);

}

static {

try {

m1 = Class.forName(“java.lang.Object”).getMethod(“equals”, Class.forName(“java.lang.Object”));

m6 = Class.forName(“proxy.UserService”).getMethod(“batchUpdateUser”, new Class[0]);

m2 = Class.forName(“java.lang.Object”).getMethod(“toString”, new Class[0]);

m7 = Class.forName(“proxy.UserService”).getMethod(“batchDeleteUser”, new Class[0]);

m0 = Class.forName(“java.lang.Object”).getMethod(“hashCode”, new Class[0]);

m3 = Class.forName(“proxy.UserService”).getMethod(“addUser”, new Class[0]);

m4 = Class.forName(“proxy.UserService”).getMethod(“updateUser”, new Class[0]);

m5 = Class.forName(“proxy.UserService”).getMethod(“deleteUser”, new Class[0]);

return;

}

catch (NoSuchMethodException noSuchMethodException) {

throw new NoSuchMethodError(noSuchMethodException.getMessage());

}

catch (ClassNotFoundException classNotFoundException) {

throw new NoClassDefFoundError(classNotFoundException.getMessage());

}

}

public final boolean equals(Object object) {

try {

return (Boolean)this.h.invoke(this, m1, new Object[]{object});

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public final String toString() {

try {

return (String)this.h.invoke(this, m2, null);

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

public final int hashCode() {

try {

return (Integer)this.h.invoke(this, m0, null);

}

catch (Error | RuntimeException throwable) {

throw throwable;

}

catch (Throwable throwable) {

throw new UndeclaredThrowableException(throwable);

}

}

}

由上面的代码可以看到我们的代理类已经生成好了,没当我们调用方法如 addUser(),实际分派到h变量的invoke方法上执行:

this.h.invoke(this, m3, null);

h变量是什么呢?其实就是我们实现了InvocationHandler的DynamicProxy类。

cglib动态代理


通过观察上面的静态代理和JDK动态代理模式,发现要求目标对象_实现一个接口_,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口。这时候要怎么处理呢?下面引出大名鼎鼎的CGlib动态代理

cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

要用cglib需要引入它的jar包,因为spring已经集成了它,因此引入spring包即可

编写代理类:

public class CGLibProxy implements MethodInterceptor {

private Object target; // 目标对象

public Object bind(Object target) {

this.target = target;

Enhancer enhancer = new Enhancer();

//设置父类

enhancer.setSuperclass(this.target.getClass());

//设置回调函数

enhancer.setCallback(this);

//创建子类(代理对象)

return enhancer.create();

}

@Override

public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

Object result = methodProxy.invokeSuper(obj, args);

System.out.println(“拿个小本本记录一下”);

return result;

}

}

其中,Enhancer需要设置目标对象为父类(因为生成的代理类需要继承目标对象)

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

);

return result;

}

}

其中,Enhancer需要设置目标对象为父类(因为生成的代理类需要继承目标对象)

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-bGY5fznF-1710921518869)]
[外链图片转存中…(img-7y42SeOc-1710921518870)]
[外链图片转存中…(img-22wUzuhv-1710921518870)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-RQN3TrFt-1710921518870)]

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值