整理自https://blog.csdn.net/h_xiao_x/article/details/72552819
1、代理模式
(1)概念
代理模式:代理模式是为另一个对象提供一个替身来控制对这个对象的访问。代理类负责为这个对象预处理消息,过滤消息并转发消息,以及进行消息被该对象执行后的后续处理。
对于程序来说,代理对象就可以拦截下许多目标对象不需要或者不想知道的信息,代理类也可以再转发请求给目标对象之前对数据做点处理,总而言之就是,代理类让客户与目标对象解耦,既能满足客户的需求,也能让目标对象只管做好自己的本分工作。
(2)模式图
(3)分类
按照代理类的创建时期,可分为静态代理和动态代理
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
静态代理演示示例
接口
interface UserManager{
//为了代码简洁和清晰阅读,只写一个方法
void addUser(String userName);
}
实现类
class UserManagerImpl implements UserManager{
@Override
public void addUser(String userName) {
System.out.println("添加用户成功!用户为:" + userName);
}
}
代理类
class Proxy implements UserManager{
private UserManagerImpl umi;
public Proxy(UserManagerImpl umi){
this.umi = umi;
}
@Override
public void addUser(String userName) {
//使用代理的一个好处是可以在对实际对象访问前进行一些必要的处理,控制了对实际对象的访问
//在添加之前做一些日志操作
System.out.println("添加之前,记录到日志里去……");
umi.addUser(userName);
//在添加之后也做一些日志操作
System.out.println("记录成功!时间:" + new Date());
}
}
测试类
public class StaticProxy {
public static void main(String[] args) {
//客户想访问实际对象,只需要访问实际对象的代理对象即可
UserManager um = new Proxy(new UserManagerImpl());
um.addUser("张三");
}
}
(4)静态代理类优缺点
- 优点:代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合)。
- 缺点:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。
2、动态代理
(1)模型图
(2)InvocationHandler用处
Proxy不直接转发给目标对象是因为,在程序运行时,Proxy是方法的直接调用者,然后将调用的信息传给InvocationHandler,InvocationHandler再根据传过来的信息并利用反射转为目标对象的最终调用。
(3)代码示例
被代理类
interface UserManager{
//为了代码简洁和清晰阅读,只写一个方法
void addUser(String userName);
}
实现的类
class UserManagerImpl implements UserManager{
@Override
public void addUser(String userName) {
System.out.println("添加用户成功!用户为:" + userName);
}
}
代理类
class LogHandler implements InvocationHandler{
private Object tagetObject;
//对要代理的目标对象进行绑定,关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public Object newProxyInstance(Object tagetObject){
this.tagetObject = tagetObject;
//第一个参数是:指定目标对象的类加载器
//第二个参数:指定目标对象实现的所有接口,让代理对象对目标对象实现同样的接口
//第三个参数:指定代理对象要要转发请求给的实现了InvokationHandler的子类,并执行对应的invoke()方法
//根据传入的目标对象,产生并返回改目标对象的一个代理对象,这就是动态代理
return Proxy.newProxyInstance(tagetObject.getClass().getClassLoader(),
tagetObject.getClass().getInterfaces(), this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法:proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
proxy = null;
try {
proxy = method.invoke(tagetObject, args);
} catch (Exception e) {
e.printStackTrace();
}
return proxy;
}
测试类
public class DynamicProxy {
public static void main(String[] args) {
//给客户一个InvokationHandler对象,就能动态生成一个代理对象,进而能够访问目标对象
LogHandler lh = new LogHandler();
UserManager um = (UserManager) lh.newProxyInstance(new UserManagerImpl());
um.addUser("张三");
}
}
运行结果:
我们直接调用方法的对象时代理对象,然后Handler里的invoke()方法被自动调用,最后转为了目标对象的调用。
(4)优点
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。