动态代理

动态代理

1.动态代理概述

代理模式是Java设计模式中的一种,其特征为代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

代理类与委托类之间通常存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现业务,而是通过调用委托类对象的相关方法来提供具体业务。

通过反射动态生成代理对象,通过代理对象调用委托类的方法.

作用:代理类会对委托类的方法进行过滤.

在Java中的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和接口可以生成JDK动态代理或动态代理对象。

按照代理的创建时间不同,可以分为两种:

Ø  静态代理:手动创建,再对其编译。在程序运行前,代理类的.class文件就已经存在。

Ø  动态代理:在程序运行时,通过反射机制动态创建而成。

2.动态代理的原理

动态代理的实现原理有些类似于过滤器的实现原理,但有所不同。
动态代理的代理类与委托类之间的关系更像是明星与经纪人之间的关系,也就是说,如果你想找某个明星演出的话,并不是找他本人,而是找到他的经纪人就可以了。
动态代理的实现过程很类似于这个过程,具体请看下图



3.Proxy代理类

Proxy类是Java的java.lang.reflect包下提供的,该类用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。

如果在程序中为一个或多个接口动态地生成实现类,就可以用Proxy类来创建动态代理类;

如果需要为一个或多个接口动态地创建实例,也可以使用Proxy类来创建动态代理实例。

static InvocationHandler

getInvocationHandler(Object proxy)
返回指定代理实例的调用处理程序。

static Class<?>

getProxyClass(ClassLoader loader,Class<?>... interfaces)
返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。

static boolean

isProxyClass(Class<?> cl)
当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。

static Object

newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

Ø  static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):

创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader参数指定生成动态代理类的类加载器。

Ø static Object newProxyInstance(ClassLoaderloader, Class<?>[] interfaces, InvocationHandler h):

直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke()方法。


4.InvocationHandler

InvocationHandler接口提供了invoke()方法,用于替换代理对象的每一个方法。

真实业务类可以通过代理类对象调用InvocationHandler接口提供的invoke()方法,来替代调用委托类的真实方法。

invoke()方法:通过代理对象去执行委托类的方法 , 即代理对象通过该方法去执行委托类的方法

以下是InvocationHandler的API内容:

Object

invoke(Object proxy,Method method, Object[] args)
在代理实例上处理方法调用并返回结果。

Ø  Object invoke(Object proxy, Method method, Object[] args):

在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

²  参数proxy:表示代理类对象,也就是Proxy.newProxyInstance()方法返回的对象,通常用不上。

²  参数method:表示当前被调用方法的反射对象,

²  参数args:表示调用目标方法时传入的实参。


5.实现动态代理

Proxy类:
       * newProxyInstance()
invocationHandler接口:
       * invoke(proxy对象,Method,对应方法的实参)

利用Java提供的Proxy类和InvocationHandler接口来生成动态代理类或动态代理对象,具体实现步骤如下:

1.定义一个业务接口,该接口提供具体业务方法的定义。

public interface Person {
	void sayMe();
	void sayHello(String name);
}

2.定义一个InvocationHandler接口的实现类,并重写invoke()方法。

public class MyInvocationHandler implements InvocationHandler {
	/**
	 * 执行动态代理对象的所有方法时,都会被替换成执行下面的invoke()方法.
	 *  * 参数proxy:代表动态代理对象.
 *  * 参数method:代表正在执行的方法.
	 *  * 参数args:代表调用目标方法时传入的实参.
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("---正在执行的方法: "+method);
		if(args == null){
			System.out.println("当前调用的方法没有参数.");
		}else{
			System.out.println("当前调用的方法需要传入的实参为:");
			for (Object val : args) {
				System.out.println(val);
			}
		}
		return null;
	}
}

3. 编写一个用于测试动态代理的测试类。

public class ProxyTest {
	public static void main(String[] args) {
		// 创建一个InvocationHandler对象
		InvocationHandler handler = new MyInvocationHandler();
		// 通过Proxy类使用指定的InvocationHandler来生成动态代理对象
		Person p = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, handler);
		// 调用动态代理对象的业务方法
		p.sayMe();
		p.sayHello("张无忌");
	}
}

6、动态代理的作用

可以获取到对应委托类方法的参数.
阻止调用对应委托类的方法.
修改委托类方法的返回值.

通过Java提供的Proxy类和InvocationHandler接口生成的动态代理类,可以阻止调用委托类的方法、过滤参数及修改对应方法的返回值等作用。

实现业务接口方法的实现类即委托类,具体操作如下:

1、创建一个实现类,实现Person接口,并重写业务方法。

public class Fanbingbing implements Person {
	@Override
	public void sayMe() {
		System.out.println("我真的是范冰冰哦!");
	}
@Override
	public String sayHello(String name) {
		System.out.println("你好:"+name+",我等你很久了...");
		return "我终于见到范冰冰啦!";
	}
}

2、编写一个用于测试动态代理的测试类。

public class FanbingbingTest {
	public static void main(String[] args) {
		Person p = (Person) Proxy.newProxyInstance(
				Person.class.getClassLoader(),
				Fanbingbing.class.getInterfaces(), new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						// 通过method的getName()方法获取业务方法名,进行阻止.
						if (method.getName().equals("sayMe")) {
							System.out.println("你想多了,哪那么容易见到啊!");
							return null;
						}
						// 通过args获取实参,进行修改
						if(method.getName().equals("sayHello")){
							String name = (String)args[0];
							method.invoke(Class.forName("app.java.proxy.Fanbingbing").newInstance(), "某局长");
						}
						// 修改返回值
						if(method.getName().equals("sayHello")){
							return "都是假的!";
						}
						return null;
					}
				});
		p.sayMe();
		p.sayHello("张无忌");
	}
}

Star:

package app.java.proxy;

public interface Star {

	public void song(String name);
	public String movie();
	
}

Fbb:

package app.java.proxy;

public class Fbb implements Star {

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

	public String movie() {
		System.out.println("movie........");
		return "武媚娘传奇,禁播了!!!";
	}

}

ProxyTest:

package app.java.proxy;

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

public class ProxyTest {

	public static void main(String[] args) {
		
		/**
		 * Proxy类的newProxyInstance(ClassLoader,Class[])方法:
		 *  * 获取对应委托类的代理对象.
		 *  * 参数一:指定对应委托类的类加载器.
		 *  * 参数二:指定委托类的Class
		 *  * 参数三:InvocationHandler的实例(重写invoke)
		 *  
		 *  动态代理的作用:
		 *   * 不需要得到对应委托类的对象.
		 *   * 通过代理类对象,对委托类的方法进行过滤(修改)等.
		 */
		Star fbb = (Star)Proxy.newProxyInstance(Star.class.getClassLoader(), Fbb.class.getInterfaces(), new InvocationHandler() {
			/**
			 * invoke(Object proxy, Method method, Object[] args)
			 *  * 该方法的作用:调用对应的委托类的方法.
			 *  * 参数一:返回的是代理类对象.
			 *  * 参数二:返回的是对应委托类方法的Method类型对象.
			 *  * 参数三:返回的是对应委托类方法接收的实参.
			 */
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				// 可以获取到对应委托类方法的参数. 
				/*String name = (String)args[0];
				if(name.equals("zhangwuji")){
					method.invoke(new Fbb(), "zhouzhiruo");
				}*/
				// 阻止调用对应委托类的方法
				/*String methodName = method.getName();
				if(methodName.equals("movie")){
					System.out.println("范冰冰最近太累了,不能拍电影,改唱歌了!");
				}*/
				// 修改委托类方法的返回值
				String methodName = method.getName();
				if(methodName.equals("movie")){
					return "武媚娘传奇,重播了!!!";
				}
				
				return null;
			}
		});
		
//		fbb.song("zhangwuji");
		String value = fbb.movie();
		System.out.println(value);
	}
	
}

ProxyTest2:

package app.java.proxy;

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

public class ProxyTest2 {

	public static void main(String[] args) {
		
		Star star = (Star)Proxy.newProxyInstance(Star.class.getClassLoader(), Fbb.class.getInterfaces(), new InvocationHandler() {
			
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return method.invoke(new Fbb(), args);
			}
		});
		
		System.out.println(star);
		
		star.movie();
		
	}
	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值