------- android培训、java培训、java学习型技术博客、期待与您交流! ----------
java代理
1、了解代理模式
代理:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法的运行时间、事务管理等等。
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。
如图:
图 1-1
相关的类和接口
java.lang.reflect.Proxy:动态代理机制的主类。
static InvocationHandler getInvocationHandler(Object proxy) 用于获取指定代理对象所关联的调用处理器
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static boolean isProxyClass(Class<?> cl) 用于判断指定类对象是否是一个动态代理类
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。
Object invoke(Object proxy, Method method, Object[] args) 负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象,第三个方法是调用参数。
调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
简单代理过程:
Collection proxy2 = (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
Object retVal= method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+"----run---"+(endTime-startTime));
return retVal;
}
});
proxy2.add("abc");
proxy2.add("bcd");
System.out.println(proxy2);
2、动态代理
动态代理:JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被作代理类,即动态代理类。
代理方法可以放在如下四个位置:
1、在调用目标方法之前
2、在调用目标方法之后
3、在调用目标方法前后
4、在处理目标方法异常的catch块中
动态代理创建过程:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
简化过程:
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
new Class[] { Interface.class },
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
});
类继承图:
图 2-1
由图可见,Proxy 类是它的父类,这个规则适用于所有由 Proxy 创建的动态代理类。而且该类还实现了其所代理的一组接口,这就是为什么它能够被安全地类型转换到其所代理的某接口的根本原因。
每个实例都会关联一个调用处理器对象,可以通过 Proxy 提供的静态方法 getInvocationHandler 去获得代理类实例的调用处理器对象。在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都会由调用处理器的 invoke 方法执行,此外,值得注意的是,代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,可能的原因有:一是因为这些方法为 public 且非 final 类型,能够被代理类覆盖;二是因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被分派到委托类执行。当代理的一组接口有重复声明的方法且该方法被调用时,代理类总是从排在最前面的接口中获取方法对象并分派给调用处理器,而无论代理类实例是否正在以该接口(或继承于该接口的某子接口)的形式被外部引用,因为在代理类内部无法区分其当前的被引用类型。
InvocationHandler类
让jvm创建动态类及其实例对象,需要:
生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
产生的类字节码必须有一个关联的类加载器对象;
生成的类中的方法的代码怎样的,也得由我们提供。把我们的代码
写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法
即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象
它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,
就可以看到这些代码被调用运行了。
动态代理的工作原理:
将目标对象和新添加方法抽取出来新建方法
Advice---记住
public interface Advice{
public void beforeMethod(参数);
public void afterMethod(参数);
}