说道Java的代理模式,很可能就想到类Proxy和接口InvocationHandler两个东西,事实上代理模式的思想应用还是不止于此,本篇就做个梳理;
先看看InvocationHandler
//proxy:宿主对象的代理对象
//method:宿主对象的某个方法
//args:method方法所需的参数
public Object invoke(Object proxy, Method method, Object[] args)
需要注意第二个参数Method,该对象表明的是宿主对象的某个方法。该对象使得我们可以通过其invoke方法执行宿主的某个方法:
//obj:当前方法所属的对象,不能为null
//args:当然方法所需的参数
Object invoke(Object obj, Object... args)
所以我们在执行method的invoke方法时需要这么做(伪代码):
//初始化一个宿主对象
SubjectImpl subject = new SubjectImpl();
//执行subject对象的某个方法
method.invoke(subject,params);
综合上面的说明,所以网上关于动态代理的例子就很好理解为什么是如下所示了:
/**
* 实现了InvocationHandler接口的类:
*/
public class SubjectInvocationHandler implements InvocationHandler {
//宿主对象
private ISubject subject;
public SubjectInvocationHandler(ISubject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("--代理类工作:实际是宿主在执行相关的invoke方法--");
return method.invoke(subject, args);
}
/**
* 为宿主对象subject创建代理类对象
* @return
*/
public ISubject createProxy() {
ISubject subjectProxy = (ISubject) Proxy.newProxyInstance(getClass().getClassLoader(),
subject.getClass().getInterfaces(),
this);
return subjectProxy;
}
}
public interface ISubject {
void doSomething();
}
public class SubjectImpl implements ISubject {
public void doSomething() {
System.out.println("SubjectImple do Somthing");
}
}
客户端的调用也很简单:
SubjectInvocationHandler subjectInvocationHandler = new SubjectInvocationHandler(new SubjectImpl());
//运行时创建代理类对象
ISubject proxy = subjectInvocationHandler.createProxy();
//代理执行doSomething
proxy.doSomething();
这段代码仿写很容易,但是有什么用呢?换句话说我都拿到了SubjectImpl还费劲创建一个代理对象干嘛?从上面的代码来看proxy.doSometing()执行的仍然是SubjectImpl这个宿主类的doSmething方法。何必多此一举不直接使用宿主呢?
回答这个问题之前先回顾一下问题的本质:什么是动态代理?代理类或者对象在程序运行时创建的代理被成为 动态代理。动态代理模式理论上的有点就不细说了,关于更多的讨论可以参考知乎的这篇文章。下面就结合实际例子来看看动态代理的强大作用。
作为android开发人员,Retrofit这个网络框架可能都不陌生,该框架就是借助于动态代理实现了这么牛叉的网络框架。使用该框架你需要先定制一个接口:
public interface MyService{
@FormUrlEncoded
@POST("you url")
Call loadData(@FieldMap Map params);
}
上面的接口很简单,就是配置了三个注解@FormUrlEncoded,@POST,@FieldMap
.关键是我们在使用这个接口进行网络请求的时候,没有自己implements MyService,直接如下调用即可:
MyService myService = retrofit.create(MyService.class)
myService.loadData(map);
从上面的代码看,调用Retrofit对象的create方法即可创建一个MyService对象;其实也没啥神秘的地方,就是使用了动态代理模式:
public T create(final Class service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service },
new InvocationHandler() {
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
ServiceMethod
看到了吧,在create方法中主要是Proxy.newProxyInstance动态创建了MyService的代理对象,在我们执行loadData的时候调用代理对象的invoke方法;通过invoke方法的第二个参数Method对象,我们可以拿到loadData方法配置的注解信息以及loadData方法的map参数的值,从而执行网络请求。只不过Retrofit又将method封装到了ServiceMethod里面而已。从Retrofit的例子中相信你们可以意识到了,动态代理其实不需要宿主对象也能发挥其强大的作用。在retrofit我们只是拿到了接口方法的Method对象,然后通过操控method对象获取注解以及参数信息来执行网络请求而已。关于Retrofit的更多细节,可以点此了解更多
实际上动态代理理念的运用不仅仅体现在上面的Proxy和InvocationHandler接口。其思想运用的很广泛,比如android开发程序员EventBus,ButteKinfe等等,都是运用了注解+动态代理的思想;或者从某种程度来说EventBus和ButterKnife运用了AOP的思想。
比如ButterKnife的运用,ButterKnife事先是不知道宿主对象是谁,在程序运行的时候需要拿到具体的宿主对象,然后横向切入到宿主代码中去,这其实就是动态代理和AOP思想的结合。
还是用代码来说话,没使用ButterKnife的代码如下所示
public class MyActivity extends Activity {
TextView helloWoldTxtView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//初始化helloWoldTxtView
helloWoldTxtView = findViewById(R.id.hello_world)
helloWoldTxtView.setText("Hello Android")
}
而使用了ButterKnife的代码则如下所示:
public class MyActivity extends Activity {
@BindView(R.id.hello_world)
TextView helloWoldTxtView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//绑定butterknife
ButterKnife.bind(this)
//直接使用(看起来没有初始化)
helloWoldTxtView.setText("Hello Aop")
}
}
从上面代码来看这就是代理模式的体现,比如把初始化控件的方法代理给了ButterKnife,由ButterKnife代劳android组件的初始化工作。那么ButterKnife是怎么做到的呢?其原理也很简单,在用apt或者annotationProcessor这种技术,在程序编译期间扫描程序,对标有@BindView注解的类进行扫描,然后生成一个Activity的代理类其命名规则为MyActivity_ViewBinding:
public class MyActivity_ViewBinding implements Unbinder {
//宿主类
private MyActivity target;
@UiThread
public MyActivity_ViewBinding (MyActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public ActiveWebActivity_ViewBinding(ActiveWebActivity target, View source) {
this.target = target;
target.helloWoldTxtView= Utils.findRequiredViewAsType(source, R.id.helloWoldTxtView, "field 'helloWoldTxtView'", TextView.class);
}
}
可以看出宿主组件helloWoldTxtView的初始化代理给了MyActivity_ViewBinding 这个类。该类调用 Utils.findRequiredViewAsType这个方法,其内部也即是调用了android的findViewById(R.id.helloWoldTxtView)方法来完成了初始化。
从上面代码可以看出,编译期间动态生成的代码MyActivity_ViewBinding 有两个构造函数,那么什么时候初始化这个代理类呢?答案是调用ButterKnife.bind(this)
的时候,会调用createBind方法:
//target就是宿主Activity
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
所以进入createBinding里看看发生了什么:
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class> targetClass = target.getClass();
//根据Activity所在的class所在的路径拼接_ViewBinding查抄对应代理类的Class对象
Constructor extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
//代理类初始化
return constructor.newInstance(target, source);
}
private static Constructor extends Unbinder> findBindingConstructorForClass(Class> cls) {
//获取你的Activity的class的路径
String clsName = cls.getName();
//拼接代理类所在的路径
Class> bindingClass = Class.forName(clsName + "_ViewBinding");
//获取代理类的构造函数
bindingCtor = (Constructor extends Unbinder>) bindingClass.getConstructor(cls, View.class);
return bindingCtor;
}
上面代码思路也很清晰:
1、获取宿主类的Class对象
2、根据该Class对象的 cls.getName()来拼接出编译期间动态生成的代理类的路径
3、根据代理类所在的路径获取其构造器,然后初始化代理对象
4、在构造器对android组件进行初始化
到此为止,代理模式的分析讲解完毕。从中也体会到了注解+代理模式的强大作用。如有不当之处欢迎批评指正。