结构型模式-代理模式
本文介绍了代理模式,其他常见设计模式在本系列其他文章,有兴趣可了解
文章目录
- 代理模式
- 静态代理
- 动态代理
- **JDK动态代理**
- **CGLIB代理**
- 总结
代理模式
静态代理
举一个音乐会买票的例子感受一下静态代理:
定义一个售卖接口
publicinterfaceSell{publicvoidsell();}定义音乐会售票类
publicclassConcertSellimplementsSell{@Overridepublicvoidsell(){System.out.println("卖票");}}定义中介类
Java有自带的Proxy类,最好不要使用Proxy作为类名,在动态代理中我们会用到这个类
中介增强了ConcertSell的功能
publicclassProxyPointimplementsSell{privateConcertSellconcertSell=newConcertSell();@Overridepublicvoidsell(){System.out.println("中介收取费用");concertSell.sell();}}运行
publicclassClient{publicstaticvoidmain(String[]args){ProxyPointproxy=newProxyPoint();proxy.sell();}}结果
中介收取费用 卖票这里使用了接口(使用接口比使用抽象父类耦合度低),音乐会售票类和中介类都继承这个接口,他们必须实现接口中的抽象方法(中介类在实现的方法中使用了音乐会售票类的方法),中介类在不影响音乐会售票类的同时增强了其功能
动态代理
JDK动态代理
JDK动态代理只能代理接口中定义的方法,换句话说目标类有接口才能用JDK动态代理
JDK动态代理不是代理了目标类(ConcertSell)而是通过反射使用了目标类实例对象的方法
准备好接口和目标类
接口
publicinterfaceSell{publicvoidsell();publicvoidsell2();}目标类
publicclassConcertSellimplementsSell{@Overridepublicvoidsell(){System.out.println("卖场次1票");}@Overridepublicvoidsell2(){System.out.println("卖场次2票");}}我们直接使用工厂模式建一个ProxyFactory工厂,将细节封装到这个工厂类中,通过一个getProxy()方法得到代理对象
publicclassProxyFactory{privateConcertSellconcertSell=newConcertSell();/*执行newProxyInstance()方法时,Java底层在内存中动态生成$Proxy0.class字节码, 必须通过类加载器加载到JVM内存中,才能成为一个可用的代理类, newProxyInstance()方法返回的是这个代理类的实例对象,并由 proxy 变量接收。 *//*newProxyInstance()方法有三个参数, 第一个参数是获取一个类加载器,用来加载$Proxy0.class到JVM内存中, 第二个参数是获取目标类实现的接口数组(这就是JDK动态代理只能代理接口中定义的方法的原因), 第三个参数是匿名内部类形式,其中有一个invoke方法(这里涉及到了反射),invoke方法三个参数分别是: Object proxy(代理对象自身), Method method(外部调用的接口方法), Object[] args(调用时传入的参数数组), method.invoke()利用了反射(这里的invoke方法和匿名内部类的invoke方法意义不同),调用了目标类实例对象的方法, 在invoke方法里可以对目标类实例对象方法进行增强 */publicSellgetProxy(){//JDK动态代理代理的是接口中的方法,强转类型自然是接口类型Sellproxy=(Sell)Proxy.newProxyInstance(concertSell.getClass().getClassLoader(),concertSell.getClass().getInterfaces(),newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{//方法体System.out.println("中介收取费用");method.invoke(concertSell,args);returnnull;}});returnproxy;}}运行
publicclassClient{publicstaticvoidmain(String[]args){ProxyFactoryfactory=newProxyFactory();Sellproxy=factory.getProxy();proxy.sell();proxy.sell2();}}结果
中介收取费用 卖场次1票 中介收取费用 卖场次2票CGLIB代理
CGLIB代理可以为没有实现接口的类提供代理
CGLIB代理类是目标类的子类(要求目标类可继承,因此CGLIB代理不能代理final修饰的类)
CGLIB代理是第三方提供的包,需引入对应jar包,maven工程中可引入jar包坐标(放在标签里)
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>jdk17报错可以将以上依赖替换为
<dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>6.1.8</version></dependency>同时包名也要替换
// 原来是// import net.sf.cglib.proxy.Enhancer;// 现在改为importorg.springframework.cglib.proxy.Enhancer;其他包名同理,将net.sf改为org.springframework
报错原因有兴趣可自行搜索
准备目标类ConcertSell
publicclassConcertSell{publicvoidsell(){System.out.println("卖场次1票");}publicvoidsell2(){System.out.println("卖场次2票");}}我们同样使用工厂模式建一个ProxyFactory工厂,将细节封装到这个工厂类中,通过一个getProxy()方法得到代理对象
publicclassProxyFactory<T>implementsMethodInterceptor{privatefinalClass<T>targetClass;//创建ProxyFactory对象时,将目标类字节码传递过来,使ProxyFactory工厂成为一个通用工厂publicProxyFactory(Class<T>targetClass){this.targetClass=targetClass;}publicTgetProxy(){//创建工具类对象Enhancerenhancer=newEnhancer();//告诉CGLIB要生成的代理类,必须继承T(传过来的类型)enhancer.setSuperclass(targetClass);//设置回调函数,当代理对象的方法被调用时,会回调当前类的intercept方法//这里的参数要求必须是Callback接口的子类。而MethodInterceptor正是Callback最重要的子接口enhancer.setCallback(this);//返回代理对象,并强转成目标类类型return(T)enhancer.create();}@OverridepublicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println("CGLIB中介收取费用");//可以使用method.invoke(o, objects),但这样就是利用反射调用方法了,效率低//CGLIB的优势,不是通过传统反射来调用父类方法,而是通过动态生成的字节码直接调用(类似invokevirtual指令),性能远高于JDK原生反射Objectresult=methodProxy.invokeSuper(o,objects);returnresult;}}运行
publicclassClient{publicstaticvoidmain(String[]args){ProxyFactory<ConcertSell>proxyFactory=newProxyFactory(ConcertSell.class);ConcertSellproxy=proxyFactory.getProxy();//代理对象的方法被调用时,会回调intercept方法proxy.sell();proxy.sell2();}}结果
CGLIB中介收取费用 卖场次1票CGLIB中介收取费用 卖场次2票总结
以上就是本期要讲的内容,本文介绍了代理模式,其他常见设计模式在本系列其他文章,有兴趣可了解
