代理模式
在讲解动态代理之前,先来简单说一下代理模式。代理模式的作用是为某个对象提供一个代理以控制对这个对象的访问。代理对象和被代理对象需要实现相同的接口,调用者直接与代理对象交互,被代理对象对调用者来说是透明的。某些情况下,一个对象不希望或者不能被外部直接引用就可以考虑使用代理模式。
代理模式涉及到三个部分:
抽象接口:代理对象和被代理对象的共有接口;
代理对象:代理对象内部含有对真实对象的引用,从而可以操作真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
被代理对象:最终被引用的真实对象。
动态代理
动态代理是一种高级的代理模式,是代理模式的一种扩展形式,在基于AOP的框架中使用较多。
通常情况下,代理模式中的代理类和被代理类都是固定的,一个代理类对应于一个被代理类,一旦代码完成代理关系就不会再变化。如果有新的类需要被代理,就需要对应的增加一个新的代理类,这将导致系统中的类个数急剧增加,而且导致代码不容易扩展。动态代理可以让系统根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实类。
实现方式
目前Java开发包中包含了对动态代理的支持, 动态代理的实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。 Proxy类用来获取动态代理对象,InvocationHandler接口对代理接口进行处理。
Proxy类提供了newProxyInstance方法,该方法用于返回一个动态创建的代理类的实例,方法声明如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h)
参数loader表示代理类的类加载器,参数interfaces表示代理类所实现的接口列表,第三个参数h表示对应的调用处理程序类。
InvocationHandler接口是代理处理程序接口,每一个代理类的实例都可以提供一个相关的具体调用处理程序。在该接口中声明了如下方法:
public Object invoke(Objectproxy, Method method, Object[] args)
当执行代理对象中的方法时,会调用对应的InvocationHandler中的invoke方法,参数proxy表示被代理的对象,参数method表示被代理的方法,最后一个参数args表示被代理方法的参数。
优点与不足
那么使用动态代理有什么好处呢?
首先,可以减少编码工作量,假如需要实现多种代理处理逻辑,无需每种方式都写一个代理类,只需写多个代理处理器。另外,通过动态代理,可以实现方法的增强,在不修改源码的情况下,在方法添加你想做的事。
虽然有上面提到的这些好处,但是动态代理却只能代理接口,不能代理类,也就是说只有实现了某个接口的类可以使用Java动态代理机制。
实例
下面来看一个动态代理的例子。
首先定义两个接口和对应于两个接口的实现类。
public interface IEat {
void eat(String food);
}
public interface IDrive {
void drive(String car);
}
public class EatImpl implements IEat{
@Override
public void eat(String food) {
L.d("dproxy------" + "eat " + food);
}
}
public class DriveImpl implements IDrive{
@Override
public void drive(String car) {
L.d("dproxy------" + "drive " + car);
}
}
然后实现InvocationHandler接口,在它的invoke方法中通过反射调用传入的代理对象的方法,并且在方法执行前后添加想要执行的其他方法。这里我们只是在方法执行前后打印日志。
public class CustomInvocationHandler implements InvocationHandler {
private Object object;
public CustomInvocationHandler() {
}
public CustomInvocationHandler(Object ob) {
object = ob;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeInvoke();
Object result = method.invoke(object, args);
afterInvoke();
return result;
}
public void beforeInvoke() {
L.d("dproxy------" + "被代理代码开始执行");
}
public void afterInvoke() {
L.d("dproxy------" + "被代理代码执行结束");
}
}
最后通过Proxy代理类生成一个代理对象,然后利用这个代理对象去调用被代理对象的方法。
InvocationHandler handler = null;
IEat eatImpl = new EatImpl();
handler = new CustomInvocationHandler(eatImpl);
IEat proxy = null;
proxy = (IEat) Proxy.newProxyInstance(IEat.class.getClassLoader(), new Class[]{IEat.class}, handler);
proxy.eat("apple");
IDrive driveImpl = new DriveImpl();
handler = new CustomInvocationHandler(driveImpl);
IDrive proxy_new = null;
proxy_new = (IDrive)Proxy.newProxyInstance(IDrive.class.getClassLoader(), new Class[]{IDrive.class}, handler);
proxy_new.drive("train");
通过打印信息可以看到,调用代理对象的方法,实际执行了被代理对象的方法,而且还在执行前后添加了增强代码。
D/viclee ( 7309): dproxy------被代理代码开始执行
D/viclee ( 7309): dproxy------eat apple
D/viclee ( 7309): dproxy------被代理代码执行结束
D/viclee ( 7309): dproxy------被代理代码开始执行
D/viclee ( 7309): dproxy------drive train
D/viclee ( 7309): dproxy------被代理代码执行结束
欢迎关注我的公众号一起交流学习