java之动态代理
程序员文章站
2022-06-09 20:40:47
...
Java之动态代理
基本说明
-
我们的目标是:在不改变目标对象方法的情况下对方法进行增强!
-
动态代理类的字节码在程序运行时由Java反射机制动态生成,而无需手动编写它的源代码。
- 简化了编程工作
- 提高了软件系统的可扩展性
-
Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
例子
- 目标:现在有一个linux命令的接口
LinuxService
,接口中有个方法是创建目录的方法mkdir
,想在创建目录的时候能指定一个目标路径。但是不改变LinuxService
中的方法。 - 方案:使用动态代理,在使用
LinuxService
接口中的mkdir
时,代理对象指定目录,但在调用方看来,使用的就是LinuxService
类的实例中的mkdir
方法。
代码
接口LinuxService
public interface LinuxService {
/**
* make directory, 创建目录
*/
void mkdir();
/**
* 如果文件不存在,则会建立空文件;如果文件已经存在,则会修改文件的时间戳
*/
void touch();
/**
* 编辑文件
*/
void vim();
/**
* 连接文件并打印输出到标准输出 查看文件内容
*/
void cat();
}
实现类LinuxServiceImpl
public class LinuxServiceImpl implements LinuxService {
private static String centreMessage = "我的核心实现是:";
@Override
public void mkdir() {
System.out.println(centreMessage+"创建一个文件夹!");
}
@Override
public void touch() {
System.out.println(centreMessage+"创建一个用来编辑的文件!");
}
@Override
public void vim() {
System.out.println(centreMessage+"打开上面的文件并编辑!");
}
@Override
public void cat() {
System.out.println(centreMessage+"查看编辑过后的文件!");
}
}
想要代理接口的代理对象
public class LinuxServiceProxy implements InvocationHandler {
/**
* 要代理的核心对象 B是A的代理,则target=A
* B = LinuxServiceProxy A = LinuxService
*/
private Object target;
public LinuxServiceProxy(Object target){
this.target = target;
}
/**
* jdk动态代理
* @param proxy 指被代理的对象。
* @param method 要调用的方法
* @param args 方法调用时所需要的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("=======代理对象开始工作========");
System.out.println("进入/Users/mac/Documents目录下后,创建一个新目录test,之后在/test中创建文件以及编辑文件!");
//执行真正的核心的方法 即执行LinuxService中接口中真正关心的方法。
Object result=method.invoke(target, args);
System.out.println("=======执行结束后跟我说一下========");
return result;
}
}
执行类
public class TestLinuxProxy {
public static void main(String[] args) {
/**
* 有一个linux方法的接口,它定义了要实现这些方法的规范,LinuxServiceImpl是接口的具体实现。
* 我要用它方法的具体实现,但我想先指定一个目录。
*/
LinuxService linuxService = new LinuxServiceImpl();
/**
* 想指定一个目录,但是LinuxService并没有提供指定目录的功能
* 我去找它的代理。
*/
LinuxServiceProxy linuxServiceProxy = new LinuxServiceProxy(linuxService);
/**
* Proxy类是专门完成代理的操作类,通过newProxyInstance方法为接口动态的生成实现类。
* 尽管我想指定一个目录, 但我核心是想调用LinuxService中的方法,因为我最终要调用的对象应该是 LinuxService下面的方法。
* 底层的实现我不在乎,在乎的是我调用的是LinuxService接口中的方法
*/
/**
* 三个参数的说明:
* ClassLoader loader:类加载器
* Class<?>[] interfaces:得到全部的接口
* InvocationHandler h:得到InvocationHandler接口的子类实例
*/
LinuxService asr = (LinuxService) Proxy
.newProxyInstance(linuxService.getClass().getClassLoader(),
linuxService.getClass().getInterfaces(),
linuxServiceProxy);
/**
* 调用生成目录的方法,用返回的代理。
*/
asr.mkdir();
}
}
结果
=======代理对象开始工作========
进入/Users/mac/Documents目录下后,创建一个新目录test,之后在/test中创建文件以及编辑文件!
我的核心实现是:创建一个文件夹!
=======执行结束后跟我说一下========
上一篇: JAVA SE回顾--集合(2)
下一篇: JAVA SE集合容器