如要转载请标明作者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
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
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
target,
Advice
advice
)。
3.2)实现代码
【功能描述】
在添加元素期间,打印系统辅助的切面代码的信息。
【程序代码】
①生成动态代理对象的核心类
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 |