代理二:深入研究InvocationHandler、动态代理类工作原理、实现AOP框架

9  通过实例深入研究InvocationHandler

先看如下代码:

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

public class ProxyDemo4 {
	public static void main(String[] args) throws Exception {
		//动态生成代理类的Class实例,此代理类实现Collection接口并调用目标类的方法
		Class clazzProxy = 
			Proxy.getProxyClass(Collection. class.getClassLoader(), Collection.class );
		
		System.out.println("--------begin create instance object-------");
		
		//获得此代理类的构造方法
		Constructor constructor = clazzProxy.getConstructor(InvocationHandler. class) ;
		
		//生成一个代理类对象,InvocationHandler用匿名内部类。invoke方法封装了要执行的目标代码
		Collection proxy = ( Collection)constructor.newInstance( new InvocationHandler(){
			public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
				return null ;
			}
		});
		
		System.out.println(proxy); //结果:null
		proxy.clear();  //执行没有返回值的方法,不会报告异常
		proxy.size();  //执行有返回值的方法,会报告空指针异常
        System.out.println(proxy.getClass()); //class $Proxy0
	}
}

1分析上面打印动态类的实例对象时,结果为什么会是null呢?

         答:打印动态类的示例对象实际上就是打印proxytoString方法,也就是执行代理对象中的如下代码:

         String toString(){
              return handler.invoke(this, this.getClass().getMethod(“toString”), null);
         }

         由于invoke方法返回的是null,打印出来的结果肯定是null

 

2调用有基本类型返回值的方法时为什么会出现NullPointerException异常?

         答:执行proxy.size()方法,就是执行下面的代码:

         int size(){
              return handler.invoke(this, this.getClass().getMethod(“size”), null);
         }

         由于invoke方法返回的是null,要将null转换为int类型,肯定会报告空指针异常。

 

3分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?

         答:调用代理对象的从Object类继承的hashCode, equals, toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,

     对于其他方法,则不转发调用请求,比如getClass方法,所以它会返回正确的结果


10  总结分析动态代理类的设计原理与结构

动态代理类的工作原理图:

《代理二:深入研究InvocationHandler、动态代理类工作原理、实现AOP框架》

简单总结:

      代理类和目标类都实现了同一个接口,目标类中有具体实现,

      而代理类中是通过InvocationHandler对像调用目标类中的具体实现:

      即,代理类的方法中,通过调用InvocationHandler对象的invoke方法,指向目标类的相应方法(具体实现)。

 

      当然,前面的例子中,是直接在InvocationHandler对象的invoke方法内,完成了具体实现,这样就没有目标类。

 

      注意:目标类必须自己定义时就实现接口,从该类的祖辈类上继承的接口是无效的。

 

将创建代理的过程改为一种更优雅的方式,Eclipse重构出一个getProxy方法绑定接收目标

同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API

将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?

把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,

接收者只要调用这个对象的方法,即等于执行了外界提供的代码!为方法增加一个Advice参数

代码示例:

package mypkg;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

interface Advice {
    void beforeAdvice(Method method);
    void afterAdvice(Method method);
}

//封装了切面代码:
class MyAdvice implements Advice {
    private long beginTime = 0;
    
    public void afterAdvice(Method method) {
        long endTime = System.currentTimeMillis();
        System.out.println(method.getName()+"running out of"+(endTime - beginTime));
    }
    public void beforeAdvice(Method method) {
        beginTime = System.currentTimeMillis();
    }
}

public class ProxyTest {
    public static void main(String[] args) throws Exception {
       final ArrayList target = new ArrayList(); //目标类
	   
	   //通过自定义的getProxy方法,生成代理类对象,并添加切面代码。
       Collection collection = (Collection) getProxy(target,new MyAdvice());
       collection.add("zxx" );
       //结果:add running out of 0
    }
	
	//实现框架功能,生成代理只需要传递target目标类,和封装了系统功能的对象MyAdvice
    public static Object getProxy(final ArrayList target,final Advice advice) {
		
		Object proxy = Proxy.newProxyInstance(   //生成代理类对象
		    target.getClass().getClassLoader(),
		    target.getClass().getInterfaces(),
		    new InvocationHandler() {
				public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				    advice.beforeAdvice(method); //切面代码
					Object retVal = method.invoke(target ,args);//调用目标类的方法
					advice.afterAdvice(method);  //切面代码
					return retVal;
			    }
		    }
		);
		return proxy;
    }
}

11  实现类似spring的可配置的AOP框架

AOP是指面向方面的编程,Aspect Oriented Program,简称AOP,见第2节。

实现一个AOP的框架,可以根据配置文件获取动态代理或其他结果。

 

工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。

getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象,即动态代理类实例。

该动态代理类中插入了切面代码。

 

BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:

xxx=mypkg.aop.ProxyFactoryBean

xxx.advice=mypkg.aop.MyAdvice

xxx.target=java.util.ArrayList

 

ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
目标类、封装了切面代码的对象(添加系统功能)。


编写客户端应用:
编写实现Advice接口的类和在配置文件中进行配置
调用BeanFactory获取对象

 

所有源文件及配置文件的包结构:

《代理二:深入研究InvocationHandler、动态代理类工作原理、实现AOP框架》

即在同一个包下。


AopFrameworkTest.java 文件:

/* AopFrameworkTest.java */
package mypkg.aop;
import java.io.InputStream;

//AOP框架,框架原理。通过修改配置文件中的内容,获取不同的结果
public class AopFrameworkTest {
	public static void main(String[] args) throws Exception{
		InputStream ips = 
			AopFrameworkTest.class.getResourceAsStream("config.properties");//相对路径,同一个包下
		
		Object bean = new BeanFactory(ips).getBean("xxx" ); //返回目标类或代理类的实例
		System. out.println(bean.getClass().getName());
    }
}

BeanFactory.java 文件:

/* BeanFactory.java */
package mypkg.aop;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

//BeanFactory类负责创建目标类或代理类的实例对象,并通过配置文件实现切换。
public class BeanFactory {
	Properties props = new Properties();
	
	public BeanFactory(InputStream ips){
		try{
			props.load(ips); //通过IO流加载配置文件的内容
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
	 
	public Object getBean(String name) {  //返回目标类或代理类的实例
		String className = props.getProperty(name);
		Object bean = null;
		try{
			Class clazz = Class.forName(className);//获取Class类
			bean = clazz.newInstance(); //创建此类的实例
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		if(bean instanceof ProxyFactoryBean){
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
			Object proxy = null;
			try{
				Advice advice = (Advice) Class.forName(
					props.getProperty(name + ".advice")).newInstance();
					
				Object target = Class. forName(
					props.getProperty(name + ".target")).newInstance();//目标类
					
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTarget(target);
				proxy = proxyFactoryBean.getProxy();//生成动态代理
			}
			catch (Exception e) {
				e.printStackTrace();
			}
			return proxy;
		}
		return bean;
	}
}

ProxyFactoryBean.java 文件:

/* ProxyFactoryBean.java */
package mypkg.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//动态代理类

/*ProxyFactoryBean类是一个生产动态代理类的工厂。
需要提供目标Target对象、Advice对象(封装了系统功能代码)。
此类是一个JavaBean。*/
public class ProxyFactoryBean {
	private Advice advice; //封装了系统功能的对象
	private Object target; //目标类
    
	public Advice getAdvice() {
		return advice ;
	}
	public void setAdvice(Advice advice) {
		this.advice = advice;
	}
	public Object getTarget() {
		return target ;
	}
	public void setTarget(Object target) {
		this.target = target;
	}
     
	public Object getProxy(){  //获取动态代理类的实例
		Object proxy = Proxy.newProxyInstance(
			target.getClass().getClassLoader(),
			target.getClass().getInterfaces(),
			new InvocationHandler() {
				public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
					advice.beforeAdvice(method); //切面代码
					Object retVal = method.invoke( target ,args); //调用目标类的方法
					advice.afterAdvice(method); //切面代码
					return retVal;
				}
		    }
	    );
	    return proxy;
	}
}

Advice.java 文件:

/* Advice.java */
package mypkg.aop;
import java.lang.reflect.Method;

public interface Advice {  //要添加的系统功能,即切面代码
    void beforeAdvice(Method method);
    void afterAdvice(Method method);
}

MyAdvice.java 文件:

/* MyAdvice.java */
package mypkg.aop;
import java.lang.reflect.Method;

public class MyAdvice implements Advice { //封装了切面代码:
    private long beginTime = 0;
    
    public void afterAdvice(Method method) {
        long endTime = System.currentTimeMillis();
        System.out.println(method.getName()+"running out of"+(endTime - beginTime));
    }
    public void beforeAdvice(Method method) {
        beginTime = System.currentTimeMillis();
    }
}

配置文件“config.properties”中的内容:

xxx=mypkg.aop.ProxyFactoryBean

xxx.advice=mypkg.aop.MyAdvice

xxx.target=java.util.ArrayList

 

编译运行结果:

《代理二:深入研究InvocationHandler、动态代理类工作原理、实现AOP框架》

    原文作者:AOP
    原文地址: https://blog.csdn.net/rock_ray/article/details/22491763
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞