java进阶(七):详解JAVA代理

来源:互联网 发布:淘宝违禁词查询工具 编辑:程序博客网 时间:2024/09/21 08:53

一、Proxy类和InvocationHandler接口

1、和动态代理有关的有两个类 :InvocationHandler 接口和Proxy类。

2、InvocationHandler :提供了一个方法,public Object invoke(Object proxy, Method method, Object[] args)。
3、Proxy:表示动态代理的类,提供两个静态方法: 

1)public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):用来产生代理类。

2)public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)产生代理对象,多了InvocationHandler参数(对的,就是InvocationHandler接口的实现类),它与代理对象关联,当请求分发到代理对象后,会自动执行InvocationHandler实现类的invoke(...)方法,invoke方法就是我们用来做N多事情的地方。


二、 Proxy的getProxyClass方法——以创建一个实现了Collection接口的动态代理类为例。

1、public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):用来产生代理类,实质上是在内存中生成一份代理类的字节码,所以返回值为Class。

2、两个参数分别为:

1)【参数1】ClassLoader loader:指定产生这份字节码的类加载器。我们知道每一份Class字节码,都有getClassLoader()方法得到加载它本身的类加载器,而动态代理的份字节码不能平白无故在内存中创建的,所以为他指定加载器,通常为实现接口的同个加载器。

2)【参数2】Class<?>... interfaces:指定字节码实现的接口。指内存中生成的这份字节码,实现的哪个接口,可以指定实现多个接口。

3、例如创建一个实现Collection接口的动态类:得到这个动态类的字节码。有了这份字节码后,我们就可以利用反射,查看这个动态代理类的各种信息,如类加载器、构造方法和方法等等。如下:

// 创建一个实现Collection接口的动态类// 第一个参数为:指定clazzProxy1的 类加载器为 Collection 接口的类加载器// 第二个参数为:指定clazzProxy1实现了 Collection 接口。Class<?> clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);// 为null,取不到这份字节码的类加载器,原因在于加载Collection的ClassLoader为BootstrapClassloader,// 是最基础的类加载器,我们是拿不到的,自能为空。System.out.println("代理产生的字节码的ClassLoader------>"+ clazzProxy1.getClassLoader());//输出为null// clazzProxy1.getName();System.out.println(clazzProxy1.getName());// 产生的代理类的名字:$Proxy0System.out.println("----------1、查看动态代理类的构造方法 constructors-------------");/* * 以这种列表的形式打印构造方法与其参数列表  * $Proxy0()  * $Proxy0(InvocationHandler,int) */Constructor<?>[] constructors = clazzProxy1.getConstructors();//利用反射,得到所有的构造函数for (Constructor<?> constructor : constructors) {String name = constructor.getName();System.out.println("构造方法的名字:  " + name);// 代理类的构造方法的名字// 拼参数列表StringBuilder tBuilder = new StringBuilder(name);tBuilder.append('(');Class<?>[] clazzParams = constructor.getParameterTypes();// 构造方法的参数for (Class<?> clazzParam : clazzParams) {System.out.println("构造方法参数的类型:  " + clazzParam.getName());// 构造方法的参数tBuilder.append(clazzParam.getName()).append(',');}if (clazzParams != null && clazzParams.length != 0) {// 有参数的时候tBuilder.deleteCharAt(tBuilder.length() - 1);// 去掉最后一个逗号}tBuilder.append(')');System.out.println(tBuilder);}System.out.println();System.out.println("----------2、动态代理类的方法以及参数-------------");/* * 以这种列表的形式打印方法与其参数列表 * add(Collection c)  * remove() */Method[] methods = clazzProxy1.getMethods();//利用反射得到所有的方法for (Method method : methods) {String name = method.getName();// System.out.println(name);//方法的名字StringBuilder tBuilder = new StringBuilder(name);tBuilder.append('(');Class<?>[] clazzParams = method.getParameterTypes();//得到方法的参数类型for (Class<?> clazzParam : clazzParams) {tBuilder.append(clazzParam.getName()).append(',');}if (clazzParams != null && clazzParams.length != 0) {tBuilder.deleteCharAt(tBuilder.length() - 1);}tBuilder.append(')');System.out.println(tBuilder);}

【输出结果】

代理产生的字节码的ClassLoader------>null$Proxy0----------1、查看动态代理类的构造方法 constructors-------------构造方法的名字:  $Proxy0构造方法参数的类型:  java.lang.reflect.InvocationHandler$Proxy0(java.lang.reflect.InvocationHandler)----------2、动态代理类的方法以及参数-------------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()

4、从输出的结果,我们可以看到:

1)这个动态代理类只有一个构造方法:$Proxy0(java.lang.reflect.InvocationHandler)

2)方法可以分为3类:

A、继承自Object的方法,如hashCode()、equals(java.lang.Object)、toString()等等;

B、实现自Collection接口,如:add(java.lang.Object)、clear()、isEmpty()、size()等等;

C、还具有Proxy的方法,如:getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)、newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)等等;


三、创建动态类的实例对象及其调用。

1、我们利用 Proxy的getProxyClass方法创建了一个实现了Collection接口的动态代理类,现在我们利用反射创建这个动态代理类的实例对象。

2、我们已经知道这个动态代理类只有一个有参的构造方法:$Proxy0(java.lang.reflect.InvocationHandler),其参数为一个实现了InvocationHandler接口的类。这样我们就可以利用反射的newInstance()方法去实例化一个对象。为了方法,这边使用内部类。如:

System.out.println("----------3、创建动态类的实例对象及其调用-------------");// 因为只有一个有参的构造方法,所以只能利用那个有参的构造方法// clazzProxy1.newInstance();//错误。只有一个构造方法,没有无参的构造方法// 得到有参构造方法Constructor<?> tConstructor = clazzProxy1.getConstructor(InvocationHandler.class);// 内部类class MyInvocationHandler1 implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {return null;<span style="color:#ff0000;">//注意:这边返回空</span>}}// 利用有参构造方法,初始化一个动态代理类的对象。传入一个MyInvocationHandler对象参数Collection proxy1 = (Collection) tConstructor.newInstance(new MyInvocationHandler1());

3、打印这个对象和调用对象的方法如下:这边会有几个问题,但是不必急着解决,等下面分析了代理类的代码后,自然就会解决。

1)为什么调用proxy1.toString()为null,而proxy1.getClass()却有值?

2)为什么调用没有返回值的clear()成功,而调用有返回值的size()却报错?

3)以上原因,为什么会和invoke()的返回值有关?

// 打印这个对象,为null// null有2中可能,1为对象本身为空,这样会报空指针,2为这个对象的toString()返回为空。这边为第二种。// 打印出来时null,是因为toString返回为null,因为invoke()返回为空创建成功System.out.println(proxy1);System.out.println(proxy1.getClass().getName());//$Proxy0// 对象的方法调用proxy1.clear();// 没有返回值,成功调用,说明对象不为空。// 有返回值的方法,调用不成功。报错:java.lang.NullPointerException.// 因为size()方法会去调用invoke()方法,然后返回null,size返回要的是int,现在是null。proxy1.size();//报错


4、利用匿名内部类,改进以上创建动态代理实例的代码,如下:

// 修改2:上面代码的修改:利用匿名内部类Constructor<?> tConstructor = clazzProxy1.getConstructor(InvocationHandler.class);Collection proxy2 = (Collection) tConstructor.newInstance(new InvocationHandler() {//匿名内部类@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {return null;}});


三、利用Proxy.newProxtinstance()方法实现动态代理类的实例化

1、Proxy.newProxtinstance()比getProxyClass()方法增加了一个参数:InvocationHandle接口的实现类。

2、其实回顾下我们刚刚利用getProxyClass方法,然后利用有参构造函数实例化对象的例子,会发现,用到的参数其实一共也是三个:

1)先是getProxyClass()的两个参数:类加载器,实现的接口。

2)然后利用有参构造函数,传入实现了InvocationHandle接口的类。

3、这样,其实你或许会想到,对的。Proxy.newProxtinstance()内部也是调用了getProxyClass()方法,然后死有参构造函数实例化。如下为Proxy.newProxtinstance()方法的源码代码:

    public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)throws IllegalArgumentException    {if (h == null) {    throw new NullPointerException();}/* * Look up or generate the designated proxy class. */Class cl = getProxyClass(loader, interfaces);//调用getProxyClass()方法/* * Invoke its constructor with the designated invocation handler. */try {    Constructor cons = cl.getConstructor(constructorParams);//有参构造函数实例化    return (Object) cons.newInstance(new Object[] { h });} catch (NoSuchMethodException e) {    throw new InternalError(e.toString());} catch (IllegalAccessException e) {    throw new InternalError(e.toString());} catch (InstantiationException e) {    throw new InternalError(e.toString());} catch (InvocationTargetException e) {    throw new InternalError(e.toString());}    }


4、直接调用Proxy.newProxyInstance()方法实例化动态代理类的对象:

1)、利用匿名内部类,传入实现InvocationHandler接口的对象,重写invoke()方法。

invoke有三个参数,public Object invoke(Object proxy, Method method,Object[] args):分别为动态产生的代理对象、调用的方法、方法的参数。

2)invoke()方法内部,去调用这个Method的invoke()方法,传入被代理对象与参数。

Method方法的反射,在调用时,即invoke()调用时,需要传入具体的对象和参数。传入什么对象即调用哪个对象的这个方法。例如,调用String的charAt()方法:

//调用String的charAt()方法---->bMethod methodCharAt = String.class.getMethod("charAt", int.class);//调用静态方法,对象设为null即可//System.out.println(methodCharAt.invoke(null, 1));//bmethodCharAt.invoke(str1, 1);//  get/invoke方法必须用在具体的对象身上System.out.println(methodCharAt.invoke(str1, 1));//b

3)准备一个目标对象,ArrayList<String> target = new ArrayList<String>();交给Method,invoke()调用。代理对象proxy3和被代理对象需要实现相同的接口。

// 修改3:一步到位,利用Proxy.newProxtinstance()方法// 三个参数:类加载器,实现的接口,InvocationHandle接口的实现类// proxy3为动态生成的代理对象,target为被代理对象。这样就实现了代理对象与被代理对象实现了同样的接口CollectionCollection proxy3 = (Collection) Proxy.newProxyInstance(//Collection.class.getClassLoader(), // 指定类加载器new Class[] { Collection.class }, // 数组,表示可以实现多个接口new InvocationHandler() {// 实现InvocationHandler接口的实例对象ArrayList<String> target = new ArrayList<String>();//被代理对象,不要放在invoke方法里面@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {System.out.println("++++++++++++++++++++++++++被代理对象的方法被调用之前++++++++++++++++++++++++++");System.out.println("动态代理对象-----"+proxy.getClass().getName());System.out.println("调用的方法-------"+method.getName());if(args!=null){System.out.println("方法的参数------" + Arrays.asList(args));}else{System.out.println("参数为空,即无参的方法");}//修改参数//args = null;Object object = method.invoke(target, args);//真正调用被代理对象的具体方法System.out.println("===========================被代理对象的方法被调用之后============================");//修改返回结果//object=null;return object;//return method.invoke(proxy, args);//死循环,不停的调用代理对象的方法}});//proxy3.clear();//System.out.println(proxy3.size());proxy3.add("111111111");//调用add,就会去调用invoke()方法。proxy3.add("222222222");proxy3.add("333333333");proxy3.add("444444444");System.out.println("具体的被代理对象一共加了"+proxy3.size());
【输出】

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>一步到位,利用Proxy.newProxtinstance()方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<++++++++++++++++++++++++++被代理对象的方法被调用之前++++++++++++++++++++++++++动态代理对象-----$Proxy0调用的方法-------add方法的参数------[111111111]===========================被代理对象的方法被调用之后============================++++++++++++++++++++++++++被代理对象的方法被调用之前++++++++++++++++++++++++++动态代理对象-----$Proxy0调用的方法-------add方法的参数------[222222222]===========================被代理对象的方法被调用之后============================++++++++++++++++++++++++++被代理对象的方法被调用之前++++++++++++++++++++++++++动态代理对象-----$Proxy0调用的方法-------add方法的参数------[333333333]===========================被代理对象的方法被调用之后============================++++++++++++++++++++++++++被代理对象的方法被调用之前++++++++++++++++++++++++++动态代理对象-----$Proxy0调用的方法-------add方法的参数------[444444444]===========================被代理对象的方法被调用之后============================++++++++++++++++++++++++++被代理对象的方法被调用之前++++++++++++++++++++++++++动态代理对象-----$Proxy0调用的方法-------size参数为空,即无参的方法===========================被代理对象的方法被调用之后============================具体的被代理对象一共加了4


5、通过输出地内容可以确定:

1)这个代理对象在调用add()方法的时候,会去调用实现InvocationHandler接口的实例对象的invoke()方法,然后通过传入的Method方法,利用反射区调用目标对象的具体方法。

2)这样,我们大概也可以猜测出这个动态代理类的代码是这样的。

/* * 猜想分析动态生成的类proxy3的内部代码 * 1- 动态生成的类实现了Colletion接口,生成的类有Collection接口中的所有方法和 * 一个如下接收InvocationHandler参数的构造方法。 * 2、构造方法接收一个InvocationHandler对象: *  * InvocationHandler handler; *  public $Proxy0(InvocationHandler handler){ *  this. handler =  handler; *  } *   *  3、实现的Collection接口的动态类中的各个方法的代码是怎样的? * InvocationHandler接口中定义的invoke()方法的三个参数又是什么意思? *  * int size(){ * return handle.invoke(this, this.getClass().getMethod("size"), null); * } *  * void clear(){ * handle.invoke(this, this.getClass().getMethod("clear"), null); * } *  *  * void add(Object obj){ * handle.invoke(this, this.getClass().getMethod("add"), obj);//封装成数组 * } */


3)通过反编译工具,我们可以真实的获取动态代理的代码:

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements IProxy {private static Method m0;private static Method m1;private static Method m2;private static Method m3;static {   try {   //通过反射机制获取Object中的hashCode(), equals()和toString()方法   m0 = Class.forName("java.lang.Object").getMethod("hashCode",          new Class[0]);    m1 = Class.forName("java.lang.Object").getMethod("equals",          new Class[] { Class.forName("java.lang.Object") });    m2 = Class.forName("java.lang.Object").getMethod("toString",          new Class[0]);   //获取代理接口Proxy的代理方法hello      m3 = Class.forName("com.proxy.test.IProxy").getMethod("hello",            new Class[0]);      } catch (NoSuchMethodException nosuchmethodexception) {       throw new NoSuchMethodError(nosuchmethodexception.getMessage());   } catch (ClassNotFoundException classnotfoundexception) {       throw new NoClassDefFoundError(classnotfoundexception.getMessage());   }}//将实现了InvocationHandler接口的对象传递给父类Proxypublic $Proxy0(InvocationHandler invocationhandler) {   super(invocationhandler);}//代理hashCode()方法@Overridepublic final int hashCode() {   try {    return ((Integer) super.h.invoke(this, m0, null)).intValue();   } catch (Throwable throwable) {    throw new UndeclaredThrowableException(throwable);   }}//代理equals()方法@Overridepublic final boolean equals(Object obj) {   try {    return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))      .booleanValue();   } catch (Throwable throwable) {    throw new UndeclaredThrowableException(throwable);   }}//代理toString()方法@Overridepublic final String toString() {   try {    return (String) super.h.invoke(this, m2, null);   } catch (Throwable throwable) {    throw new UndeclaredThrowableException(throwable);   }}//代理接口中的代理方法public final void hello() {   try {    super.h.invoke(this, m3, null);    return;   } catch (Error e) {   } catch (Throwable throwable) {    throw new UndeclaredThrowableException(throwable);   }}}



3)这个过程可以用张图表示:


4)到这边我们就可以解释上面的一些问题了。

A、动态代理对象调用toString()方法的时候,会去调用invoke()方法,然后接收返回值,而我们上面的例子刚好返回一个null,所以即为null了。

B、那为什么getClass()方法有值呢?JAVA的API规定,继承自Object的方法,只有hashcode,equals,toString会去调用handle的invoke(),其他会自己实现,即getClass()方法不会去找invoke(),而是自己实现了。

C、代理对象调用clear()和size()方法,分别四调用handle的invoke()方法,然后拿到返回值,这边返回值刚好为空。这样,clear()不需要返回值,所以null不null无所谓,而size()需要一个整型的返回值,而你却返回了一个null,就报错了。


5)在去调用目标对象的前后,我们可以调用一些方法,或者修改参数等,如上面例子中打印的前后输出,从而实现了在调用目标对象的前后,实现切面编程。但是现在的这个切面是硬编码的,是写死在代码中的,我们试着动态实现它。


六、动态实现切面编程。

1、我们硬编码的其实只有两个类:一个是代理目标对象,一个就是实现切面功能的方法,但是方法不能传入,却可以传入对象,然后调用这个对象的切面方法。所以传入两个对象即可:代理目标对象与切面对象。

2、我们先写一个切面的接口,有两个方法,然后一个实现这个了这个接口的类。

package com.onhance.proxy;import java.lang.reflect.Method;/** *  * Advice.java * * @title 切面的接口,借用spring的命名,为Advice  * @description * @author SAM-SHO  * @Date 2014-9-21 */public interface Advice {//像spring一样,其实方法的参数可以为三个,这边简便处理void beforeMethod(Method method);//方法前调用void afterMethod(Method method);//方法后调用//异常的//方法前后}

package com.onhance.proxy;import java.lang.reflect.Method;/** *  * MyAdvice.java * * @title 切面的具体实现 * @description * @author SAM-SHO  * @Date 2014-9-21 */public class MyAdvice implements Advice {long beginTime = 0;public void afterMethod(Method method) {System.out.println("方法已经调用结束啦");long endTime = System.currentTimeMillis();System.out.println(method.getName() + " 方法一共运行的时间为 " + (endTime - beginTime));}public void beforeMethod(Method method) {System.out.println("在方法调用之前");beginTime = System.currentTimeMillis();}}

3、被代理的对象还是new一个ArrayList对象,ArrayList<String> target = new ArrayList<String>()。

这样在创建动态代理对象的时候,应该把这个ArrayList对象和MyAdvice对象当做参数传入,代码如下:因为使用了方法里面的内部类,而这个内部类想要访问局部变量,只能是final类型的。

System.out.println("*************************改进硬编码后**********************************************");/* * 改进硬编码 * 1、目标对象不能硬编码,传入 * 2、切面对象的传入 */final ArrayList<String> target = new ArrayList<String>();//被代理对象,抽出final MyAdvice myAdvice = new MyAdvice();//切面对象Collection proxy = (Collection) getProxy(target,myAdvice);proxy.add("shaoxiaobao");
/** *  * @param target 被代理的对象 * @param advice 切面对象 * @return */private static Object getProxy(final Object target,final Advice advice ) {Object proxy =  Proxy.newProxyInstance(////Collection.class.getClassLoader(), // 指定类加载器target.getClass().getClassLoader(),//利用反射得到类加载器//new Class[] { Collection.class }, // 数组,表示可以实现多个接口target.getClass().getInterfaces(),//利用反射得到实现的接口new InvocationHandler() {// 实现InvocationHandler接口的实例对象@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {advice.beforeMethod(method);//调用切面的方法Object object = method.invoke(target, args);//方法中内部类,要访问局部变量,只能是finaladvice.afterMethod(method);<span style="font-family: Helvetica, Tahoma, Arial, sans-serif;">//调用切面的方法</span>return object;}});return proxy;}
【输出】

*************************改进硬编码后**********************************************在方法调用之前方法已经调用结束啦add 方法一共运行的时间为 0

七、一般动态代理的设计是这样的。

package proxy.itcast;import java.lang.reflect.Method;/** *  * Advice.java * * @title 切面接口 * @description * @author SAM-SHO  * @Date 2014-9-22 */public interface Advice {/** * 方法之前调用 * @param method */public void beforeMethod(Method method);/** * 方法之后调用 * @param method */public void afterMethod(Method method);}
package proxy.itcast;import java.lang.reflect.Method;/** *  * MyAdvice.java * * @title 切面实现类 * @description * @author SAM-SHO  * @Date 2014-9-22 */public class MyAdvice implements Advice {@Overridepublic void afterMethod(Method method) {System.out.println("方法调用之后");}@Overridepublic void beforeMethod(Method method) {System.out.println("在方法调用之前");}}
package proxy.itcast;/** *  * Target.java * * @title 被代理对象接口 * @description * @author SAM-SHO  * @Date 2014-9-22 */public interface Target {public void first();}
package proxy.itcast;/** *  * MyTarget.java * * @title 被代理对象实现类 * @description * @author SAM-SHO  * @Date 2014-9-22 */public class MyTarget implements Target {@Overridepublic void first() {System.out.println("方法-first 被调用了");}}
package proxy.itcast;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** *  * MyInvocation.java * * @title InvocationHandler实现类 * @description * @author SAM-SHO  * @Date 2014-9-22 */public class MyInvocation implements  InvocationHandler{private Advice advice;//切面对象private Object target;//被代理对象,即目标属性@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {advice.beforeMethod(method);Object obj = method.invoke(target, args);advice.afterMethod(method);return obj;}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;}}
package proxy.itcast;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** *  * ProxyUtil.java * * @title 获取代理对象的工具类 * @description * @author SAM-SHO  * @Date 2014-9-22 */public class ProxyUtil {static{System.out.println("在什么时候被调用");}/** * 得到代理对象 * @param targrt * @param advice * @param handle * @return */public static Object getProxy(Object targrt, Advice advice, InvocationHandler handle){Object proxy = Proxy.newProxyInstance(targrt.getClass().getClassLoader(), //targrt.getClass().getInterfaces(), handle);return proxy;}/** * 得到代理对象 * @param targrt * @param advice * @return */public static Object getProxy(final Object targrt, final Advice advice){//这边2个参数也可以作为全局变量Object proxy = Proxy.newProxyInstance(targrt.getClass().getClassLoader(), //targrt.getClass().getInterfaces(), new InvocationHandler(){//方法体重的内部类,访问局部变量,只能是final@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {advice.beforeMethod(method);Object object = method.invoke(targrt, args);advice.afterMethod(method);return object;}});return proxy;}}
package proxy.itcast;import java.lang.reflect.Proxy;/** *  * ProxyTest.java * * @title 测试 * @description * @author SAM-SHO  * @Date 2014-9-22 */public class ProxyTest {public static void main(String[] args) {//MyInvocation中需要被传入两个对象//目标代理对象与切面对象Target myTarget = new MyTarget();Advice myAdvice = new MyAdvice();MyInvocation tMyInvocation= new MyInvocation();tMyInvocation.setAdvice(myAdvice);tMyInvocation.setTarget(myTarget);//得到代理对象Target proxy = (Target)Proxy.newProxyInstance(myTarget.getClass().getClassLoader(), myTarget.getClass().getInterfaces() , tMyInvocation);proxy.first();// 把得到代理对象封装起来Target proxy2 = (Target)ProxyUtil.getProxy(myTarget, myAdvice, tMyInvocation);proxy2.first();Target proxy3 = (Target)ProxyUtil.getProxy(myTarget, myAdvice);proxy3.first();}}
【输出】

在方法调用之前方法-first 被调用了方法调用之后------------------------------在什么时候被调用,初始化一次在方法调用之前方法-first 被调用了方法调用之后++++++++++++++++++++++++++++++++++在方法调用之前方法-first 被调用了方法调用之后


八、利用动态代理,实现类似Spring的AOP功能。

实质:

1、把封装好的获取代理工具类、被代理对象、切面类放入配置文件,利用一个配置文件读取类,去读取配置好的信息。

2、当从配置文件加载的是获取代理工具类的时候,先初始化自己,然后去配置文件获取被代理对象和切面类,并利用反射(javaBean有默认的无参构造方法)初始化,传入代理工具类。

3、以上就完成了代理对象的初始化,可以直接使用了。


#配置文件#代理工具Beanxxx=com.onhance.proxy.aopframework.ProxyFactoryBean#切面xxx.advice=com.onhance.proxy.MyAdvice#被代理对象xxx.target=java.util.ArrayList

package com.onhance.proxy.aopframework;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import com.onhance.proxy.Advice;/** *  * ProxyFactoryBean.java *  * @title 获取代理类的封装,Spring中是一个接口 *  * @description * @author SAM-SHO * @Date 2014-9-22 */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;}/** * 获取代理 * @return */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.beforeMethod(method);Object retVal = method.invoke(target, args);advice.afterMethod(method);return retVal;}});return proxy;}}
package com.onhance.proxy.aopframework;import java.io.IOException;import java.io.InputStream;import java.util.Properties;import com.onhance.proxy.Advice;/** *  * BeanFactory.java * * @title Bean工厂,从配置文件获取这种Bean * @description * @author SAM-SHO  * @Date 2014-9-22 */public class BeanFactory {Properties props = new Properties();//构造初始化的时候,获取配置文件public BeanFactory(InputStream ips){try {props.load(ips);} catch (IOException e) {e.printStackTrace();}}/** * 获取具体的Bean * @param name * @return */public Object getBean(String name){String className = props.getProperty(name);Object bean = null;try {Class clazz = Class.forName(className);//反射初始化bean = clazz.newInstance();} catch (Exception e) {e.printStackTrace();} /* * 检查Bean对象,如果是ProxyFactoryBean * 那么去获取切面类advice和被代理类target */if(bean instanceof ProxyFactoryBean){Object proxy = null;ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;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;}}

package com.onhance.proxy.aopframework;import java.io.InputStream;import java.util.Collection;/** *  * AopFrameworkTest.java * * @title 测试 * @description * @author SAM-SHO  * @Date 2014-9-22 */public class AopFrameworkTest {/** * @param args */public static void main(String[] args) throws Exception {// 获取配置文件InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");//利用Bean工厂获取配置文件的BeanObject bean = new BeanFactory(ips).getBean("xxx");System.out.println(bean.getClass().getName());//得到代理((Collection)bean).clear();}}

【输出】

$Proxy0在方法调用之前方法已经调用结束啦clear 方法一共运行的时间为 0

































0 0
原创粉丝点击