大纲
代理模式(结构型设计模式)通过代理类去访问实现类中的方法,使用场景比如:已有接口和实现类的情况下,想要在已实现的方法基础上扩展更多的功能的场景。
代理模式里的主要类:
接口
实现类,需实现接口,用来完成真正的业务。(网上资料也叫做委托类、被代理类)
代理类,也需要实现接口,调用实现类的方法,本身不处理业务。
使用代理模式的好处:可以扩展实现类的功能,在实现类基本功能基础上增加一些额外的操作。
静态代理模式
图示:
要想做到在原来实现方法的基础上多增加功能,代理类同样也需要实现接口,并且需要持有一个实现类,在自己实现的方法中扩展功能,并且调用实现类的方法去处理真正的业务:
public class TV {String name;String model;public String getModel() {return model;}public String getName() {return name;}public void setModel(String model) {this.model = model;}public void setName(String name) {this.name = name;}public TV(String name,String model){this.name = name;this.model = model;}@Overridepublic String toString() {return "TV{" +"name='" + name + '\'' +", model='" + model + '\'' +'}';}
}
实体类TV.java
public interface ICompany {TV produceTV();
}
接口ICompany.java
实现类需要实现ICompany接口:
/**
* 实现类
*/
public class CompanyFactory implements ICompany{@Overridepublic TV produceTV() {System.out.println("生产商生产一台电视");return new TV("TCL","40寸");}
}
代理类也需要实现ICompany接口:
/**
* 代理类
* 代理类和实现类都要实现接口
*/
public class TVProxy implements ICompany {ICompany company;@Overridepublic TV produceTV() {System.out.println("代理收到一笔订单");if (Objects.isNull(company)) {company = new CompanyFactory();}return company.produceTV();}
}
CompanyFactory.java
运行时构建代理类,调用代理类的方法同时也调用了实体类的实现方法:
public class MainClass {public static void main(String[] args) {ICompany company = new TVProxy();TV tv = company.produceTV();System.out.println(tv.toString());}
}
总结:
静态代理通过创建代理类去实现接口,在实现方法里添加额外功能,从而实现不改变原有实现方法下做到功能扩展。
动态代理模式
静态代理有什么缺点?(先别看下面内容思考一下)
静态代理的局限在于,如果接口新增了方法,则代理类必须实现该方法,而使用动态JDK代理,代理类不用实现接口。
实体类TV不变,接口增加维修方法,如果是静态代理,接口新增方法,则对应的实现类和代理类都要实现这个方法,而动态代理则在代码编写层面不用实现接口的所有方法:
图示:
接口新增repairTV方法:
public interface ICompany {TV produceTV();void repairTV(String name); //新增维修电视的方法
}
实现类需要实现这个方法:
public class CompanyFactory implements ICompany {@Overridepublic TV produceTV() {System.out.println("生产商生产一台电视");return new TV("TCL", "40寸");}@Overridepublic void repairTV(String name) {System.out.println(String.format("生产商维修%s电视",name));}
}
这里是动态代理类,可以看到,这里并没有实现ICompany接口了
public class TVDynamicProxy {Object factory;public TVDynamicProxy(Object factory) {this.factory = factory;}public <T> T getProxy(Class<T> clazz) {//newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的:return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //回调System.out.println("动态代理执行:接收到一笔订单");Object tv = method.invoke(factory, args); //这里通过反射调用了factory里实现的方法System.out.println("动态代理结束");return tv;}});}}
TVDynamicProxy.java
上面方法中newProxyInstance实际上是返回了一个实现了接口的代理类实例,这个实例是存在在内存中的,动态生成的
public class MainClassB {public static void main(String[] args) {ICompany company = new CompanyFactory();//将生成的代理类写到本地:System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); //将内存中的$Proxy0.class写到本地ICompany company1 = new TVDynamicProxy(company).getProxy(ICompany.class);TV tv = company1.produceTV();company1.repairTV(tv.name);}
}
代码运行查看结果
运行后断点可以看到,这里实际上返回了$Proxy0
$Proxy0实现了接口,并实现了接口里定义的方法。
所以先前我们说,是在代码编写层面上不用去实现接口,实际是JDK帮我们自动实现了。
public final class $Proxy0 extends Proxy implements ICompany {private static Method m1;private static Method m3;private static Method m2;private static Method m4;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});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}//自动实现了接口里定义的方法public final TV produceTV() throws {try {return (TV)super.h.invoke(this, m3, (Object[])null); //这里回调了InvocationHandler的invoke方法} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}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 void repairTV(String var1) throws {try {super.h.invoke(this, m4, 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);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}...
}
$Proxy0.class
这个$Proxy0生成到了我们的根目录下面
总结:
动态代理在运行后会在内存中创建一个实现了接口的代理类,通过调用代理类的实现方法,回调到InvocationHandler接口的invoke方法,通过反射拿到实现类的方法,并进行调用。
思考:
如果我新建一个工厂类,不实现ICompany接口,那是否可以代理成功?
public class FactoryB {public TV produceTVB() {System.out.println("B工厂生产电视");return new TV("华为电视机", "南京");}
}
上面问题可以使用Cglib代理解决。有兴趣的同学可以看给出的参考链接自行拓展。
参考资料:
https://segmentfault.com/a/1190000040407024