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
编译运行结果: