java 动态代理

本文介绍了动态代理的概念及其在Java中的实现方式,包括如何利用动态代理为对象的方法添加额外的功能,如计算方法执行时间等。同时对比了动态代理与装饰模式的区别,并通过示例展示了如何使用动态代理实现面向切面编程(AOP)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



 

动态代理

理解:动态代理就是使用原先类中时,使用代理向原先类的方法的(前面,后面,异常产生时,前面和后面)添加代理功能或处理功能 比如:在代码类的前面和后面添加计算原先方法执行时间的功能,也可以说是对某个方法的增强。

动态代理执行步骤:

示例代码

		Collection coll = (Collection)Proxy.newProxyInstance(Collection.class
				.getClassLoader(), new Class[]{Collection.class},
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						Object o = method.invoke(list, args);
						return o;
					}
				});
		coll.add("aa");

首先会创建一个代理对象Proxy.newProxyInstance 根据类实现的接口

下一步会指定InvocationHandler  因为代理类每执行一次方法,便会到记忆的InvocationHandler 中执行invoke方法,并且把(代理类的对象,方法名,参数传进行) 而invoke方法的几个参数正是通过执行方法的时候传进来的

代理方法执行一次,便在invokecation里面执行一次invoke方法   invokecation被创建后不会被再次创建

当coll.add("aa");执行时找到记忆的invokecation 并调用Invoke方法,把参数传进去,由代理对象执行这个方法,并返回执行的结果

 

 

 

动态代理和装饰模式区别:

装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;
代理模式:给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用

装饰模式应该为所装饰的对象增强功能;代理模式对代理的对象施加控制,并不提供对象本身的增强功能

二者的实现机制确实是一样的,可以看到他们的实例代码重复是很多的。但就语义上说,这两者的功能是相反的,模式的一个重要作用是简化其他程序员对你程序的理解,

你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大家就知道是在限制

       这两个设计模式看起来很像。对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

        然而,实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话 说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

       我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。    

 代理方法执行示意图

对象如果要代理,首先会找代理对象,并执行invocationHander方法,invocationHander对目标进行更改后 再执行对象本身的方法

简单代理方法示例:

		final ArrayList list = new ArrayList();  //要代理的对象
		Collection coll = (Collection)Proxy.newProxyInstance(Collection.class
				.getClassLoader(), new Class[]{Collection.class},
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {

						long startTime = System.currentTimeMillis();

						Object o = method.invoke(list, args);

						long stopTime = System.currentTimeMillis();
						
						System.out.println("方法" + method.getName() + "一共运行时间是"
								+ (stopTime - startTime));
						return o;
					}
				});
		coll.add("aa");
		coll.add("cc");
		coll.add("dd");
		System.out.println(coll.size());

 

将方法提取出来,改成通用的取得代理类的方法

1、创建建议接口和实现类:

	public interface Adivse {

	public void startTime(Method method, Object[] args, Object object);

	public void stopTime(Method method, Object[] args, Object object);

	}

 

	public class MyAdivse implements Adivse {
	long startTime = 0;
	@Override
	public void startTime(Method method, Object[] args, Object object) {
		int len = 0;
		if (args != null) {
			len = args.length;
		}
		else {
			len = 0;
		}
		startTime = System.currentTimeMillis();
		System.out.println("method " + method.getName() + " 一共有" + len + " 个参数"
				+ "现在操作的对象是" + object.getClass().getName());
	}
	@Override
	public void stopTime(Method method, Object[] args, Object object) {
		long stopTime = System.currentTimeMillis();
		long runtime = stopTime - startTime;
		System.out.println("方法" + method.getName() + "一共运行时间是" + runtime);
	}
}


2、建立取得代理类

		public static Object getProxy(final Object object, final Adivse adivse) {

			Object proxyObject = Proxy.newProxyInstance(object.getClass()
				.getClassLoader(), object.getClass().getInterfaces(),
				new InvocationHandler() {

					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {

						adivse.startTime(method, args, object);
						Object methodOb = method.invoke(object, args);

						adivse.stopTime(method, args, object);

						return methodOb;
					}
				});

			return proxyObject;

		}


3、调用方法

		public static void main(String[] args) {
		final ArrayList list = new ArrayList(); // 要代理的对象

		Collection coll = (Collection)getProxy(list, new MyAdivse());

		coll.add("aa");
		coll.add("cc");
		coll.add("dd");
		System.out.println(coll.size()); }

 

4、输出结果

method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是1
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method size 一共有0 个参数现在操作的对象是java.util.ArrayList
方法size一共运行时间是0
3


模拟spring框架实现AOP代理

血的教训:

 

执行第三步:

//bean = ibf.getProxy(target, adivse);  第一个传的参数必须是目标对象,并不是代理对象。例如有的bean只是实现了接口接口的代理对象

//传递的参数必须是参数不能是字符串

 

1、创建 建议接口和实现类

	public interface Adivse {

	public void startTime(Method method, Object[] args, Object object);

	public void stopTime(Method method, Object[] args, Object object);

	}

 

	public class MyAdivse implements Adivse {
	long startTime = 0;
	@Override
	public void startTime(Method method, Object[] args, Object object) {
		int len = 0;
		if (args != null) {
			len = args.length;
		}
		else {
			len = 0;
		}
		startTime = System.currentTimeMillis();
		System.out.println("method " + method.getName() + " 一共有" + len + " 个参数"
				+ "现在操作的对象是" + object.getClass().getName());
	}
	@Override
	public void stopTime(Method method, Object[] args, Object object) {
		long stopTime = System.currentTimeMillis();
		long runtime = stopTime - startTime;
		System.out.println("方法" + method.getName() + "一共运行时间是" + runtime);
	}
}

 

2、创建代理取得类的方法   有代理接口和代理类

public interface IProxyBeanFactory {
	public Object getProxy(Object object, Adivse adivse);
}
public class ProxyBeanFactory implements IProxyBeanFactory {

	@Override
	public Object getProxy(final Object object, final Adivse adivse) {
		Object proxyObject = Proxy.newProxyInstance(object.getClass()
				.getClassLoader(), object.getClass().getInterfaces(),
				new InvocationHandler() {

					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {

						adivse.startTime(method, args, object);
						Object methodOb = method.invoke(object, args);
						adivse.stopTime(method, args, object);

						return methodOb;
					}
				});

		return proxyObject;
	}

}


3、创建取得类对象的方法 可以选择是代理的方式还是直接创建的方式(取决于config.properties中选择的的代理,还是直接是对象)  构造参数需要传进去一个输入流来取得config.properties

public class BeanFactory {

	Properties pt = new Properties();

	public BeanFactory(InputStream is) throws Exception {
		super();
		pt.load(is);

	}

	public Object getBean(String name) throws Exception {

		Class classes = Class.forName(pt.getProperty(name));
		Object bean = classes.newInstance();
		if (bean instanceof IProxyBeanFactory) {

			IProxyBeanFactory ibf = (IProxyBeanFactory) bean;
			Object target = Class.forName(pt.getProperty(name + ".target"))
					.newInstance();
			String adivseName = pt.getProperty(name + ".divse");
			Adivse adivse = (Adivse) Class.forName(adivseName).newInstance();
			//第一个传的参数必须是目标对象,并不是bean。bean只是实现了接口接口的代理对象  
			//还有传递的必须是参数不能是字符串
			bean = ibf.getProxy(target, adivse);  
			return bean;
		} else {
			return bean;
		}
	}
}


4、方法的调用

public static void main(String[] args) throws Exception {

		InputStream is = Demo3.class.getResourceAsStream("config.properties");
		BeanFactory bf = new BeanFactory(is);
		Collection coll = (Collection) bf.getBean("x");
		coll.add("aa");
		coll.add("cc");
		coll.add("dd");
		System.out.println(coll.size());

	}

5、执行结果

method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是1
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method add 一共有1 个参数现在操作的对象是java.util.ArrayList
方法add一共运行时间是0
method size 一共有0 个参数现在操作的对象是java.util.ArrayList
方法size一共运行时间是0
3

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值