java动态代理

java动态代理

动态代理在java中有很多使用,自己的一个学习项目微服务远程调用RPC就是需要用到动态代理来隐藏屏蔽掉客户端服务端之间的复杂通信,另外Spring AOP的底层实现也是使用到了动态代理。个人感觉动态代理应该可以算是一个java基础知识中比较重要和较难理解的内容吧。
如果想面试时候不虚的话还是要仔细搞懂动态代理内容的,最好不要一问就开始吟唱:“动态代理有jdk和cglib两种实现方式,分别是基于接口和…”
我想如果我来面试的话,如果说了解动态代理,我可能会让打开idea现场手撕一个简单动态代理。因为面试中有碰到让手撕多线程的(这个后面我要整理下,手撕多线程,有同门面试鹅碰到面试官让手写Ngix配置的),所以感觉手撕动态代理也不是没有可能。

理解

功能扩展:
面对对象编程中,有开闭原则这个东西,及对扩展开放,对修改关闭,这个我个人的理解就是,你实现了一个类A,这个类包含了某个对象的属性和方法,那么后续你可能需要增加一些功能,比如对某型功能要进行扩展,这个时候直接更改原本定义的方法会很不合适,如果很多人开发的话,那么共有的定义类会被改成屎山,而针对业务需要对实现好的类功能进行增强就需要使用代理。
控制访问:
另一种场景是控制访问的需要:
(引用一个举例):
在开发中也会有这样的情况, 你有a类, 本来是调用c类的方法, 完成某个功能。 但是c不让a调用。

a -----不能调用 c的方法。
在a 和 c 直接 创建一个 b 代理, c让b访问。
a --访问b—访问c

这种思想感觉可以类比Java Class中的 Pravite 属性,外部不能直接访问对吧,那就用一个get()方法来获得,get()在这里我感觉就是代理方法。

最简单粗暴的方式就是实现一个新的类B,在这个类中实例化一个A,在B的方法中B.Method调用A中的方法A.Method,那么在B.Method中就可以夹带私货,为所欲为了。

class A{
	public Method(){
		System.out.println("A功能代码");
	}
}
class B{
	//B中实例化一个A对象
	A a = new A();
	public Method(){
		System.out.println("夹带私货部分代码");
		a.Method();
		System.out.println("为所欲为代码");
	}
}

感觉就是一个嵌套调用对吧,但这其实就是静态代理,这种方式的缺点显而易见,目标类如果比较多的话,是不是需要一个个的手写代理类,虽然是重复工作难度不大,但是顶不住代码改成屎山。
是不是如果需要实现的增强相同,也就是比如说我们要在函数调用后增加日志输出,那么对所有方法的增强都是一样的,对C类的方法增强也是这一套代码,只是目标类不同而已,代码大量重复。如果可以在调用时候将目标类传入,然后就可以实现代理调用就好了,不是吗,例如:

//目标类作为参数传入;
proxy = new Proxy(A.class);
//实际运行A中方法的增强,等价于之前的B.Method();
proxy.Method();

这种情况是不是很理想,只使用很少的代码,就可以统一动态增强所有方法。

所以本质的本质,就是:

  1. 需要实现一个代理类,里面有对方法的增强,但是跟目标类(被增强的类)无关。
  2. 动态传入目标类(被增强的类),实现增强。

也就是把本来固定的A(target),变成传入的参数,在调用时根据需要传入。

class Proxy{
	//被代理类作为参数传入
	public Method(Target target){
		System.out.println("夹带私货部分代码");
		target.Method();
		System.out.println("为所欲为代码");
	}

JDK动态代理

简单示例:

1.第一步,创建一个接口

public interface Subject {
    void hello(String param);
}
2.第二步,实现接口

public class SubjectImpl implements Subject {
    @Override
    public void hello(String param) {
        System.out.println("hello  " + param);
    }
}
3.第三步,创建SubjectImpl的代理类

public class SubjectProxy implements InvocationHandler {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("--------------begin-------------");
        Object invoke = method.invoke(subject, args);
        System.out.println("--------------end-------------");
        return invoke;
    }
}
4.编写代理类实际的调用,利用Proxy类创建代理之后的Subject类。

public class Main {

    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler subjectProxy = new SubjectProxy(subject);
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(subjectProxy.getClass().getClassLoader(), subject.getClass().getInterfaces(), subjectProxy);
        proxyInstance.hello("world");
    }
}

引用java动态代理详解这篇文章中对invoke和Proxy.newProxyInstance的解释:
invoke:
invoke解释
Proxy.newProxyInstance:
Proxy.newProxyInstance
InvocationHandler.invoke所干的事情,其实就是先前我们写的伪代码中的假设的那样:

class Proxy{
	//被代理类作为参数传入
	public Method(Target target){
		System.out.println("夹带私货部分代码");
		target.Method();
		System.out.println("为所欲为代码");
	}

不过是利用反射的方法来进行的方法调用,InvocationHandler.invoke 方法的设计意图是作为代理对象和目标对象之间的桥梁,当代理对象上的方法被调用时,由 JVM 内部的机制自动调用 invoke 方法,并将方法调用信息(如方法名、参数等)作为参数传递给 invoke 方法。

在动态代理中,代理对象是通过 Proxy.newProxyInstance 方法创建的,该方法接受三个参数:类加载器、目标接口数组以及 InvocationHandler 实例。返回的代理对象实现了指定的接口,并在内部与 InvocationHandler 实例关联。当代理对象上的方法被调用时,JVM 会自动调用与代理对象关联的 InvocationHandler 实例的 invoke 方法。

按照我的理解,到InvocationHandler.invoke这一步,已经基本能知道jdk动态代理要干的事情了,感觉上说如果你可以自己手动创建 Method 对象并调用 invoke 方法,就不需要 Proxy.newProxyInstance 这一步了,但是为什么还是需要呢?
动态代理
这样并没有生成代理对象。其实按照我的理解就是太麻烦了,目标是无感代理,想想静态代理,直接targetObject.method()就可以调用代理了,所以按照我的理解就是Proxy.newProxyInstance要做的就是继续封装掉一些不需要用户关注的细节,更加自动化,就像RPC中利用动态代理屏蔽网络传输细节一样。

Proxy.newProxyInstance

Proxy.newProxyInstance的源码:
Proxy.newProxyInstance源码探究

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值