参考博客:
https://www.cnblogs.com/baizhanshi/p/6611164.html
https://mp.weixin.qq.com/s/34LAnTGhqe7DTYmT1PRQJg
代理模式是一种设计模式,主要解决的问题是:在直接访问对象时带来的问题
可能被代理对象的功能是可以实现的,但是我们有时候会考虑到对其进行增强的方式来对其进行解决;但是有时候我们也不需要来对其进行增强。
本质上是我们需要根据自己的需求实现,来对其采用特殊处理。
![](
)
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
使用场合举例:
如果需要委托类处理某一业务,那么我们就可以先在代理类中统一处理然后在调用具体实现类
按照代理的创建时期,代理类可以分为两种:
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
1、静态代理:
直接看一个对应的例子说明:
public interface UserManager {
void addUser(String userId, String userName);
void delUser(String userId);
String findUser(String userId);
void modifyUser(String userId, String userName);
}
创建其中一个实现类来对其进行实现:
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
创建静态代理类:
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 void delUser(String userId) {
userManager.delUser(userId);
}
@Override
public String findUser(String userId) {
userManager.findUser(userId);
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
userManager.modifyUser(userId,userName);
}
}
测试:
public class TestStaticProxy {
public static void main(String[] args) {
UserManagerImpl userManager = new UserManagerImpl();
UserManagerImplProxy userManagerImplProxy = new UserManagerImplProxy(userManager);
userManagerImplProxy.addUser("111","lig");
}
}
静态代理类优缺点
优点:
代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。
缺点:
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。
举例说明:代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则。但是如果想让每个实现类都添加打印日志的功能的话,就需要添加多个代理类,以及代理类中各个方法都需要添加打印日志功能(如上的代理方法中删除,修改,以及查询都需要添加上打印日志的功能)
即静态代理类只能为特定的接口(Service)服务。如想要为多个接口服务则需要建立很多个代理类。
上面的两个缺点非常致命,所以开发中都不会来使用这两个代理类来做操作
2、动态代理:
根据如上的介绍,你会发现每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类
所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理
在上面的示例中,一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象。而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy 类的支持
java.lang.reflect.InvocationHandler接口的定义如下:
//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java.lang.reflect.Proxy类的定义如下:
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
对应的代理实例如下所示:
接口类:
public interface UserManager {
void addUser(String userId, String userName);
void delUser(String userId);
String findUser(String userId);
void modifyUser(String userId, String userName);
}
实现类:
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.addUser");
}
@Override
public void delUser(String userId) {
System.out.println("UserManagerImpl.delUser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.findUser");
return "张三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
对应的代理类:
//动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。
// 该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
public class LogHandlerProxy implements InvocationHandler {
// 目标对象
private Object targetObject;
// 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
public UserManager bind(Object targetObject){
this.targetObject=targetObject;
// 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
// 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
// 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
// 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
// 根据传入的目标返回一个代理对象
return (UserManager) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),this);
}
@Override
//关联的这个实现类的方法被调用时将被执行
/*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start-->>");
for(int i=0;i<args.length;i++){
System.out.println(args[i]);
}
Object ret=null;
try{
/*原对象方法调用前处理日志信息*/
System.out.println("satrt-->>");
//调用目标方法
ret=method.invoke(targetObject, args);
/*原对象方法调用后处理日志信息*/
System.out.println("success-->>");
}catch(Exception e){
e.printStackTrace();
System.out.println("error-->>");
throw e;
}
return ret;
}
}
测试类:
public class UserManagerTest {
public static void main(String[] args) {
LogHandlerProxy logHandlerProxy = new LogHandlerProxy();
UserManager userManager = logHandlerProxy.bind(new UserManagerImpl());
userManager.addUser("111","lig");
System.out.println("=====================");
userManager.delUser("666");
}
}
这里有三个对象:
- UserManagerImpl对象 , 我们称之为被代理对象
- LogHandlerProxy对象,我们称之为执行者对象
- Proxy对象 (通过在ProviderHandler bind方法中使用Proxy.newProxyInstance生成的对象) 我们称之为代理对象
这三个对象是什么关系呢?
Proxy是真正的代理类,UserManagerImpl是被代理类,LogHandlerProxy是执行方法增强的执行者。
我们是为了增强UserManagerImpl(被代理对象)的addUser
方法,就Proxy对象来代理被代理对象的执行,Proxy不亲自来做这件事,而是交给执行者对象ProviderHandler 来实现增加的目录,执行调用前的限流校验。
实际怎么实现的呢?
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//对 Invocationhandler做判空处理
Objects.requireNonNull(h);
//复制[IProvider接口]
final Class<?>[] intfs = interfaces.clone();
//根据IProvider的类加载器IProvider接口生成了Proxy类,关键:根据类加载器和接口对象在JVM缓存中生成一个类对象
Class<?> cl = getProxyClass0(loader, intfs);
//获取构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//保存InvocationHandler的引用
final InvocationHandler ih = h;
//通过构造器实例化Proxy代理对象
return cons.newInstance(new Object[]{h});
}
3、生成的Proxy对象是怎样调用执行者的invoke函数的
这个地方通过这段代码将Proxy0的class字节码输出到文件。
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", WeiboProvider.class.getInterfaces());
String path = "C:**/IdeaProjects/study/out/production/study/SimpleProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理类class文件写入成功");
} catch (Exception e) {
System.out.println("写文件错误");
}
反编译Proxy0如下:
//Proxy0 是动态生成的类,继承自Proxy,实现了IProvider接口
public final class $Proxy0 extends Proxy implements IProvider {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String getData(String var1) throws {
try {
// 这里来到了最本质的地方
//m3就是IProvider 接口的getData方法
//super.h 是父类java.lang.reflect.Proxy的属性 InvocationHandler
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
//m3就是IProvider 接口的getData方法
m3 = Class.forName("aop.IProvider").getMethod("getData", new Class[]{Class.forName("java.lang.String")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
重点在 return (String)super.h.invoke(this, m3, new Object[]{var1});
代码。
$Proxy0继承Proxy类,实现了IProvider接口,所以也有getData()函数,而getData函数调用的是执行者InvocationHandler的invoke方法,m3是通过反射拿到的Method对象,所以看getData调用invoke传递的。三个参数,第一个是Proxy对象,第二个是getData方法对象,第三个是参数。
总结一下:
- 动态代理的本质就是,生成一个继承自Proxy,实现被代理接口(IProvider)的类 - Proxy0。
- Proxy0 持有InvocationHandler实例,InvocationHandler 持有SimpleProvider实例。Proxy0调用接口 getData方法时,先传递给InvocationHandler,传递的时候调用invoke()方法,然后InvocationHandler再传递给SimpleProvider实例来反射调用其中的方法。
动态代理实际上就是帮我们在JVM内存中直接重新生成了代理类class和对应类对象,然后通过执行者InvocationHandler调用被代理对象SimpleProvider。
这就是我们经常看到的,为什么代理对象调用方法的时候会首先调用代理对象的Invoke()方法,然后再去调用目标对象的方法。