1、什么是代理模式
定义:给目标对象提供一个代理对象,并由代理对象控制目标对象的引用。
在代理模式中需要代理对象和目标对象实现同一个接口,看下UML图
2、为什么要用代理
代理的作用:使用代理模式,可以在不修改目标对象方法的前提下,对方法进行包装增强。
举个例子
public interface IPerson{
void say();
}
public class Student implements IPerson{
@Override
public void say() {
Log.e("TAG","Student Say");
}
}
Student类
实现了IPerson
接口,如果我们想在say()
被调用时记录下调用的时间,最粗暴的方式就是在say()
中直接增加时间记录的方法,但是如果项目中有很多类都实现了IPerson
那么就需要修改很多类,而且并不是所有类都需要记录调用时间,那该怎么处理呢?肯定是使用代理啊。
3、静态代理
public interface IPerson {
void say();
}
public class Student implements IPerson {
@Override
public void say() {
Log.e("TAG", "Student Say");
}
}
public class StudentProxy implements IPerson {
private IPerson target;
public IPerson getTarget() {
return target;
}
public void setTarget(IPerson target) {
this.target = target;
}
@Override
public void say() {
Log.e("TAG", "记录目标对象调用say的时间");
target.say();
}
}
我们需要创建一个代理类StudentProxy
同样需要实现接口IPerson
,将要代理的对象传进来,这样就可以不需要修改目标对象Student
的say()
方法来实现我们的需求了。这就是静态代理,由于静态代理类和接口是一一对应的,如果有多个接口需要代理的话,就需要创建多个代理类,所以动态代理就应运而生。
静态代理的特点
- 1、一个静态代理类只能对应一种目标对象,即静态代理类和接口一一对应。
- 2、如果有多个接口需要代理,就需要创建多个静态代理类对象。
4、动态代理
4.1、动态代理的原理
- 1、设计动态代理类时,不需要显式的实现和目标类对象相同的接口。而是将这种实现推迟到运行时进行实现。
- 2、调用动态代理类对象的方法时,会通过
反射机制
的method.invoke()
,从而自动调用目标类对象的方法。
4.2、使用步骤
- 1、声明 调用处理器类
- 2、声明 目标对象类的接口
- 3、声明 目标对象类
- 4、通过动态代理对象调用目标对象的方法
步骤1:声明 调用处理器类
/**
* 作用:
* 1、生成 动态代理类对象
* 2、指定 代理对象调用目标对象方法前要完成的任务
* 注:需要实现调用处理器接口即InvocationHandler,故该类是调用处理器类
*/
public class InvocationHandlerImpl implements InvocationHandler {
//声明 被代理对象
//作用:指定该对象类实现的一组接口中,那些方法被调用时,执行invoke
private Object proxyObject;
public Object newInstance(Object proxyObject) {
this.proxyObject = proxyObject;
return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this);
//Proxy.newProxyInstance() 根据指定的类加载器、目标类实现的一组接口和调用处理器类 创建动态代理类对象
//参数1:类加载器 需要和目标类对象为同一个类加载器
//参数2:一组接口 目标类实现的接口,动态代理类会默认实现这组接口
//参数3:调用处理器类
}
/**
* 动态代理类对象调用目标类方法前会调用invoke()
* @param proxy 动态代理对象
* @param method 目标对象被调用的方法
* @param args 目标对象被调用方法的参数
* @return 目标对象被调用方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result=null;
Log.e("Proxy","目标方法调用前");
//通过反射调用目标类对象方法
result=method.invoke(proxyObject,args);
Log.e("Proxy","目标方法调用后");
return result;
}
}
步骤二:声明目标类要实现的接口
/**
* 目标对象类要实现的接口
*/
public interface Subject {
//定义目标对象类的接口方法
public void buy(String name);
}
步骤三:声明目标类
/**
* 声明目标对象类并实现接口
*/
public class RealObject1 implements Subject {
@Override
public void buy(String name) {
Log.e("Proxy",name+"想买电脑");
}
}
public class RealObject2 implements Subject {
@Override
public void buy(String name) {
Log.e("Proxy", name+"想买平板");
}
}
步骤四:通过动态代理对象调用目标类方法
InvocationHandlerImpl dynamicProxy = new InvocationHandlerImpl();
RealObject1 realObject1 = new RealObject1();
Subject realObject1Proxy = (Subject) dynamicProxy.newInstance(realObject1);
realObject1Proxy.buy("小红");
RealObject2 realObject2 = new RealObject2();
Subject realObject2Proxy = (Subject) dynamicProxy.newInstance(realObject2);
realObject2Proxy.buy("小兰");
5、源码解析
看到这里会有如下2个疑问:
- 1、动态代理类对象是如何生成的?
- 2、如何通过调用动态代理类对象,来调用目标类对象?
5.1、动态代理类和对象是如何动态生成的
Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this)
上面是创建动态代理类对象的代码,我们就从Proxy.newProxyInstance()
入手
private final static Class[] constructorParams =
{ InvocationHandler.class };
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
//通过类加载器(和目标类对象同一个类加载器)和一组接口(目标类实现的接口)创建动态代理类
Class<?> cl = getProxyClass0(loader, interfaces);
try {
//通过反射获取动态代理类的带参(参数类型为InvocationHandler)构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
//通过动态代理类的构造函数,创建动态代理类对象,参数为h
return newInstance(cons, h);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
该方法主要做了如下几件事:
- 1、根据类加载器和一组接口创建动态代理类
- 2、获取动态类的带参构造函数
- 3、反射创建动态代理类对象
5.2、如何通过调用动态代理对象方法来调用目标类方法
//使用代码
realObject1Proxy.buy("小红");
下面直接看动态代理类的实现
/**
* 动态代理类的实现
*/
public class ProxySubject extends Proxy implements Subject {
//构造函数 在动态代理类的创建时获取带有一个参数(调用处理器类型)的构造函数就是这个
protected ProxySubject(InvocationHandler h) {
super(h);
}
//buy是目标类实现接口(Subject)中的方法,所以动态代理类也需要实现该接口
@Override
public final void buy(String name) {
try {
//该方法实际上调用了父类Proxy类中的h的invoke方法
//h即是在Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(),proxyObject.getClass().getInterfaces(),this);中传入的第三个参数InvocationHandler对象
//即调用了调用处理器的InvocationHandler.invoke()
//而在复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
//从而调用目标对象的的方法
super.h.invoke(this, method, args);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
总结:
- 1、动态代理类实现了和目标类相同的接口,并实现了接口的方法
- 2、方法中的实现逻辑为:通过
super.h.invoke()调用
InvocationHandler的
invoke()方法
- 3、在
InvocationHandler
中的invoke
中通过反射调用目标类的方法。
网友评论