Java反射-动态代理概念与实现

一、代理的概念

动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性。

二、Java动态代理的类和接口(jdk1.6)

1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。

	// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
	public static InvocationHandler getInvocationHandler(Object proxy)
	
	// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象 
	public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
	
	// 方法 3:该方法用于判断指定类对象是否是一个动态代理类 
	public static boolean isProxyClass(Class<?> cl)
	
	// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 
	public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)

newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数:
ClassLoader loader:用来指明生成代理对象使用哪个类装载器;
Class<?>[] interfaces:用来指明生成哪个对象的代理对象,通过接口指定;
InvocationHandler h:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

2,java.lang.reflect.InvocationHandler:调用处理器接口,自定义invokle方法,用于实现对于真正委托类的代理访问。

	/**
	 该方法负责集中处理动态代理类上的所有方法调用。
	 第一个参数既是代理类实例,
	 第二个参数是被调用的方法对象
	 第三个方法是调用参数。
	 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
	*/
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

3,java.lang.ClassLoader:类装载器类

将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中
每次生成动态代理类对象时都需要指定一个类装载器对象:newProxyInstance()方法的第一个参数
 

三、代理的实现

java动态代理创建对象的过程为如下步骤:
1,通过实现 InvocationHandler 接口创建自己的调用处理器;

	// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
	// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
	InvocationHandler handler = new InvocationHandlerImpl(..); 

2,通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

	// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
	Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

3,通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

	// 通过反射从生成的类对象获得构造函数对象
	Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

4,通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

	// 通过构造函数对象创建动态代理类实例
	Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

	// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 
	InvocationHandler handler = new InvocationHandlerImpl(..); 
	// 通过 Proxy 直接创建动态代理类实例 
	Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );

四、demo

1、定义业务接口

public interface HelloService {

	public void sayHello(String name);

}

2、定义业务接口实现

public class HelloServiceImpl implements HelloService {

	public void sayHello(String name) {
		System.out.println("htllo " + name);
	}
}

3、创建生产代理对象的代理类

public class HelloServiceProxy implements InvocationHandler {

	// 目标对象
	private Object target;

	/**
	 * 绑定委托对象并返回一个【代理占位】
	 * 
	 * @param target
	 *            真实对象
	 * @param interfaces
	 *            委托对象
	 * @return 代理对象【占位】
	 */
	public Object bind(Object target, Class<?>[] interfaces) {
		this.target = target;
		// 取得代理对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), interfaces, this);
	}

	@Override
	/**
	 * 同过代理对象调用方法首先进入这个方法.
	 * 
	 * @param proxy
	 *            代理对象
	 * @param method
	 *            方法,被调用方法.
	 * @param args
	 *            方法的参数
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.err.println("############我是JDK动态代理################");
		Object result = null;
		// 反射方法前调用
		System.err.println("我准备说hello。");
		// 反射执行方法 相当于调用target.sayHelllo;
		result = method.invoke(target, args);
		// 反射方法后调用.
		System.err.println("我说过hello了");
		return result;
	}
}

4、测试类

public class Test {

	public static void main(String[] args) {

		HelloServiceProxy proxy = new HelloServiceProxy();
		HelloService service = new HelloServiceImpl();
		// 绑定代理对象。
		service = (HelloService) proxy.bind(service, new Class[] { HelloService.class }); 
		// 这里service经过绑定,就会进入invoke方法里面了。
		service.sayHello("张三");
	}
}

5、输出结果

############我是JDK动态代理################
我准备说hello。
htllo 张三
我说过hello了

参考:

https://blog.csdn.net/scplove/article/details/52451899

https://www.cnblogs.com/gonjan-blog/p/6685611.html

猜你喜欢

转载自blog.csdn.net/diweikang/article/details/87885411