动态代理和AOP02

如要转载请标明作者zjrodger和出处:http://blog.youkuaiyun.com/zjrodger/,谢谢微笑

                                                        笔记目录
· 分析JVM动态生成的类
(·让JVM先创建动态类,再创建其实例对象一般方法
1. 创建一个实现了Collection接口的动态代理类(并不创建其实例对象)。
2. 实现Collection接口,创建一个动态代理类,并且创建该动态类的对象。
(·) 让JVM创建动态类及其实例对象快速简便方法
(·) 总结让JVM创建动态代理类及其实例对象
(·) 分析InvocationHandler对象的运行原理和动态代理的运行原理
1. 猜想分析动态生成的类的内部代码
2. 动态代理类的工作原理图
3. 实际开发中的动态代理类的实现(重要



·  分析JVM动态生成的类
(·让JVM先创建动态类,再创建其实例对象一般方法
第一步:创建动态代理类。新创建的代理类的类名一般是:$Proxy0。 
第二部:创建动态类的实例对象。
1. 创建一个实现了Collection接口的动态代理类(并不创建其实例对象)。
【实现功能】
(1)通过实现某个接口,比如java.util.Collection,创建一个动态的代理类。 
(2)该接口可以由config.properties配置文件指定。
(3)新创建的动态的代理类会将该代理类的构造器和普通方法,以一个列表的形式打印出来。
【程序代码】
(1)config.properties配置文件中的内容: keyName01=java.util.Collection
(2) Java代码

package com.zjrodger.day3;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Properties;

/**
 * 程序功能: 
 * 1. 通过实现某个接口,比如java.util.Collection,创建一个动态的代理类。
 * 2. 该接口可以由config.properties配置文件指定。
 * **/
public class ProxyTest{
	
	private static String interfaceName = null;
	
	//在静态初始化块儿中读取配置文件config.properties中的类名。
	static{
		InputStream inStream = null;
		Properties prop = new Properties();	
		try {
			inStream = ProxyTest.class.getClassLoader().getResourceAsStream("com/zjrodger/day3/config.properties");
			prop.load(inStream);
			ProxyTest.interfaceName = prop.getProperty("keyName01");			
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("读取配置文件config.properties失败!!");
		} finally{
			try {
				inStream.close();				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("关闭配置文件config.properties失败!!");
			}
			inStream = null;
		}
	}
	
	public static void main(String[] args){	
		Class<?> clazzInterface = null;
		
		try {
			clazzInterface = Class.forName(ProxyTest.interfaceName);			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("加载ProxyTest.interfaceName失败!!");
		}
		
		System.out.println(clazzInterface.getName());
		
		// 通过实现Collection接口,创建一个动态的代理类。
		Class<?> clazzMyProxyName01 = Proxy.getProxyClass(clazzInterface.getClassLoader(), clazzInterface);
		
		System.out.println("-----------------动态代理类的构造方法列表-------------");

		//预期目标: $Proxy0(java.lang.reflect.InvocationHandler)
		Constructor[] constructors = clazzMyProxyName01.getConstructors();
		for(Constructor oneConstructor: constructors){			
			StringBuilder sBuilder = new StringBuilder(oneConstructor.getName()+'(');
			
			Class[] parameterTyes = oneConstructor.getParameterTypes();
			for(Class oneParamType: parameterTyes){
				sBuilder.append(oneParamType.getName());
				sBuilder.append(',');
			}
			if(parameterTyes != null && parameterTyes.length != 0){
				sBuilder.deleteCharAt(sBuilder.length()-1);
			}
			
			sBuilder.append(')');	
			System.out.println(sBuilder.toString());
		}			
		System.out.println("\n");
		
		
		System.out.println("-----------------动态代理类的普通方法列表-------------");
		Method[] methods = clazzMyProxyName01.getMethods();
		for(Method oneMethod: methods){			
			StringBuilder sBuilder = new StringBuilder(oneMethod.getName()+'(');
			
			Class[] parameterTyes = oneMethod.getParameterTypes();
			for(Class oneParamType: parameterTyes){
				sBuilder.append(oneParamType.getName());
				sBuilder.append(',');
			}
			if(parameterTyes != null && parameterTyes.length != 0){
				sBuilder.deleteCharAt(sBuilder.length()-1);
			}			
			sBuilder.append(')');	
			System.out.println(sBuilder.toString());
		}		
	}
}

【运行结果】

java.util.Collection
-----------------动态代理类的构造方法列表-------------
$Proxy0(java.lang.reflect.InvocationHandler)
-----------------动态代理类的普通方法列表-------------
add(java.lang.Object)
hashCode()
clear()
equals(java.lang.Object)
toString()
contains(java.lang.Object)
isEmpty()
addAll(java.util.Collection)
iterator()
size()
toArray([Ljava.lang.Object;)
toArray()
remove(java.lang.Object)
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait()
wait(long,int)
wait(long)
getClass()
notify()
notifyAll()

2. 实现Collection接口,创建一个动态代理类,并且创建该动态类的对象。
【实现功能】
通过实现java.util.Collection接口,创建一个动态的代理类,并且创建该类的实例对象。 
方式一:创建一个内部类,该内部类实现了InvocationHandler接口。
方式二:通过匿名内部类来创建动态代理类的实例对象。
【程序代码】
(1)config.properties配置文件中的内容: keyName01=java.util.Collection
(2) Java代码
package com.zjrodger.day3;

import java.io.IOException;
import java.io.InputStream;
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;
import java.util.Properties;

/**
 * 程序功能: 
 * 1. 通过实现某个接口,比如java.util.Collection,创建一个动态的代理类。
 * 2. 该接口可以由config.properties配置文件指定。
 * **/
public class ProxyTest{
	
	private static String interfaceName = null;
	
	//在静态初始化块儿中读取配置文件config.properties中的类名。
	static{
		InputStream inStream = null;
		Properties prop = new Properties();	
		try {
			inStream = ProxyTest.class.getClassLoader().getResourceAsStream("com/zjrodger/day3/config.properties");
			prop.load(inStream);
			ProxyTest.interfaceName = prop.getProperty("keyName01");			
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("读取配置文件config.properties失败!!");
		} finally{
			try {
				inStream.close();				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("关闭配置文件config.properties失败!!");
			}
			inStream = null;
		}
	}
	
	public static void main(String[] args){	
		Class<?> clazzInterface = null;
		
		try {
			clazzInterface = Class.forName(ProxyTest.interfaceName);			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("加载ProxyTest.interfaceName失败!!");
		}
		
		System.out.println(clazzInterface.getName());
		
		// 通过实现Collection接口,创建一个动态的代理类。
		Class<?> clazzMyProxyClassName01 = Proxy.getProxyClass(clazzInterface.getClassLoader(), clazzInterface);
		
		
		/**
		 * 创建该动态代理类的实例对象。
		 * **/		
		try {
			//方式一:创建一个内部类,该内部类实现了InvocationHandler接口。
			class MyInvocationHandler implements InvocationHandler{
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					return null;
				}			
			}			
			Constructor constructor = clazzMyProxyClassName01.getConstructor(InvocationHandler.class);

			//创建动态代理类的实例对象。			
			Collection proxyInstance01 = (Collection)constructor.newInstance(new MyInvocationHandler());
			System.out.println(proxyInstance01.toString());
			proxyInstance01.clear();

			
			//方式二:通过匿名内部类来创建动态代理类的实例对象。
			Collection proxyInstance02 = (Collection)constructor.newInstance(new InvocationHandler(){
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					return null;
				}			
			});
						
			System.out.println("The size is "+proxyInstance02.size());
			
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}				
	}
}

(·) 让JVM创建动态类及其实例对象快速简便方法
【实现功能】
通过实现java.util.Collection接口,创建一个动态的代理类,并且创建该类的实例对象。 
方式三 快速简便方法的代码结构:
 Foo f = (Foo) Proxy.newProxyInstance( Foo.class.getClassLoader(),new Class[] { Foo.class },handler);
【程序代码】
(1)config.properties配置文件中的内容:keyName01=java.util.Collection
(2) Java代码
package com.zjrodger.day3;

import java.io.IOException;
import java.io.InputStream;
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;
import java.util.Properties;

/**
 * 程序功能: 
 * 1. 通过实现某个接口,比如java.util.Collection,创建一个动态的代理类。
 * 2. 该接口可以由config.properties配置文件指定。
 * **/
public class ProxyTest{
	
	private static String interfaceName = null;
	
	//在静态初始化块儿中读取配置文件config.properties中的类名。
	static{
		InputStream inStream = null;
		Properties prop = new Properties();	
		try {
			inStream = ProxyTest.class.getClassLoader().getResourceAsStream("com/zjrodger/day3/config.properties");
			prop.load(inStream);
			ProxyTest.interfaceName = prop.getProperty("keyName01");			
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("读取配置文件config.properties失败!!");
		} finally{
			try {
				inStream.close();				
			} catch (IOException e) {
				e.printStackTrace();
				System.out.println("关闭配置文件config.properties失败!!");
			}
			inStream = null;
		}
	}
	
	public static void main(String[] args){	
		Class<?> clazzInterface = null;
		
		try {
			clazzInterface = Class.forName(ProxyTest.interfaceName);			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("加载ProxyTest.interfaceName失败!!");
		}
		
		System.out.println(clazzInterface.getName());
		
		// 通过实现Collection接口,创建一个动态的代理类。
		Class<?> clazzMyProxyClassName01 = Proxy.getProxyClass(clazzInterface.getClassLoader(), clazzInterface);
		
		
		/**
		 * 创建该动态代理类的实例对象。
		 * **/		
		try {
			//方式三:让JVM创建动态类及其实例对象的快速简便方法。
			Collection proxyInstance03 = (Collection) Proxy.newProxyInstance(
					ProxyTest.class.getClassLoader(), 
					new Class[]{java.util.Collection.class}, 					
					new InvocationHandler(){						
						//定义一个目标类,注意区别它和Proxy代理类的关系;
						//另外,目标类和Proxy代理类都实现了相同的接口。
						ArrayList target = new ArrayList();						
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							
							long beginTime = System.currentTimeMillis();
							try {
								Thread.sleep(1000);
							} catch (Exception e) {
								e.printStackTrace();
							}
							Object returnedValue = method.invoke(target, args);
							long endTime = System.currentTimeMillis();
							System.out.println("代理类实例的代理方法"+method.getName()+"()执行的时间跨度为: "+(endTime - beginTime));
							return returnedValue;
						}			
					}
			);
			
			proxyInstance03.add("zjrodger01");
			proxyInstance03.add("Coffee");
			proxyInstance03.add("Zhuxinyue");
			System.out.println("The size is "+proxyInstance03.size());
			
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}				
	}
}

(·) 总结让JVM创建动态代理类及其实例对象
让JVM创建动态类及其实例对象,需要为其提供哪些信息?
(1) 生成的动态类中有哪些方法,通过让其实现相应的接口来告知JVM生成的动态类中所包含的各个方法。
(2) 新创建的类的字节码必须由一个关联的类加载器的对象。
(3) 创建动态类的实例对象时,还必须向其构造其中传入一个InvocationHandler。

(·) 分析InvocationHandler对象的运行原理和动态代理的运行原理
1. 猜想分析动态生成的类的内部代码
(1) 动态生成的类实现了Collection接口(可以实现若干个接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
(2) 构造方法接受一个InvocationHandler对象,接收对象了要干什么用呢?该方法内部的代码会是怎样的呢?
(3) 实现Collection接口的动态类中的各个方法的代码又是怎样的呢?
InvocationHandler接口中定义的invoke()方法接受的单个参数又是什么意思? 图解如下说明:
注释描述:Client程序调用objProxy.add("add")方法时,涉及三个要素:objProxy对象,add方法,"abc"参数
Class Proxy${
    add(Object object){
        return handler.invoke(Object proxy, Method method, Object[] args);
    }
}
(4) 分析先前打印动态类的内部对象时,结果为什么会是null呢?
   动态代理类中的一部分方法继承自java.lang.Object类,当调用 hashCode()方法, equals()方法和toString()方法时,这些方法会被推送给目标类的方法;若调用剩余的其他方法,比如getClass()方法,Proxy类就会有自己的实现,即不会将方法交给Handler。

2. 动态代理类的工作原理图

    在log()(划红线的地方)传递一个对象,这样就可以执行对象中的方法,从而避免了 硬编码把代码写死的状态,方便以后灵活的应用。
这样就是AOP(面向切面变成)的好处,即,将切面的代码用对象的方式进行封装,然后用对象的方式传递给InvocationHandler,这样相当于执行了切面的代码。

3. 实际开发中的动态代理类的实现(重要
    在实际的开发中,向InvocationHandler对象实际传入的对象应该有两个:一个是target对象,另一个是系统的切面对象(Advice接口的实现类对象)。
3.1)具体实现思路
(1)定义并且获得 target类实例。
(2)要定义一个接口,其名字叫做 Advice(Spring中的术语),。
    将系统切面代码(负责安全,日志,系统时间等具有辅助功能的代码)封装到接口Advice中去,然后获得Advice接口的实现类和实现类对象。
(3)编写生成Proxy对象的方法,假设其名为: Object myGetProxyInstance( Object  targetAdvice  advice   )

3.2)实现代码
【功能描述】
    利用动态代理功能,向ArrayList类(target类)中添加若干元素。
    在添加元素期间,打印系统辅助的切面代码的信息。
【程序代码】
生成动态代理对象的核心类
package com.zjrodger.aopframework02;

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

public class DynamicProxy {
	/**
	 * 1.根据传入的targe对象和advice对象,生成相应的Proxy对象。
	 * 2.代码改造:
	 *   也可以将targe对象和advice对象定义为DynamicProxy类的静态类属性,这样,在创建Proxy对象之前,
	 *   可以通过静态的setter()和getter()方法设置target对象和advice对象,这样,
	 *   当调用myNewProxyInstance()方法时,就不用将target对象和advice对象作为参数传入到方法中了。
	 * **/
	public static Object myNewProxyInstance(final Object target, final Advice advice){
		
		Object oneProxyInstance = Proxy.newProxyInstance(
				target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler(){
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {						
						advice.beforeMethod();  //系统辅助功能的切面代码01
						
						//核心业务代码						
						Object returnedValue = method.invoke(target, args);								
						System.out.println("核心方法\""+method.getName()+"()\"执行完毕,其返回值:"+returnedValue);						
						
						Thread.sleep(1500);
						advice.afterMethod();   //系统辅助功能的切面代码02
						return returnedValue;
					}
				}
		);		
		return oneProxyInstance;
	}
}

②Advice接口
package com.zjrodger.aopframework02;

public interface Advice {
	public void beforeMethod();
	public void afterMethod();
}

③Advice接口的实现类MyAdvice类
package com.zjrodger.aopframework02;

import java.text.DateFormat;
import java.util.Date;

public class MyAdvice implements Advice {

	private DateFormat df = DateFormat.getTimeInstance(DateFormat.MEDIUM);
	
	public void afterMethod() {
		System.out.println("执行核心代码之后,执行切面代码02,时间为:"+df.format(new Date()));
		System.out.println("\n");
	}

	public void beforeMethod() {
		System.out.println("执行核心代码之前,执行切面代码01,时间为:"+df.format(new Date()));
	}
}

④程序启动入口类
package com.zjrodger.aopframework02;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class DynamicProxyTestLauncher {
	public static void main(String[] args) {
		//定义一个taget类
		ArrayList target = new ArrayList();
		
		//定义一个advice类
		MyAdvice advice = new MyAdvice();
		
		//生成动态代理对象。
		Collection proxyObj = (Collection)DynamicProxy.myNewProxyInstance(target,advice);
		
		//调用新创建动态代理对象的方法。
		proxyObj.add("zjrodger");
		proxyObj.add("Lilin");
		System.out.println("Proxy对象的size属性为: "+proxyObj.size());

		//遍历新建的Proxy对象中存放的元素。
		for(Iterator it = proxyObj.iterator(); it.hasNext();){
			System.out.println(it.next());
		}
	}
}

【结果演示】
执行核心代码之前,执行切面代码01,时间为:14:37:59 
核心方法"add()"执行完毕,其返回值:true 
执行核心代码之后,执行切面代码02,时间为:14:38:00 

执行核心代码之前,执行切面代码01,时间为:14:38:00 
核心方法"add()"执行完毕,其返回值:true 
执行核心代码之后,执行切面代码02,时间为:14:38:02 

执行核心代码之前,执行切面代码01,时间为:14:38:02 
核心方法"size()"执行完毕,其返回值:2 
执行核心代码之后,执行切面代码02,时间为:14:38:03 

Proxy对象的size属性为: 2 
执行核心代码之前,执行切面代码01,时间为:14:38:03 
核心方法"iterator()"执行完毕,其返回值:java.util.AbstractList$Itr@1dff3a2 
执行核心代码之后,执行切面代码02,时间为:14:38:05 

zjrodger 
Lilin


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值