代理模式就是为其他对象提供一种代理以控制对这个对象的访问。
类似于我们生活中请律师帮我们打官司,虽然不请律师也可以自己打官司但是请了律师之后不仅可以代替自己陈述还可以帮我们在打官司的前后处理一些事情(我们不愿意干、或者干不了的东西).
在java中的代理主要分为以下几种方式.
1.静态代理
静态代理就是定义一个接口或者是父类,然后代理类与被代理类都需要继承这个接口,在代理类中实现一个方法,需要注入被代理类,这样就能通过代理类去访问被代理类,同时,也可以对被代理类进行增强.
Client:客户端
Target:被代理类
Proxy:代理类
代码实现:
1.先写一个接口,里面有一个say这个方法
2.创建一个Slef这个类继承Person这个接口并且实现say这个方法
3.创建Self的代理类,需要在代理类里面引入Self,调用被代理类的say的方法.这里就是Self的say方法.
就像律师在代理的时候要知道为谁代理什么案件一样.
4.测试结果:
优点:可以在不改变被代理类的情况下,扩展一些功能.
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
2.动态代理
2.1 jdk代理
所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:InvocationHandler和Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:
loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载【一般我们使用的是被代理类的装载器】
interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。【指定要被代理类的接口】
h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。(生成的代理对象的方法里干什么事,实现handler接口,我们想怎么实现就怎么实现)
(以上引用随风yy 原创文档. 原文链接:https://blog.csdn.net/yaomingyang/article/details/80981004)
1.编写一个类实现InvocationHandler接口
2.调用
动态代理工厂式:
2.调用:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
2.2 Cglib代理
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
1.编写没有接口的被代理类
2.实现 MethodInterceptor.
3.调用
Cglib代理工厂客户端代码中首先创建了一个Enhancer对象,并且设置了父类及代理回调类对象。该Enhancer对象会为目标类创建相关的子类字节码,并且将代理代码植入该子类字节码中。
调用
在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理
总结
本文主要对代理模式的三种实现方式进行了详细讲解,并且比较了各个代理方式的优缺点,Spring主要使用的是动态代理方式实现切面编程的。这里读者可能会有一个疑问,即上述代理代码中,根据实现方式的不同,对客户端代码都有一定的侵入性,比如静态代理客户端需要侵入代理类的实例,jdk代理需要侵入Proxy类,而Cglib代理则需要侵入子类子类对象创建等代码。理论上,客户端只需要获取目标对象,无论是否为代理过的,然后调用其相关方法实现特定功能即可。这其实也是工厂方法的强大之处,因为工厂方法会将对象的创建封装起来,对象的具体创建过程可以根据具体的业务处理即可,客户端只需要依赖工厂类调用相关的方法即可。同样的这也就说明了Spring IoC容器是天然支持AOP代理的原因,因为其将对象的创建过程交由容器进行了。
参考:https://www.cnblogs.com/zhangxufeng/p/9162182.html
https://www.cnblogs.com/leeego-123/p/10995975.html