提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
一、Java代理模式?
概念:Java代理模式(Proxy Pattern)
是一种结构型
设计模式,它允许为另一个对象提供一个代替或占位符对象以控制对它的访问。代理模式可以在不直接使用实际对象
的情况下,提供对目标对象的访问。(为真实对象提供代理,然后供其他对象通过代理访问真实对象
)
代理形式有三种:分别是静态代理
,动态代理
和cglib动态代理
主要角色:
代理(Proxy)
:TeacherDaoProxy
代理对象与目标对象TeacherDao
有相同的接口,可以代替真实对象TeacherDao
进行teach
操作。
目标(Subject)
:需要被代理的真实对象TeacherDao
。
请求(Request)
:客户端通过代理对象发起的请求。
客户端(Client)
:使用代理对象TeacherDaoProxy
的代码。
使用场景:
- 网络请求代理:为网络请求创建代理,可以在请求前后添加日志记录、错误处理、线程管理等。
- 图片加载代理:使用代理模式管理图片的加载和缓存,以及处理图片的异步加载和内存优化。
- 内存管理:对象的延迟初始化,例如,通过代理模式延迟加载大对象,以减少内存消耗。
- 权限检查代理:在执行需要特定权限的操作之前,使用代理进行权限检查和请求。
- 数据访问对象(DAO)代理:为数据库访问操作创建代理,以封装数据库操作和事务管理。
二、静态代理
步骤1.编写其接口
代码如下(示例):
/**
* 静态代理——接口
*/
public interface ITeacherDao {
void teach();//代理的方法
}
步骤2.编写目标对象
代码如下(示例):
/**
* 目标对象(被代理的对象)——都要实现 ITeacherDao 接口
*/
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("林大大在上课......");
}
}
步骤3.编写代理对象
/**
* 静态代理——代理对象
*/
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target;//目标对象,通过这个对象要实现最终的调用
//TODO 通过这个构造器,把目标对象作为参数代入,从而实现真正的代理实现
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
@Override
public void teach() {
System.out.println("开始代理......");//这里可以加入一些扩展功能逻辑
target.teach();//目标对象的 teach()
System.out.println("提交....");
}
}
步骤4.编写Client
/**
1. 静态代理——客户端测试类
*/
public class Client {
public static void main(String[] args) {
//1.创建目标对象(被代理的对象)
TeacherDao teacherDao=new TeacherDao();
//2.创建代理对象,同时将目标对象传递给代理对象
TeacherDaoProxy teacherDaoProxy=new TeacherDaoProxy(teacherDao);
//3.通过代理对象,调用到目标对象的 teach() 方法(即执行的是代理对象的方法,再由代理对象去调用目标对象的方法)可断点查看每一步的走向
teacherDaoProxy.teach();
}
}
注:teacherDaoProxy.teach()
调用到目标对象的 teach()
方法(即执行的是代理对象的方法,再由代理对象去调用目标对象的方法)可断点查看每一步的走向
测试结果如下:
三、动态代理
注: 1. 代理对象不需要实现接口
,但是目标对象要实现接口,否则不能使用动态代理
2. 动态代理也叫作:JDK代理
,接口代理
步骤1.编写其接口
代码如下(示例):
/**
* 动态代理——接口
*/
public interface ITeacherDao {
void teach();//代理的方法
}
步骤2.编写目标对象
代码如下(示例):
/**
* 目标对象(被代理的对象)——都要实现 ITeacherDao 接口
*/
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("林大大在上课......");
}
}
步骤3.编写代理类
代码如下(示例):
/**
* 动态代理——代理工厂
*/
public class ProxyFactory {
private Object target;//目标对象,通过这个对象要实现最终的调用
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象生成一个代理对象
public Object getProxyInstance(){
/**
* 类加载器:target.getClass().getClassLoader(),用于定义代理类的类加载器。
* 接口数组:target.getClass().getInterfaces(),代理类需要实现的接口列表。这里使用了目标对象 target 实现的所有接口。
* 一个实现了 InvocationHandler 接口的实例:这是一个自定义的调用处理器,负责定义代理对象的方法调用逻辑。
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始了....");
Object result = method.invoke(target, args);
return result;
}
});
}
}
核心代码:
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始了....");
Object result = method.invoke(target, args);
return result;
}
});
类加载器:target.getClass().getClassLoader(),用于定义代理类的类加载器。
接口数组:target.getClass().getInterfaces(),代理类需要实现的接口列表。这里使用了目标对象 target 实现的所有接口。
一个实现了 InvocationHandler 接口的实例:这是一个自定义的调用处理器,负责定义代理对象的方法调用逻辑。
步骤4.编写Client
/**
* 动态代理——客户端测试类
*/
public class Client {
public static void main(String[] args) {
//1.创建目标对象(被代理的对象)
ITeacherDao target=new TeacherDao();
//2.创建代理对象,同时将目标对象传递给代理对象,同时需要转成 ITeacherDao
ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();
//3.通过代理对象,调用到目标对象的 teach() 方法(即执行的是代理对象的方法,再由代理对象去调用目标对象的方法)可断点查看每一步的走向
System.out.println(proxyInstance.getClass());
proxyInstance.teach();
}
}
测试结果如下:
四、Cglib代理
从上面可以看出,jdk
动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib
动态代理了,CGLIB(Code Generation Library)
是一个基于ASM
的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB
通过继承方式实现代理。
想具体实现该功能的可以看以下博客:
CGLIB详细讲解
深度思考】聊聊CGLIB动态代理原理