您的位置:首页 > 运维架构

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

2014-03-29 14:43 447 查看

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呢?

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

         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对像调用目标类中的具体实现:
      即,代理类的方法中,通过调用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获取对象。

 

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



即在同一个包下。

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

 

编译运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐