java代理模式与动态代理的实现
1.什么是代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。本文将详细介绍代理模式与动态代理的实现方式。
百度图-侵删.jpg
组成:
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
(以上摘自百度百科)
2.为什么使用代理模式
1.真实角色的职责清晰。
2.对真实角色的访问保护。
3.高扩展性。
比如:在数据库的操作业务中,数据库对象就是真实角色,只负责增删改查。beginTransaction 和commit不属于它的职责,但实际业务总是需要调用怎么办?可以创建一个代理,持有数据库的真实访问,外部总是通过代理来操作数据。请看下面的栗子↓
3.一颗栗子
就拿数据库举个栗子吧,首先根据基本组成,定义接口和真实类、代理类。
数据库接口-增删改查
interface IDao {
void add(Object o);
void delete(Object o);
void update(int id, Object o);
Object find(int id);
}
真实类 实现(此处用一个map模拟实际数据操作)
class SimpleDao implements IDao {
private List<Object> mDataBase = new ArrayList<>();
@Override
public void add(Object o) {
mDataBase.add(o);
}
@Override
public void delete(Object o) {
mDataBase.remove(o);
}
@Override
public void update(int id, Object o) {
mDataBase.remove(id);
mDataBase.add(id, o);
}
@Override
public Object find(int id) {
return mDataBase.get(id);
}
}
代理类 实现
class DaoProxy implements IDao {
private IDao mDao;
public DaoProxy(IDao dao) {
this.mDao = dao;
}
private void beginTransaction() {
System.out.println("读写开始前,开启数据库!");
}
private void commit() {
System.out.println("读写结束,关闭数据库!");
}
@Override
public void add(Object o) {
beginTransaction();
mDao.add(o);
commit();
}
...... //废话就别看了下面都一样
}
实际使用时
SimpleDao simpleDao = new SimpleDao();
DaoProxy proxy = new DaoProxy(simpleDao);
proxy.add(new Object());
......
这就是简单的静态代理实现啦,这样看貌似成了装饰器模式?别急,我们先来分析一下两种模式的定义:
装饰器模式:侧重对于目标类中核心逻辑的扩展,依然是以目标类为中心。
代理模式:更加侧重于对目标类的访问限制与处理,某些场景甚至不需要调用目标类的实现,比如👇
@Override
public void add(Object o) {
if(amIHappy()) {
beginTransaction();
mDao.add(o);
commit();
} else {
System.out.println("不高兴,我不干!");
}
}
private boolean amIHappy() {
......
}
上面就是代理模式的静态代理基本实现方式了,然后我们接着来看看java的动态代理。
动态代理
1.动态代理简介
首先,相对于静态代理,动态代理中的代理类不需要实现与目标类相同的接口,而且不依赖于接口的具体实现,理论上可以代理所有的类所有方法。但因为要考虑到涉及到的业务,所以要求面向接口代理。
实现机制:运行时创建一个虚拟的代理类,在代理的目标方法实际执行时,通过java的反射技术获取到该方法对象,并在执行前或执行后添加需要的操作,这需要实现一个InvocationHandler接口,来看一个具体的实现
2.又是一颗栗子
class DaoProxy {
这里需要注意,返回值只能是一个接口,而不能是具体的是实现类
public IDao createProxy(IDao target) {
此方法生成的虚拟类是根据目标的Class文件拿到的父类接口生成,因此不能强转成实现类
return (IDao) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beginTransaction();
Object invoke = method.invoke(target, args);
commit();
return invoke;
}
});
}
private void beginTransaction() {
System.out.println("读写开始前,开启数据库!");
}
private void commit() {
System.out.println("读写结束,关闭数据库!");
}
}
如上,实际返回的是一个匿名内部代理类,类型为$Proxy1(数字可能是其它),可以强转成目标类的父类接口使用,等于是生成了一个全是空方法的接口实现类。然后再实际执行到某个方法的时候,会执行 invoke 方法中的逻辑,这里我们调用了目标类的具体实现。
具体使用:
SimpleDao simpleDao = new SimpleDao();
IDao proxy = new DaoProxy().createProxy(simpleDao);
proxy.add(new Object());
......
//------------我是手动分割线------------
3.无需实现的接口代理
接下来,我们要将一个比较极端的代理方式,可以完全抛开具体实现,进行接口的完全代理。这种代理方式中我们只关心方法传入的参数和方法本身,重点逻辑全在代理中实现,完全无需接口的具体实现。👇
class LogProxy {
public <T> T createProxy(Class<T> targetInterface) {
return (T) Proxy.newProxyInstance(targetInterface.getClassLoader(), new Class[]{ targetInterface },
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("开始输出日志------->");
System.out.print("调用方法:" + method.getName() + makeArgsText(args));
System.out.print("<-------日志输出结束");
System.out.println();
return null;
}
});
}
private String makeArgsText(Object[] args) {
StringBuilder builder = new StringBuilder();
builder.append(", 共有").append(args.length).append("个参数: ");
for(Object item : args) {
builder.append(item.toString()).append(" & ");
}
return builder.toString();
}
}
使用此代理并开始执行:
IDao proxy = new LogProxy().createProxy(IDao.class);
proxy.add("item");
proxy.update(0, "newItem");
得到的结果是:
开始输出日志------->调用方法:add, 共有1个参数: “item”,<-------日志输出结束
开始输出日志------->调用方法:update, 共有2个参数: “0”,“newItem”,<-------日志输出结束
这种代理方式使用到的场景,都是完全不关心接口的实现逻辑,代理类中已经完全集成了需要的操作,只需要取到接口方法上定义的注解和实际调用时传入的参数,用这些数据进行实际的操作。
安卓开发中使用的Retrofit框架就是通过这种方式实现的动态代理,日后有时间会再更一篇文章来深入的扒一扒Retrofit框架的实现原理。
以上就是本文的全部内容,如有不足请多指教,共同进步。