代理模式(五)JDK动态代理深入分析


       JDK动态代理实现的核心技术是java反射机制,其主要的两个类是:Proxy、InvocationHandler。使用java.lang.reflect.Proxy类可以动态实现接口作为代理类。

一. Creating Proxies

使用Proxy.newProxyInstance()方法创建动态代理类,方法定义:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

  1. ClassLoader :加载动态代理类的类加载器
  2. interfaces   :该动态代理类所要实现的接口
  3. InvocationHandler:事件处理,转发所有的方法调用代理。
  4. 函数返回创建的代理类,实现了interfaces接口

例如:

InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                            MyInterface.class.getClassLoader(),
                            new Class[] { MyInterface.class },
                            handler);

   函数返回值强转成MyInterface类型,所有对代理类的方法调用都将转发到实现了InvocationHandler的handler对象中。

二. InvocationHandler's

Proxy.newProxyInstance() 方法中需要传入实现InvocationHandler的对象,proxy方法的调用才能转发到InvocationHandler的实现对象中。

public interface InvocationHandler{
  Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable;
}
例如:

public class MyInvocationHandler implements InvocationHandler{

  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {
    //do something "dynamic"
  }
}

   1. proxy就是通过 Proxy.newProxyInstance()方法创建的动态代理类,它实现了指定的interfaces接口。 通常这个对象在invoke函数中不使用。

   2. Method 表示动态代理类所实现的接口中的方法以及被代理类父类的方法(比如Object的方法)。通过Method 对象可以获取方法名、参数类型、返回类型等信息。

   3. Object[] args 包含了传入动态代理类所实现的方法的参数值。

三. JDK动态代理类Demo及原理分析

      Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口, 客户端通常需要针对抽象主题角色进行编程(面向接口编程)。
package com.markliu.proxy;

/**
 * 抽象角色;动态代理只能代理接口
 * @author markliu
 *
 */
public interface Subject {  
    public void request();
}

     DynamicInvocationHandler(代理主题中的业务处理角色):它包含了对真实主题的引用(对象组合),从而可以在invoke方法中操作真实主题对象;在代理主题角色中提供一个实现与真实主题角色相同接口的对象realSubject,以便在任何时候都可以替代真实主题;在代理主题角色中,invoke函数在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
package com.markliu.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理类
 * 实现了InvocationHandler
 * @author markliu
 *
 */
public class DynamicInvocationHandler implements InvocationHandler {
	// 这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象
	private Object realSubject; // 保存的是真实对象

	public DynamicInvocationHandler() {
	}

	public DynamicInvocationHandler(Object realSubject) {
		this.realSubject = realSubject;
	}

      /*
       * 这个方法不是我们显示的去调用
       * 实际内部调用的形式是invoke(this, m3, null);
       * this指$Proxy0动态代理对象,
       * m3代表$Proxy0所实现的接口Subject中的request的方法对象
       */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("before calling " + method);
		method.invoke(realSubject, args);
		System.out.println("after calling " + method);

		return null;
	}

}

       RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

package com.markliu.proxy;

/**
 * 真实角色->被代理类:实现了Subject的request()方法  
 * @author markliu
 *
 */
public class RealSubject implements Subject {
	
	@Override
	public void request() {
		System.out.println("From real subject.");
	}
}
     客户端测试代码:

package com.markliu.proxy;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 客户端:生成代理实例,并调用了request()方法  
 * @author markliu
 */
public class Client {

	public static void main(String[] args) throws Throwable {
		// 真实角色->被代理类
		Subject realSubject = new RealSubject();
		// 调用处理的业务具体实现
		InvocationHandler h = new DynamicInvocationHandler(realSubject);
		// Returns the runtime class of this Object
		Class<?> cls = realSubject.getClass();
		
		/*
		 * 动态代理类
		 *  Returns an instance of a proxy class for the specified interfaces 
		 *  that dispatches method invocations to the specified invocation handler.
		 *  返回一个指定接口的proxy类对象,用于将方法调用分派到指定的InvocationHandler中
		 *  
		 *  cls.getInterfaces(): 获取cls所对应的类RealSubject锁实现的接口,即Subject
		 *  因此创建的Proxy的一个实例实现了动态指定的接口!
		 */
		Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);

		// 这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口
		System.out.println(subject instanceof Proxy);

		// 这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口
		System.out.println("subject的Class类是:" + subject.getClass().toString());

		System.out.print("subject中的属性有:");
		Field[] field = subject.getClass().getDeclaredFields();
		for (Field f : field) {
			System.out.print(f.getName() + ", ");
		}

		System.out.print("\n" + "subject中的方法有:");

		Method[] method = subject.getClass().getDeclaredMethods();
		for (Method m : method) {
			System.out.print(m.getName() + ", ");
		}

		System.out.println("\n" + "subject的父类是:" + subject.getClass().getSuperclass());
		System.out.print("subject实现的接口是:");

		Class<?>[] interfaces = subject.getClass().getInterfaces();
		for (Class<?> i : interfaces) {
			System.out.print(i.getName() + ", ");
		}

		System.out.println("\n\n" + "运行结果为:");
		subject.request();
	}
}

程序运行结果:

true
subject的Class类是:class com.sun.proxy.$Proxy0
subject中的属性有:m1, m2, m3, m0, 
subject中的方法有:request, equals, toString, hashCode, 
subject的父类是:class java.lang.reflect.Proxy
subject实现的接口是:com.markliu.proxy.Subject, 

运行结果为:
before calling public abstract void com.markliu.proxy.Subject.request()
From real subject.
after calling public abstract void com.markliu.proxy.Subject.request()

    这个结果的信息非常重要。要理解subject和Proxy之间的联系,request()和invoke()的调用关系。

    由运行结果可以看出:

  • subject instanceof Proxy为true,subject是Proxy的一个实例,这个实例实现了Subject接口

  • subject的Class类是com.sun.proxy.$Proxy0,是一个通过反射机制动态生成的,下面分析其源代码

  • subject中只有四个方法,其中一个就是抽象主题角色中的方法request

     从Client中的代码看,从newProxyInstance这个方法入手,看一下Proxy类中newProxyInstance方法的源代码,是如何将interfaces和invacationhandler建立联系,创建了动态代理类。


    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
		...
        final Class<?>[] intfs = interfaces.clone();

        /*
         * 根据类加载器loader获取实现intfs接口的类类型,其实就是com.sun.proxy.$Proxy0
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 通过指定的handler调用com.sun.proxy.$Proxy0的构造方法.
         */
        try {
        	...
            /*
             * 获取public类型的构造函数
             * 其中有定义private static final Class<?>[] constructorParams = { InvocationHandler.class };
             * 打开getConstructor(constructorParams)方法:
             * 会看到底层调用的是:
             *     private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
             *                           int which) throws NoSuchMethodException
	     *	    {
	     *          // 获取所有的public类型的构造函数
	     *	        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
	     *	        for (Constructor<T> constructor : constructors) {
	     *              // 如果传入的构造函数的参数parameterTypes(也就是客户端传入的实现InvocationHandler接口的对象h)
	     *              // 与其中的public类型的构造函数的参数匹配,就返回该构造函数,即此Demo中的public DynamicProxySubject(Object realSubject);
	     *              // 从而实现了在动态代理类中持有InvocationHandler的引用,则可以调用invoke方法
	     *	            if (arrayContentsEq(parameterTypes, constructor.getParameterTypes())) {
	     *	                return getReflectionFactory().copyConstructor(constructor);
	     *	            }
	     *	        }
	     *	        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
	     *	    }
             */
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 此处创建了该实例,传入InvocationHandler对象
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
        	...
        }
    }

       Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事.
        (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0。$Proxy0类 实现了interfaces的接口,并继承了Proxy类.
        (2)实例化$Proxy0并在构造方法中把InvocationHandler对象传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:

public class Proxy implements java.io.Serializable {

    protected InvocationHandler h;
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
    ...
}
      这样该动态代理类就持有InvocationHandler的引用,则可以调用invoke方法。下面具体看下该动态代理类,继承了Proxy的$Proxy0的源代码:

package com.markliu.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Subject {
	private static Method m1;
	private static Method m0;
	private static Method m3;
	private static Method m2;

	static {
		try {
			m1 = Class.forName("java.lang.Object").getMethod("equals",
					new Class[] { Class.forName("java.lang.Object") });
			m0 = Class.forName("java.lang.Object").getMethod("hashCode",
					new Class[0]);
			m3 = Class.forName("***.RealSubject").getMethod("request",
					new Class[0]);
			m2 = Class.forName("java.lang.Object").getMethod("toString",
					new Class[0]);

		} catch (NoSuchMethodException nosuchmethodexception) {
			throw new NoSuchMethodError(nosuchmethodexception.getMessage());
		} catch (ClassNotFoundException classnotfoundexception) {
			throw new NoClassDefFoundError(classnotfoundexception.getMessage());
		}
	} 

	public $Proxy0(InvocationHandler invocationhandler) {
		super(invocationhandler);
	}

	@Override
	public final boolean equals(Object obj) {
		try {
			return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	@Override
	public final int hashCode() {
		try {
			return ((Integer) super.h.invoke(this, m0, null)).intValue();
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	public final void request() {
		try {
			// 此处调用了InvocationHandler接口中的invoke方法!
			super.h.invoke(this, m3, null);
			return;
		} catch (Error e) {
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	@Override
	public final String toString() {
		try {
			return (String) super.h.invoke(this, m2, null);
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}
}

     所以可以看出动态代理类$Proxy0实现了Subject接口,在Demo中调用subject.request()时,由里氏代换原则知,将调用$Proxy0中的request的方法,最终调用实现InvocationHandler接口的invoke方法。

   总结:JDK动态代理的类结构如下:

       因为java已经为我们创建了Proxy类,所以需要有办法告诉Proxy类你需要做什么。你不能像以前一样直接把代码放在Proxy类中,因为Proxy类不是你直接实现的。需要将实际添加的业务逻辑放在实现InvocationHandler中。InvocationHandler的工作是响应代理的任何调用,即调用生成的代理类的方法时最终会交给InvocationHandler的invoke方法,其中就会调用RealSubject的方法。你可以把InvocationHandler想成是代理收到方法调用后,请求做实际业务处理的对象。

四. 综合案例

  需求:

  (1) 在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统;        

  (2) 在进行商务信息查询时系统需要记录查询日志,以便根据查询次数收取查询费用。

信息查询系统设计方案示意图

  实现方案:利用JDK动态代理实现一个代理链。UML结构图如下所示:

  代码如下:

  Searcher.java

public interface Searcher {

	public String doSearch(String userName, String password);
}

 RealSearcher.java

public class RealSearcher implements Searcher {

	@Override
	public String doSearch(String userName, String password) {
		System.out.println("用户" + userName + "使用关键词查询商务信息!");
		String result = "查询返回的结果";
		return result;
	}

}

  Logger .java

public class Logger {
	
	public void log(String userName) {
		System.out.println("开始记录日志:" + new Date() + ",用户" + userName + " 进行了查询操作!");
	}
}
  AccessValidator.java
public class AccessValidator {

	public boolean validateUser(String userName, String password) {
		System.out.println("查询数据库进行验证...");
		if (userName == null || password == null) {
			return false;
		}
		if (userName.equals("SunnyMarkLiu") && password.equals("123456")) {
			
			return true;
		}
		return false;
	}
}
  LoggerInvocationHandler.java
public class LoggerInvocationHandler implements InvocationHandler {

	private Object target;
	
	public LoggerInvocationHandler(Object target) {
		this.target = target;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("开始日志记录:" + new Date() + " " + (String) args[0] + " 查询数据");
		Object result = method.invoke(target, args);
		System.out.println("日志记录: 查询成功,结果为: " + result);
		System.out.println("Logger返回...");
		return result;
	}

	/**
	 * 返回经过 LoggerInvocationHandler 包装的代理对象
	 * @param target
	 * @return
	 */
	public static Object createProxy(Object target) {
		Class<?> allClass = target.getClass();
		return Proxy.newProxyInstance(allClass.getClassLoader(), 
					          allClass.getInterfaces(), 
					          new LoggerInvocationHandler(target));
	}
}
  ValidatorInvocationHandler.java
public class ValidatorInvocationHandler implements InvocationHandler {

	private Object target;
	
	public ValidatorInvocationHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if (validate((String) args[0], (String) args[1])) {
			System.out.println((String) args[0] + "用户验证成功!");
			Object result = method.invoke(target, args);
			
			System.out.println("Validator返回...");
			return result;
		}
		return null;
	}
	
	/**
	 * 返回经过 ValidatorInvocationHandler 包装的代理对象
	 * @param target
	 * @return
	 */
	public static Object createProxy(Object target) {
		Class<?> allClass = target.getClass();
		return Proxy.newProxyInstance(allClass.getClassLoader(), 
						   allClass.getInterfaces(), 
						   new ValidatorInvocationHandler(target));
	}
	
	private boolean validate(String userName, String password) {
		AccessValidator validator = new AccessValidator();
		return validator.validateUser(userName, password);
	}
}

  DynamicProxyTest.java 测试类

public class DynamicProxyTest {

	@Test
	public void testBasicProxy() {
		
		String userName = "SunnyMarkLiu";
		String password = "123456";
		Searcher targetSearcher = new RealSearcher();
		targetSearcher.doSearch(userName, password);
		System.out.println("\n使用代理之后\n");
		
		// 注意此处链式创建代理!可以修改代理的顺序!
		Searcher proxySearcher = (Searcher) ValidatorInvocationHandler.createProxy(
				LoggerInvocationHandler.createProxy(targetSearcher));
		
		proxySearcher.doSearch(userName, password);
		
		System.out.println();
		
		Searcher proxySearcher2 = (Searcher) LoggerInvocationHandler.createProxy(
				ValidatorInvocationHandler.createProxy(targetSearcher));
		
		proxySearcher2.doSearch(userName, password);
	}
}
  运行输出:
用户SunnyMarkLiu使用关键词查询商务信息!

使用代理之后

查询数据库进行验证...
SunnyMarkLiu用户验证成功!
开始日志记录:Tue Apr 05 23:07:44 CST 2016 SunnyMarkLiu 查询数据
用户SunnyMarkLiu使用关键词查询商务信息!
日志记录: 查询成功,结果为: 查询返回的结果
Logger返回...
Validator返回...

开始日志记录:Tue Apr 05 23:07:45 CST 2016 SunnyMarkLiu 查询数据
查询数据库进行验证...
SunnyMarkLiu用户验证成功!
用户SunnyMarkLiu使用关键词查询商务信息!
Validator返回...
日志记录: 查询成功,结果为: 查询返回的结果
Logger返回...


  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunny Mornings

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值