Java中的代理模式详解及实例代码
java代理模式详解
前言:
在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。
简单来说代理模式就是通过一个代理对象去访问一个实际对象,并且可以像装饰模式一样给对象添加一些功能。
静态代理
所谓静态代理即在程序运行前代理类就已经存在,也就是说我们编写代码的时候就已经把代理类的代码写好了,而动态代理则是在程序运行时自动生成代理类。
描述起来太过抽象,看一下代码就明白是怎么回事了
main
publicclassMain{ publicstaticvoidmain(String[]args){ Waterwater=newWater(); WaterProxywaterProxy=newWaterProxy(water); waterProxy.drink(); } }
接口
//代理类与被代理类共同实现的接口 publicinterfaceDrink{ voiddrink(); }
被代理类
//被代理的类 publicclassWaterimplementsDrink{ @Override publicvoiddrink(){ System.out.println("drinkwater"); } }
代理类
//代理类 //与被代理类实现同一个接口 publicclassDrinkProxyimplementsDrink{ privateDrinkdrinkImpl; //通过构造函数传入Water对象 publicDrinkProxy(DrinkdrinkImpl){ this.drinkImpl=drinkImpl; } @Override publicvoiddrink(){ //在执行被代理对象的方法前做一些事情 System.out.println("beforedrink"); //执行被代理对象的方法 drinkImpl.drink(); //在执行被代理对象的方法后做一些事 System.out.println("afterdrink"); } }
执行结果
beforedrink drinkwater afterdrink
动态代理
有时候我们只想改变代理类所代理的类,但是代理对象执行实际对象的方法前后所做的事情是一样的,正所谓铁打的代理类,流水的被代理类。而采用静态代理就只能代理实现了同一接口的类,如果要代理任意类则必须写很多重复的代理类。此时我们可以采用动态代理,Java已经为实现动态代理提供了一套比较方便的工具。
java.lang.reflect.Proxy类中可以动态生成代理对象的方法
/** *返回实现了指定接口的对象,调用代理对象的方法会调用 *InvocationHandler的invoke方法 * *@paramloader获取代理类所使用的类加载器 *@paraminterfaces代理类所要实现的接口 *@paramh实现了InvocationHandler接口的对象 *@return代理对象 */ publicstaticObjectnewProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandlerh)
InvocationHandler接口
/** *每个代理类都有一个关联的InvocationHandler *当代理对象执行一个方法的时候会直接执行invoke方法 */ publicinterfaceInvocationHandler{ /** *@param调用该方法的代理对象 *@parammethod代理对象所调用的方法 *@paramargs调用的方法的参数 *@return调用的方法的返回值 */ publicObjectinvoke(Objectproxy,Methodmethod,Object[]args) }
描述总是比较抽象,还是看实际例子比较好理解
例子
InvocationHandler接口的实现类
publicclassCommonInvocationHandlerimplementsInvocationHandler{ //被代理的对象 privateObjectproxied; publicCommonInvocationHandler(Objectproxied){ this.proxied=proxied; } @Override publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ //在调用被代理对象的方法前做一些事情 System.out.println("beforedoingsomething"); //调用被代理对象的方法 Objectresult=method.invoke(proxied,args); //在调用被代理对象的方法后做一些事情 System.out.println("afterdoingsomething");; returnresult; } }
Main
publicclassMain{ publicstaticvoidmain(String[]args){ //被代理的对象 Waterwater=newWater(); //动态获取代理对象 DrinkwaterProxy= (Drink)Proxy.newProxyInstance(water.getClass().getClassLoader(), water.getClass().getInterfaces(), newCommonInvocationHandler(water)); //通过代理对象调用方法 waterProxy.drink(); } }
输出结果
beforedoingsomething drinkwater afterdoingsomething
也可以不要具体的被代理对象,但是必须有相应的接口(没有实现接口的类可以使用cglib实现动态代理)才可以动态获取代理对象。像最近比较火的Retrofit就直接通过声明好的接口使用动态代理进行网络请求。
例子
简单的模拟一下retrofit
POST注解
//Post请求注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public@interfacePOST{ Stringvalue()default""; }
Query注解
//Post请求注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public@interfacePOST{ Stringvalue()default""; }
Service接口
publicinterfaceService{ //用POST注解声明请求的方式和相对路径 @POST("/login") //@Query注解声明请求的参数名 voidlogin(@Query("username")Stringusername, @Query("password")Stringpassword); }
Main
publicclassMain{ publicstaticvoidmain(String[]args){ //动态获取Service接口的代理 Serviceservice=(Service)Proxy.newProxyInstance(Service.class.getClassLoader(), newClass[]{Service.class},newInvocationHandler(){ @Override publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ //通过注解获取请求的相对路径 StringretativePath=((POST)method.getAnnotations()[0]).value(); System.out.println("relativepath:"+retativePath); //获取参数的注解 Annotation[][]parameterAnnotations=method.getParameterAnnotations(); //通过参数的注解获取请求参数 for(inti=0;i<parameterAnnotations.length;i++){ if(parameterAnnotations[i].length!=0){ for(intj=0;j<parameterAnnotations[i].length;j++){ Queryquery=(Query)parameterAnnotations[i][j]; System.out.println(query.value()+":"+args[i].toString()); } } } returnnull; } }); //调用代理对象的方法 service.login("hello","world"); } }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!