结构型模式
描述将类或者对象按某种布局组成更大的结构。
分为类结构型模式和对象结构型模式。
类结构型模式采用继承机制组织接口和类。
对象结构型模式采用组合或聚合组合对象。
对象结构型模式耦合度低,比类结构型模式具有更大的灵活性。
模式意图
给某个对象提供一个代理对象,目标对象不适合直接访问,代理对象作为中介。
分为静态代理和动态代理,动态代理又分为JDK动态代理和CGLIB动态代理
结构
- 抽象目标类
- 真实目标类:实现具体业务,是代理对象所代表的真实对象
- 代理类:可以访问、控制或扩展真实目标类的功能
静态代理
火车站卖票,通过代售点进行取票,火车站是目标对象,代售点是代理对象
public class Client { public static void main(String[] args) { ProxyPoint proxyPoint = new ProxyPoint(); proxyPoint.sell(); } } interface SellTickets{ void sell(); } class TrainStation implements SellTickets{ @Override public void sell() { System.out.println("火车站卖票"); } } class ProxyPoint implements SellTickets{ private TrainStation trainStation = new TrainStation(); @Override public void sell() { System.out.println("代理点收取费用"); trainStation.sell(); } }
目标是访问火车站的卖票方法,但是通过代理类间接访问,并且对卖票方法有所加强。
JDK动态代理
动态代理的代理类是程序运行的过程中在内存中动态生成
动态代理对象要代替目标对象执行某个方法
1.首先需要在内存中创建出代理类,所以需要类加载器
2.还需要知道代理对象要执行什么方法,执行的方法是加强过的方法,该方法还得调用目标对象的方法,所以需要获取目标对象所实现的接口和执行程序(加强过的方法)。
3.获取动态代理类后,动态代理类中就有了执行程序(加强过的方法)和目标对象的所有方法,动态代理类会把所有方法当作是Method对象存起来
4.动态代理类调用sell方法,就是执行我们写的匿名实现类中的invoke方法,该invoke方法还会执行目标类中的方法(是通过传递method,method是sell,station是执行sell的具体对象,args是该方法的参数)
总结:
动态代理类中已经加载好了接口的所有方法的对象,动态代理类的父类加载好了我们写的匿名内部类。
动态代理类调用sell方法,就会调用我们写的匿名内部类中的invoke方法,因为要在invoke方法中再调用目标对象中的sell方法,就把存好的方法对象和所需参数传进来,再由我们自行传入执行该方法的目标类对象,便可实现功能。
package mode; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { //创建代理工厂对象 ProxyFactory factory = new ProxyFactory(); //使用factory对象的方法获取代理对象 SellTickets proxyObject = factory.getProxyObject(); //调用sell方法,本质是调用invoke方法 proxyObject.sell();//代售点收取服务费用 火车站卖票 System.out.println(proxyObject.getClass()); while (true){ } } } interface SellTickets{ void sell(); } class TrainStation implements SellTickets{ @Override public void sell() { System.out.println("火车站卖票"); } } class ProxyFactory{ //声明目标对象 private TrainStation station = new TrainStation(); public SellTickets getProxyObject(){ //返回代理对象 SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance( //类加载器,用于加载代理类 station.getClass().getClassLoader(), //代理类实现的接口的字节码对象,能获取里面的方法 station.getClass().getInterfaces(), //代理对象调用的处理程序 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代售点收取服务费用"); //method方法就是sell方法,args是sell方法的参数,返回值就是sell方法的返回值,因为传入的对象是station所以执行的是station方法中的sell方法 Object invoke = method.invoke(station, args); return invoke; } } ); return proxyObject; } }
通过运行程序,再通过阿里巴巴的阿尔萨斯反编译工具获取到动态代理类,反编译就是将硬件执行的代码转换成高级语言
1.Class.forName获取SellTickets的字节码对象
2.再获取这个类的sell方法,并赋给m3
3.构造器接收参数为invocationHandler,说明创建动态代理对象时必须传递这个参数,这个参数也就是我们自己代码中实现的匿名内部类,构造器中将invocationHandler(调用程序)再传给父类,将这个变量赋值给了父类中的一个成员变量h
4.执行sell方法就是执行this.h.invoke就是执行匿名内部类中的invoke方法
CGLIB动态代理
JDK动态代理要求目标类必须实现接口,如果没有接口就不能使用JDK代理了。
CGLIB可以为没有实现接口的类提供代理,为JDK动态动态代理提供了很好的补充。
CGLIB是第三方提供的包,需要引入
三种代理模式的对比
优缺点
优点
缺点
增加了系统的复杂度
使用场景