代理定义:
为某个对象提供一个代理,以控制对这个对象的访问。代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
更通俗的讲,当两个类进行通信时,引入第三个类,将两个类之间的关系解耦。举例:客户端想要访问A类,这时B类作为A的代理类,访问者只需要访问类B就可以;但是A类和B类要有一个共同的接口/父类;通过代理类,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
代理类与委托类的关系:
代理类与委托类具有相同的接口,但委托类是接口的真正实现者,代理类只是调用委托类的相关方法来提供特定的服务,代理类可以为委托类预处理信息、把信息传递给委托类处理并进行事后处理。
类图:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN5gzN0IDM5ETMwITM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
实战由浅入深:使用代理在调用方法之前、之后打印日志。
静态代理:用户管理的具体实现类
package com.xx;
/*
* 委托类(实现类)
*/
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser()------" + userId);
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser()------" + userId);
return "星星";
}
}
静态代理:用户管理的代理类
package com.xx;
public class UserManagerImplProxy implements UserManager {
// 目标对象
private UserManager userManager;
// 通过构造方法传入目标对象
public UserManagerImplProxy(UserManager userManager){
this.userManager=userManager;
}
@Override
public void addUser(String userId, String userName) {
try{
//添加打印日志的功能
System.out.println("start-->addUser()");
userManager.addUser(userId, userName);
//添加用户成功
System.out.println("success-->addUser()");
}catch(Exception e){
//添加用户失败
System.out.println("error-->addUser()");
}
}
@Override
public String findUser(String userId) {
userManager.findUser(userId);
return "张三";
}
}
客户端调用:
public class Client {
public static void main(String[] args){
UserManager userManager=new UserManagerImplProxy(new UserManagerImpl());
userManager.addUser("0001", "星星");
}
}
静态代理是由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
静态代理优缺点:
1、隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间
2、静态代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
3、静态代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。 所以动态代理应运而生。
动态代理:
动态代理类是在程序运行时,利用java的反射机制动态创建而成。它一定要实现java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
动态代理:用户管理具体实现类
package com.xx;
/*
* 委托类(实现类)
*/
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser()------" + userId);
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser()------" + userId);
return "星星";
}
}
动态代理:用户管理代理类
package com.xx;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 代理类都需要实现InvocationHandler类,实现invoke方法。invoke方法就是拿到委托类的实现接口,返回一个代理对象
*/
public class LogHandler implements InvocationHandler {
//目标对象
private Object targetObject;
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
// 根据传入的目标返回一个代理对象:第一个参数是指定目标对象的类加载器;第二个是拿到目标对象的实现接口;执行当前的invoke方法;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
//调用委托类方法时,执行此方法
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
Object retObject = null;
try {
//调用方法前——处理日志
System.out.println("startBefore------");
//调用目标方法
retObject = method.invoke(targetObject, args);
//调用方法后——处理日志
System.out.println("endAfter------");
} catch (Exception e) {
e.printStackTrace();
System.out.println("error---------");
}
return retObject;
}
}
客户端调用:
public class Client {
public static void main(String[] args){
UserManager userManager = new UserManagerImpl();
LogHandler logHandler = new LogHandler();
//动态代理
UserManager userManager2 = (UserManager)logHandler.newProxyInstance(userManager);
userManager2.addUser("0001", "小星星");
}
}
如此我们可以通过LogHandler代理类,实现代理不同类型的对象。如果把对外的接口都通过动态代理实现,那么调用所有的方法时都会执行invok()方法,我们就可以在invoke方法里执行一些公共的操作,如:日志、拦截器、事务等。其实动态代理类LogHandler其实就是对静态代理类UserManagerImplProxy的封装、抽象。
动态代理优缺点:
1、它是在运行时生成.class,在生成它时你必须提供一组interface给它,然后该class(代理类)就宣称它实现了这些interface,这样就可以调用委托类的方法。
2、动态代理更有利于程序的扩展;不需要更改原有的代码。
3、能在运行过程中根据接口的类型动态的调用实现该接口的类 。
总结
至此,对代理模式,以及静态、动态代理的演变已经很清楚,代理模式更是体现了一种AOP思想:面向切面编程,Aspect Oriented Programming的缩写。也没有违背开闭原则,很好的实现了扩展。