大家好,我是一位在java学习圈中不愿意透露姓名并苟且偷生的小学员,如果文章有错误之处,还望海涵,欢迎多多指正
如果你从本文 get 到有用的干货知识,请帮忙点个赞呗,据说点赞的都拿到了offer
在开始动态代理前,先对之前学的静态代理做一个小结(我主页里的另一篇文章):
总结:
应用场景:在用户不知情的情况下我们想要修改原有的执行过程,可以想到用静态代理模式
抽象角色-Subject:可以是抽象类,也可以是接口
真实角色/委托类-RealSubject:业务逻辑的具体执行者
代理角色/代理类-Proxy:把所有抽象主题角色定义的方法限制委托给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后处理(其实就是做手脚)
隐藏委托类,在一定程度上实现了解耦合,同时提高了安全性,符合开闭原则(扩展开放、修改封闭)
实现:一个公共接口、一个委托类、一个代理类(代理类中有委托类的对象当属性)
优点:简单、实用、效率高
缺点:需要实现接口,造成代码冗余,只能对固定接口的实现类实现代理,灵活性较差(相对于动态代理而言)
问题引申:
(1),什么是动态代理呢?依据之前学的静态代理我们不难猜测动态代理应当是动态生成代理类,那么应该是显示声明代理对象(注意这个代理对象是动态的,实际上体现在代码里就是参数),在编译期就生成了代理类
由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行之前,代理类的类文件就已经被创建了
(2),动态代理
1.现实生活中 除了iPhone以外 好多其他厂商的手机(这里举的例子类比于之前的茶农与卖茶女的例子)
vivo oppo huawei
设计一个规则(接口)
2.真正的华为专卖店比较少
为了让更多的用户能买到心仪的手机
设计静态代理----->>>>
像是一个华为的特许认证加盟店
代理即便是卖山寨的 也得是华为
3.动态代理
设计动态代理----->>>>
代工厂----华为工厂
不光能代工华为的产品 还能作别的
想要实现动态代理
需要一个类Proxy
需要了解反射机制
同样这里通过例子来实现动态代理
动态代理时可以先考虑一个类似茶农卖茶的例子–》手机制造商卖手机(这里拿华为和苹果举例)
假设苹果和华为厂商生产的手机不想自己卖了,想找人卖,恰巧他们两个都找了同一个厂来帮忙卖(动态代理类),这样就很方便,这个厂无论什么东西都可以代理销售,于是苹果和华为都变成甩手掌柜了(变成接口定义规则了),来简单实现一下这个例子;
分析过程:
首先在没有代理商帮忙华为店和苹果店卖手机之前,华为店和苹果店都是靠自己卖(自产自卖)
我们来设计两个与动态代理无关却又能引申出动态代理的类,华为店类和苹果店类,代码如下:
注意:很多分析过程都在代码注释中,小伙伴们注意理解即可
华为店:
package proxydynamic;
public class RealHuaWeiStore{
//一个负责制造手机的方法(可以理解为是一个小工厂)
private Mate30 productPhone(){
//需要先造一部手机
Mate30 mate30 = new Mate30();
//将造出的手机返回
return mate30;
}
//设计一个最主要的方法 卖手机 商店--手机的依赖关系
public MobilePhone sellMobilePhone(){
//通过当前类自己的方法先造一部手机
Mate30 mate30 = this.productPhone();
//将手机返回出去 相当于给了用户
return mate30;
}
}
苹果店:
package proxydynamic;
/**
* 这个类描述的是一个真正的苹果专卖店
* 类中有两件事情
* 1.有一个自己的方法 (可以理解为是一个小工厂) 负责制造手机
* 2.有一个方法 负责卖手机
*
* 无论你对于面向对象多么熟悉
* 一定在设计之前考虑清楚 关于方法的参数 返回值等问题
* 这些问题决定了设计的类和方法是否合理----很关键
*
* 这个类是真正的工厂店
* 工厂店可以生产 可以卖手机
*
* 工厂店生产太麻烦啦 自己需要准备好多原材料
* 工厂店自己不想生产啦 想要找代工厂()
* 如果工厂店找了代工厂帮忙生产 工厂店还需要自己生产么???
* 工厂店就没有必要了具体做事了(在一旁指挥---接口规则)
* 即工厂告知代工厂如何去做事情
*/
public class RealAppleStore{
//一个负责制造手机的方法(可以理解为是一个小工厂)
private iPhone productPhone(){
//需要先造一部手机
iPhone iPhone = new iPhone();
//将造出的手机返回
return iPhone;
}
//设计一个最主要的方法 卖手机 商店--手机的依赖关系
public MobilePhone sellMobilePhone(){
//通过当前类自己的方法先造一部手机
iPhone iPhone = this.productPhone();
//将手机返回出去 相当于给了用户
return iPhone;
}
}
引申一下上面的两个店,首先我们拿华为店举例,正常情况下我们都是new 一个原始的华为店来卖手机,但现在我们不想让华为店来做这些琐事了,因此我们找了一个代理工厂帮我们根据我们传递的参数动态的造一个相应的代理对象(即接口店的子类对象),让这个代理对象来帮我们卖手机(具体实现过程写在invoke方法中即可),现在开始设计动态代理,首先是华为手机:
package proxydynamic;
/**
* 华为生产的手机
* 给手机设计三个功能 打电话 玩游戏 拍照
*/
public class Mate30 implements MobilePhone{
@Override
public void call() {
System.out.println("这是一个华为手机,打电话很通畅");
}
@Override
public void play() {
System.out.println("这是一个华为手机,玩游戏不卡");
}
@Override
public void photo() {
System.out.println("这是一个华为手机,拍照很清晰");
}
}
接着是苹果手机:
package proxydynamic;
/**
* 苹果生产的手机
* 给手机设计三个功能 打电话 玩游戏 拍照
*/
public class iPhone implements MobilePhone{
@Override
public void call() {
System.out.println("这是一个苹果手机,打电话很通畅");
}
@Override
public void play() {
System.out.println("这是一个苹果手机,玩游戏不卡");
}
@Override
public void photo() {
System.out.println("这是一个苹果手机,拍照很清晰");
}
}
既然都是手机就有一个手机接口规则:
package proxydynamic;
/**
* 这是一个接口
* 接口出现的目的是为了让所有厂商的手机有一些统一的规则
*/
public interface MobilePhone {
//public abstract在接口中可省略不写
//手机的打电话功能
void call();
//手机的玩游戏功能
void play();
//手机的照相功能
void photo();
}
设计一个空壳子华为店(接口):
package proxydynamic;
public interface HuaWeiStore {
MobilePhone sellMobilePhone();
}
设计一个空壳子苹果店(接口):
package proxydynamic;
/**
* 工厂店 只定义生产规则 不需要自己去具体生产
*
* 用户原来找真实的RealAppleStore 买手机
* RealAppleStore背后有一个小弟 (富士康)动态代理 除了能做苹果还能做别的
*
* 用户现在找真实的AppleStore 买手机
* AppleStore是不具体做事 只负责指挥(做不了事 背后偷偷的找了代理做事)
*
* 用户觉得找到AppleStore买的手机 其实是背后的动态代理做的事情
*
*/
public interface AppleStore {
//工厂店只定义规则 让代工厂去帮我做事
MobilePhone sellMobilePhone();
//这个方法抽象的 不能做事
//有一个子类将这个方法实现掉 子类就是那个代理(富士康)
// 子类具体该做的事情
// 1.生产手机
// 2.卖手机
}
找一个代工厂帮我们来动态生产各种店(注意仔细理解这个根据参数动态造代理对象的工厂的作用,理解注释意思):
package proxydynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 这是一个生成代理对象的工厂
* 根据给定的类型造一个相应的对象出来
*/
public class ProxyFactory {
//设计一个方法 负责获取一个对象 代理对象(AppleStore的子类)
//参数 是传递进来的接口 AppleStore(负责指挥)
//返回值 根据参数创建出来的一个子类对象(代理对象)
public static <T>T getStore(Class clazz){
//Java给我们提供一个类 Proxy 帮我们创建一个代理对象
// 想要创建一个代理对象 需要知道三个参数
//1.ClassLoader loader 一个ClassLoader对象,定义了由哪个ClassLoader对象,找到对应的包路径
// 来对生成的代理对象进行加载(动态代理机制帮我们编写代理类,classLoader告知在哪个包隐式编写代理类)
ClassLoader classLoader = clazz.getClassLoader();//加载AppleStore
//2.Class<?>[] interfaces 一个Interface对象的数组,表示的是我将要给我需要代理的对象(即代理对象)
// 提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),
// 这样我就能调用这组接口中的方法了(即代理对象与目标对象有相同的api<===>有相同的方法)
Class[] classes = new Class[]{clazz};
//3.InvocationHandler h 一个InvocationHandler对象,表示的是当我这个动态代理对象
// 在调用方法的时候,会关联到哪一个InvocationHandler对象上(具体的调用者,我们调用的代理对象
// 代理对象就调用handler的invoke方法)
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法里面带参数:
//1.Object proxy 指代我们所代理的那个真实对象
//2.Method method 指代的是我们所要调用真实对象的某个方法的Method对象
//3.Object[] args 指代的是调用真实对象某个方法时接受的参数
//想让这个工厂根据传递的参数 代工不同的产品
//1.获取需要被代工的类
Class clazz = method.getDeclaringClass();
//2.判断clazz从而执行不同的代工
if(clazz == AppleStore.class){
return new iPhone();
}else if(clazz == HuaWeiStore.class){
return new Mate30();
}else{
return null;
}
}
};
//Proxy这个类的作用就是用来动态创建一个代理对象的类
//而newProxyInstance方法就得到这个类的对象 这个类对象指向传进来的那个类,多态的效果
T proxy = (T)Proxy.newProxyInstance(classLoader,classes,handler);
//将创建好的那个代理对象返回
return proxy;
}
}
测试类:
package proxydynamic;
public class TestDynamicProxy {
public static void main(String[] args){
//获取AppleStore接口对应的子类对象,这个子类不是我们创建的,是Proxy类帮我们创建的
AppleStore appleStore = ProxyFactory.getStore(AppleStore.class);
//获取HuaWeiStore接口对应的子类对象
HuaWeiStore huaWeiStore = ProxyFactory.getStore(HuaWeiStore.class);
//对象调用方法,这里通过反射来调用sellMobilePhone方法,
//注意调用的不是接口中定义的sellMobilePhone方法(没有具体实现),
//调用的是动态代理产生的代理对象所实现的方法,具体实现过程在invoke方法中
MobilePhone iPhone = appleStore.sellMobilePhone();
MobilePhone mate30 = huaWeiStore.sellMobilePhone();
iPhone.call();
iPhone.photo();
iPhone.play();
mate30.call();
mate30.photo();
mate30.play();
}
}
总结 + 补充 + 拓展:
补充:
javaAPI对 InvocationHandler 接口的描述:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
意思就是:
调用处理程序是由代理实例的调用处理程序实现的接口。 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,对方法调用进行编码,并将其分派给其调用处理程序的 invoke 方法。(通俗的说就是:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用)
javaAPI对 Proxy 类的描述:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
意思就是:
Proxy 为创建动态代理类和实例提供了静态方法,它也是由这些方法创建的所有动态代理类的超类(父类)
另外:
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用
总结:
在程序运行时通过反射机制动态创建代理类
优点:只需将被委托类作为参数传入即可,使用灵活;服务内容只需写在invoke方法中,减少了代码冗余
缺点:效率较低
基于JDK实现:通过JDK提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用)字节码文件并实例化对象返回。由Java内部的反射机制来实例化代理对象,并代理的调用委托类方法
拓展:
动态代理的其他实现方式:
基于CGlib实现:基于继承被代理类生成代理子类,不用实现接口,只需被代理类为非final类,底层借用ASM字节码技术实现
基于AspectJ实现:修改目标类的字节,织入代理的字节,在程序编译的时候,插入动态代理的字节码,不会生成全新的class
基于Instrumentation实现:修改目标类的字节码,类装载的时候动态拦截,插入动态代理的字节码,不会生成全新的class,基于JavaAgent
SpringAOP的实现基础
ASM:Automatic Storage Management自动存储管理
AOP(面向切面的编程)三剑客:
APT:Annotation Processing Tool 注解处理器
AspectJ:面向切面的框架
Javassit:开源的分析、编辑和创建Java字节码的类库
看完了小伙伴们是不是对动态代理有了更深刻的理解呢?希望这篇文章能帮到小伙伴们更好的理解动态代理模式
之后开始慢慢的更新每一种设计模式,通过生动形象的生活现象举例带你感受设计模式的世界,其实设计模式不难,只是当我们面对某个场景时想不到用哪个设计模式该不该用设计模式…
博客内容来自腾讯课堂渡一教育拓哥,以及自己的一些理解认识,同时看了其他大牛写的设计模式技术文章综合总结出来的