现在的位置: 首页 > 操作系统 > 正文

Java中的代理模式

2020年02月10日 操作系统 ⁄ 共 3856字 ⁄ 字号 评论关闭

代理模式

在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。

简单来说代理模式就是通过一个代理对象去访问一个实际对象,并且可以像装饰模式一样给对象添加一些功能。

静态代理

所谓静态代理即在程序运行前代理类就已经存在,也就是说我们编写代码的时候就已经把代理类的代码写好了,而动态代理则是在程序运行时自动生成代理类。

描述起来太过抽象,看一下代码就明白是怎么回事了

main

public class Main { publicstaticvoidmain(String[] args) { Water water = new Water(); WaterProxy waterProxy = new WaterProxy(water); waterProxy.drink(); }}

接口

//代理类与被代理类共同实现的接口public interface Drink { voiddrink();}

被代理类

//被代理的类public class Water implements Drink { @Override publicvoiddrink() { System.out.println("drink water"); }}

代理类

//代理类//与被代理类实现同一个接口public class DrinkProxy implements Drink { private Drink drinkImpl; //通过构造函数传入Water对象 publicDrinkProxy(Drink drinkImpl) { this.drinkImpl = drinkImpl; } @Override publicvoiddrink() { //在执行被代理对象的方法前做一些事情 System.out.println("before drink"); //执行被代理对象的方法 drinkImpl.drink(); //在执行被代理对象的方法后做一些事 System.out.println("after drink"); }}

执行结果

before drinkdrink waterafter drink

动态代理

有时候我们只想改变代理类所代理的类,但是代理对象执行实际对象的方法前后所做的事情是一样的,正所谓铁打的代理类,流水的被代理类。而采用静态代理就只能代理实现了同一接口的类,如果要代理任意类则必须写很多重复的代理类。此时我们可以采用动态代理,Java已经为实现动态代理提供了一套比较方便的工具。

java.lang.reflect.Proxy类中可以动态生成代理对象的方法

/** *返回实现了指定接口的对象,调用代理对象的方法会调用 *InvocationHandler的invoke方法 * * @param loader 获取代理类所使用的类加载器 * @param interfaces 代理类所要实现的接口 * @param h 实现了InvocationHandler接口的对象 * @return 代理对象 */publicstatic Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

InvocationHandler接口

/** *每个代理类都有一个关联的InvocationHandler *当代理对象执行一个方法的时候会直接执行invoke方法 */public interface InvocationHandler { /** * @param 调用该方法的代理对象 * @param method 代理对象所调用的方法 * @param args 调用的方法的参数 * @return 调用的方法的返回值 */ public Object invoke(Object proxy, Method method, Object[] args)}

描述总是比较抽象,还是看实际例子比较好理解

例子

InvocationHandler接口的实现类

public class CommonInvocationHandler implements InvocationHandler { //被代理的对象 private Object proxied; publicCommonInvocationHandler(Object proxied) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在调用被代理对象的方法前做一些事情 System.out.println("before doing something"); //调用被代理对象的方法 Object result = method.invoke(proxied, args); //在调用被代理对象的方法后做一些事情 System.out.println("after doing something");; return result; }}

Main

public class Main { publicstaticvoidmain(String[] args) { //被代理的对象 Water water = new Water(); //动态获取代理对象 Drink waterProxy = (Drink) Proxy.newProxyInstance(water.getClass().getClassLoader(), water.getClass().getInterfaces(), new CommonInvocationHandler(water)); //通过代理对象调用方法 waterProxy.drink(); }}

输出结果

before doing somethingdrink waterafter doing something

也可以不要具体的被代理对象,但是必须有相应的接口(没有实现接口的类可以使用cglib实现动态代理)才可以动态获取代理对象。像最近比较火的Retrofit就直接通过声明好的接口使用动态代理进行网络请求。

例子

简单的模拟一下retrofit

POST注解

//Post请求注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface POST { String value() default"";}

Query注解

//Post请求注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface POST { String value() default"";}

Service接口

public interface Service { //用POST注解声明请求的方式和相对路径 @POST("/login") //@Query注解声明请求的参数名 voidlogin(@Query("username")String username, @Query("password")String password);}

Main

public class Main { publicstaticvoidmain(String[] args) { // 动态获取Service接口的代理 Service service = (Service) Proxy.newProxyInstance(Service.class.getClassLoader(), new Class[] { Service.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 通过注解获取请求的相对路径 String retativePath = ((POST) method.getAnnotations()[0]).value(); System.out.println("relative path: " + retativePath); // 获取参数的注解 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); // 通过参数的注解获取请求参数 for (int i = 0; i < parameterAnnotations.length; i++) { if (parameterAnnotations[i].length != 0) { for (int j = 0; j < parameterAnnotations[i].length; j++) { Query query = (Query) parameterAnnotations[i][j]; System.out.println(query.value() + ": " + args[i].toString()); } } } return null; } }); // 调用代理对象的方法 service.login("hello", "world"); }}

本文永久更新链接地址:http://www.xuebuyuan.com/Linux/2017-01/139963.htm

以上就上有关Java中的代理模式的全部内容,学步园全面介绍编程技术、操作系统、数据库、web前端技术等内容。

抱歉!评论已关闭.