java动态代理

1.生活中代理

    在生活中有好多都用到了代理,比如:去商店买苹果,商店就好比是一个代理,否则,我们需要去苹果园那里买苹果。

2.动态代理类

    对于接口:JVM运行生成代理类,必须有接口。

    对于普通类:对于没有接口的类可以cglib一个库生成一个类的子类,子类来实现该类方法,子类可以继承父类的所有方法。

3.动态代理原理图解



4.动态代理理解代码

package com.jn.proxy;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

public class ProxyTest {

	/**
	 * @param args
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws NoSuchMethodException 
	 * @throws SecurityException 
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 */
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
		System.out.println("=====================begin constructors list ======================");
		Class<?> clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		System.out.println(clazzProxy.getName());
		Constructor[] constructors = clazzProxy.getConstructors();
		for(Constructor constructor: constructors){
			String name = constructor.getName();
			StringBuilder sBuilder = new StringBuilder(name);
			sBuilder.append("(");
//			获取构造器中的参数
			Class[] clazzParams = constructor.getParameterTypes();
			for(Class clazzParam:clazzParams){
				sBuilder.append(clazzParam.getName()).append(",");
			}
//			构造器没有参数处理。
			if(clazzParams != null && clazzParams.length != 0){
				sBuilder.deleteCharAt(sBuilder.length()-1);
			}
			sBuilder.append(")");
			System.out.println(sBuilder.toString());
		}
		
		System.out.println("=====================begin methods list ======================");
		Method[] methods = clazzProxy.getMethods();
		for(Method method: methods){
			String name = method.getName();
			StringBuilder sBuilder = new StringBuilder(name);
			sBuilder.append("(");
//			获取构造器中的参数
			Class[] clazzParams = method.getParameterTypes();
			for(Class clazzParam:clazzParams){
				sBuilder.append(clazzParam.getName()).append(",");
			}
//			构造器没有参数处理。
			if(clazzParams != null && clazzParams.length != 0){
				sBuilder.deleteCharAt(sBuilder.length()-1);
			}
			sBuilder.append(")");
			System.out.println(sBuilder.toString());
		}
		
		System.out.println("=====================begin create instance proxy list ======================");
//		clazzProxy.newInstance();
		class myInvocationHandler1 implements InvocationHandler{
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
		//		System.out.println("invoke");
				return null;
			}
			
		}
//		这里获取带参数的构造器,InvocationHandler.class只是构造器的参数类型,
		Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
//		进行实例化,需要构造器中参数的真正实例。实例化方法1
		Collection proxy1 = (Collection)constructor.newInstance(new myInvocationHandler1());
//		返回为null,对象为null或toString方法返回null,在这里是toString返回为null
		System.out.println(proxy1);
//		调用clear方法,没有问题
		proxy1.clear();
//		调用size方法,
//		错误原因:在调用invoke的时候返回null,将无效无法转换为整数。
		/**
		 * Exception in thread "main" java.lang.NullPointerException
			at $Proxy0.size(Unknown Source)
			at proxy.jn.ProxyTest.main(ProxyTest.java:81)
		 */
//		proxy1.size();
		
		
//		利用匿名内部类来实现构造器的实例化 实例化方法2
		Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
			}
		});
		proxy2.clear();
		
//		实例化方法3
		Collection proxy3 = (Collection)Proxy.newProxyInstance(
				Collection.class.getClassLoader(),
				new Class[]{Collection.class}, new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args)
							throws Throwable {
						ArrayList target = new ArrayList();
						Long startTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						Long endTime = System.currentTimeMillis();
						System.out.println(method.getName() + " running time = " +(endTime - startTime));
						return retVal;
					}
				});
		proxy3.add("aaa");
		proxy3.add("bbb");
		proxy3.add("ccc");
//		因为每次调用add,调用3次都会调用invoke方法,ArrayList会创建3个不同的,
//		调用size方法又会创建一个新的ArrayList,所以显示结果为0
		System.out.println(proxy3.size());
		/**
		 *  add running time = 0
			add running time = 0
			add running time = 0
			size running time = 0
			0
		 */
//		实例化方法4 创建一个ArrayList
		Collection proxy4 = (Collection) Proxy.newProxyInstance(
				Collection.class.getClassLoader(),
				new Class[] { Collection.class },
				new InvocationHandler() {
				ArrayList target = new ArrayList();
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						Long startTime = System.currentTimeMillis();
						Object retVal = method.invoke(target, args);
						Long endTime = System.currentTimeMillis();
						System.out.println(method.getName()
								+ " running time = " + (endTime - startTime));
						return retVal;
//						按照下面这样写将出现死循环
//						method.invoke(proxy4, args);
					}
				});
//		Object obj = proxy4.add("aaa");//代理的返回值,实际就是invoke方法的返回值。
		proxy4.add("aaa");
		proxy4.add("bbb");
		proxy4.add("ccc");
		// 因为每次调用add,调用3次都会调用invoke方法,ArrayList会创建1个相同的,
		// 调用size方法又会创建一个新的ArrayList,所以显示结果为3
		System.out.println(proxy4.size());
		System.out.println(proxy4.getClass().getName());
		/**
		 *  add running time = 0
			add running time = 0
			add running time = 0
			size running time = 0
			3
		 */
		
//		1.分析InvocationHandler对象运行原理,
		/**
		 * public $Proxy0{
		 * private InvocationHandler handler;
//		 * 构造器
		 * public $Proxy0(InvocationHandler handler){
		 * 		this.handler = handler;
		 * }
		 * add(Objcet object){
		 * 		retrun handler.invoke(
		 * 					Object proxy,//objProxy对象
		 * 					Method method,//add方法
							Object[] args//add方法的参数
							);
		 * }
		 * size(){
		 * 		return handler.invoke(this.getClass,getMethod("size"),null); 
		 * }
		 * }
		 * 在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法
		 * 的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序
		 * 的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。
		 * 代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用
		 * 行为与其对 java.lang.Object 实例的操作一样。 
		 */
		
//		实例化方法5 创建一个更一般的代理
		final ArrayList target = new ArrayList();
		Collection proxy5 = (Collection)getProxy(target,new AdviceImpl());
		proxy5.add("jiangning");
		proxy5.size();
	}

	/**
	 * 需要将目标抽取为一个参数
	 * 需要将系统功能抽取为一个对象,我要调用你,你的名字我不知道,是通过接口来进行调用的。
	 * 定义一个契约来进行调用。
	 * @param target 目标
	 * @param advice 系统功能
	 * @return
	 */
	private static Object getProxy(final Object target,final Advice advice) {
		Object proxy5 =  Proxy.newProxyInstance(
				target.getClass().getClassLoader(),
				//与target实现相同的接口
				target.getClass().getInterfaces(),
				//new Class[] { Collection.class },
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {
						advice.beforeMethod(method);
						Object retVal = method.invoke(target, args);
						advice.afterMethos(method);
						return retVal;
					}
				});
		return proxy5;
	}

}

package com.jn.proxy;

import java.lang.reflect.Method;

public interface Advice {

	public void beforeMethod(Method method);
	
	public void afterMethos(Method method);
}

package com.jn.proxy;

import java.lang.reflect.Method;

public class AdviceImpl implements Advice {
	Long startTime = 0L;
	@Override
	public void beforeMethod(Method method) {
		startTime = System.currentTimeMillis();
		System.out.println("beforeMethod");
	}

	@Override
	public void afterMethos(Method method) {
		Long endTime = System.currentTimeMillis();
		System.out.println(method.getName()
				+ " running time = " + (endTime - startTime));
		System.out.println("afterMethod");
	}

}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值