在学习动态代理之前,需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。
代理模式是常用的java设计模式,他的特点是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,它们之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
静态代理是由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成,缺点是接口与代理类是1v1,有多个接口需要代理,就需要新建多个代理类,比较繁琐。
显示方法运行的时间
UserServiceTarget 目标类 实现了UserService
TimeHandler 包含了重复代码,间接调用目标
UserServiceProxy 代理类 实现了UserService
接口
public interface UserService {
public void b();
public void c();
}
委托类实现接口
// 目标类
public class UserServiceTarget implements UserService {
public void b() {
System.out.println("b");
}
public void c() {
System.out.println("c");
}
}
代理类实现接口
// 静态代理 - 自己编码实现的代理类
// 动态代理 - jdk可以在运行期间,动态地生成这个代理类
// proxy 代理 (封装复杂逻辑,它去间接调用 TimeHandler 和 业务代码), 让使用者觉得简单
public class UserServiceProxy implements UserService {
public void b() {
try {
TimeHandler timeHandler = new TimeHandler();
//得到方法 类名.class.getMethod(方法名)
Method b = UserServiceTarget.class.getMethod("b");
//反射调用,传递方法,下面的计算类里就可以调用此方法
timeHandler.invoke(b);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public void c() {
try {
TimeHandler timeHandler = new TimeHandler();
Method c = UserServiceTarget.class.getMethod("c");
timeHandler.invoke(c);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
计算时间的代码
public class TimeHandler {
private UserServiceTarget userService = new UserServiceTarget();
// b() c()
public void invoke(Method method) {
System.out.println("==================================");
long start = System.nanoTime();
// 调用这个方法 method
// 正常调用: 对象.方法名(参数)
// 反射调用: 方法.invoke(对象, 参数);
try {
//调用 UserServiceTarget 中的方法
method.invoke(userService);
} catch (Exception e) {
e.printStackTrace();
}
long end = System.nanoTime();
System.out.println("花费了: " + (end - start));
}
}
测试类
public class TestTimeHandler {
public static void main(String[] args) {
UserServiceProxy proxy = new UserServiceProxy();
proxy.b();
proxy.c();
}
}
结果
==================================
b
花费了: 201800
==================================
c
花费了: 65691
原始编写java 代码的方式是从java文件编译为class文件,然后class文件经过翻译后变为机器语言,而动态代理就是体现了class文件到机器语言这一过程。
- 原始编写java 代码的方式
.java -> javac -> *.class -> 类加载
- 动态代理
.class -> 类加载
显示方法运行的时间
UserServiceTarget 目标类 实现了UserService
TestDynamicProxy 包含了计算时间代码,间接调用目标
UserServiceProxy 代理类 实现了UserService
接口
public interface UserService {
public void a(int x, int y);
public void b();
public void c();
}
目标类(委托类)
// 目标类
public class UserServiceTarget implements UserService {
public void a(int x, int y) {
System.out.println("a");
}
public void b() {
System.out.println("b");
}
public void c() {
System.out.println("c");
}
}
测试类
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// dynamic 动态的
// proxy 代理
public class TestDynamicProxy {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 1. 生成了类的字节码
// 参数1 string 表示代理类的名字,随意起
// 参数2 class[] 表示代理类要实现的接口
// 返回的结果就是 代理类 的字节码
byte[] bytes = ProxyGenerator.generateProxyClass("UserServiceProxy2", new Class[]{UserService.class});
// 2. 执行类加载
// 类加载的最终结果体现为 Class 类对象, 要借助类加载器
ClassLoader cl = new ClassLoader() {
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
return defineClass(name, bytes, 0, bytes.length);
}
};
Class c = cl.loadClass("UserServiceProxy2"); // 进行类加载, 获得了 UserServiceProxy 类对象
// 3. 创建代理类实例对象
// 不能正常创建它的实例对象, new UserServiceProxy2();
// 获取代理类的构造方法
Constructor constructor = c.getConstructor(InvocationHandler.class);
UserServiceTarget target = new UserServiceTarget();
// 创建实例对象, 强制转换为它的接口类型
UserService proxy = (UserService)constructor.newInstance(new InvocationHandler() {
@Override
// method 就是 a , b, c 等方法对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入了 invoke");
long start = System.nanoTime();
// 方法.invoke(目标, 参数); 通过这行代码调用目标类的方法
method.invoke(target, args);
long end = System.nanoTime();
System.out.println("花费了:" + (end - start));
return null;
}
});
// 4. 使用代理对象
proxy.b();
proxy.c();
proxy.a(10,20);
}
}
结果
进入了 invoke
b
花费了:682903
进入了 invoke
c
花费了:233935
进入了 invoke
a
花费了:267017
以上两种代理方法可以看出,静态代理中的方法实现太过繁琐(业务逻辑多),相比之下,动态代理的业务逻辑就简洁多了。
简而言之,动态代理也就是间接的调用委托的方法,并完善方法的过程,这样使代码简洁,并且在编码时,代理逻辑与业务逻辑相互独立,个不影响,没有侵入,没有耦合(耦合也就是一处代码修改,另一处代码也要修改,如果偶合性强,那么修改一处代码时,需要改多处代码,才可以把程序修改完毕,反之则修改处少)。