反射 生成JDK动态代理和AOP

1 使用Proxy和InvocationHandler创建动态代理

      在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口。通过使用这个类和接口可以生成JDK动态代理类动态代理对象。

        Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用proxy来创建动态代理类;如果需要为一个或多个接口动态的创建实例,也可以使用proxy来创建动态代理实例
        static Class<?> getProxyClass (ClassLoader loader,Class<?>...interface):创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader参数指定生成动态代理类的类加载器。
         
        static Object  newProxyInstance(ClassLoader loader,Class<?>...interface,InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。
        实际上,即使采用第一个方法获取了一个动态代理类之后,当程序需要通过该代理类来创建对象时一样需要传入一个InvocationHandler对象。也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象
       
      当执行动态代理对象里的方法时,实际上会替换成调用InvocationHandler对象的invoke方法。
程序中可以采用先生成一个动态代理类,然后通过动态代理类来创建代理对象的方式生成一个动态代理对象。代码段如下:

//创建一个InvocationHandler 对象
InvocationHandler handler=new MyInvocationHandler(...);
//使用Proxy生成一个动态代理类proxyClass
Class proxyClass=proxy.getProxyClass(Foo.class.getClassLoader(),new Class[] {Foo.class});
//获取proxyClass类中带一个InvocationHandler参数的构造器
Constructor ctor=proxyClass.getConstructor(new Class[]{InvocationHandler.class});
//调用ctor的newInstance方法来创建动态实例
Foo f=(Foo)ctor.newInstance(new Object[] {handler});

上面代码也可以简化为如下代码:
//创建一个InvocationHandler对象
InvocationHandler handler=new MyInvocationHandler(...);
//使用Proxy直接生成一个动态代理对象
Foo f=(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),new Class[]{Foo,class},handler);

下面程序示范了使用Proxy和InvocationHandler来生成动态代理对象。
public interface Person
{
        void walk();
void sayHello(String name);
}

*********************************************************************************************************************************

class MyInvocationHandler implements InvocationHandler
{
    /*
      执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
 其中:
 proxy:代表动态代理对象
 method:代表正在执行的方法
 args:代表调用目标方法时传入的参数
    */
public Object invoke(Object proxy,Method method,Object[] args)
{
 System.out.println("---正在执行的方法:"+method);
 if(args !=null)
 {
    System.out.println("下面是执行该方法时传入的参数为:");
for(Object val : args)
{
   System.out.println(val);
}
 }
 else
 {
    System.out.println("调用该方法没有参数!");
 }
 return null;

}
}

*********************************************************************************************************************************

public class ProxyTest
{
   public static void main(String[] args)
         throws Exception
{
    //创建一个InvocationHandler 对象
MyInvocationHandler handler=new MyInvocationHandler();
//使用指定的InvocationHandler来生成一个动态代理对象
Person P=(Person)Proxy.newPtoxyInstance(Person.class.getClassLoader(),new Class[] {Person.class},handler);
//调用动态代理对象的walk()和sayHello()方法
p.walk();
p.sayHello("孙悟空");
}
}


    上面程序首先提供了一个Person接口,该接口中包含了walk()和sayHello()两个抽象方法,接着定义了一个简单的InvocationHandler实现类,定义该实现类时需要重写invoke()方法——调用代理对象的所有方法时都会被替换成调用该invoke()方法。该invoke()方法中的三个参数解释如下:
              proxy:代表动态代理对象;
              method:代表正在执行的方法;
              args:代表调用目标方法时传入的实参。

      运行结果如图:






如图18.5,代码块1,代码块2,代码块3既可以执行深色代码部分,又无须在程序中以硬编码方式直接调用深色代码的方法,就是通过动态代理来达到这种效果。

      由于JDK动态代理只能为接口创建动态代理,所以下面提供一个Dog接口,只定义两个方法:


public interface Dog
{
    //info 方法声明
void info();
//run方法声明
void run();
}

*********************************************************************************************************************************

上面接口里只是简单的定义了两个方法,并未提供方法实现。如果我们直接使用Proxy为该接口创建动态代理对象,则动态代理对象的所有方法的执行效果完全一样。在这种情况下,我们先为该Dog接口提供一个简单的实现类:GunDog

*********************************************************************************************************************************

public class GunDog implements Dog
{
    //info 方法实现,仅仅打印一个字符串
    public  void info()
{
 System.out.println("我是一只猎狗");
}
//run 方法实现,仅仅打印一个字符串
public void run()
{
       System.out.println("我奔跑迅速");
}
}

*********************************************************************************************************************************

我们需要实现的功能:让代码段1,代码段2,代码段3即可以执行深色代码部分,又无须在程序中以编译码方式直接调用深色代码的方法。此时我们假设info()方法和run()两方法代表代码段1,代码段2,那么要求:程序执行info(),run()方法时能调用某个通用方法,但又不想以硬编码方式调用该方法。下面提供一个DogUtil类,该类里包含两个通用方法。

*********************************************************************************************************************************

public classDogUtil
{
    //第一个拦截器方法
public void method1()
{
  System.out.println("=====模拟第一个通用方法");
  
}
//第二个拦截器
public void method2()
{
  System.out.println("=====模拟第二个通用方法");
  
}
}

*********************************************************************************************************************************

    借助proxy和InvocationHandler就可以实现-——当程序调用info()方法和run()方法时,系统可以“自动”将method1()和method2()两个通用方法插入info()和run()方法中执行。
    这个程序的关键在于下面的MyInvocationHandler类,该类是一个InvocationHandler实现类,该实现类的invoke()方法将会作为代理对象的实现方法。

*********************************************************************************************************************************

public classMyInvocationHandlerimplements InvocationHandler
{
     //需要被代理对象
private Object target;
public void setTarget(Object target)
{
    this.target=target;
}
//执行动态代理的所有方法时,都会被替换成执行如下的invoke方法
public Object invoke(Object proxy,Method method,Object[] args)
throws Exception
{
DogUtildu=new DogUtil();
 //执行DogUtil对象中的method1方法
 du.method1();
 //以target作为主调来执行method方法
Object result=method.invoke(target,args);
//执行DogUtil对象中的method2方法
du.method2();
return result;
}
}

*********************************************************************************************************************************

   上面程序实现invoke()方法时包含了一行关键代码(以粗体字标出),这行代码通过反射以target作为主调来执行method方法,这就是回调了target对象的原有方法。在粗体字代码之前调用DogUtil对象的method1()方法,在粗体字代码后调用DogUtil对象method2()方法。
   下面在为程序提供一个MyProxyFactory类,该对象专为指定的target生成动态代理实例

*********************************************************************************************************************************
public classMyProxyFactory
{
   //为指定的target生成动态代理对象
   public  static Object getProxy(Object target)
   {
    //创建一个MyInvokationHandler对象
MyInvocationHandlerhandler=newMyInvocationHandler();
//为MyInvocationHandler设置target对象
handler.setTarget(target);
//创建并返回一个动态代理
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
   }  
}

*********************************************************************************************************************************

    上面的动态代理工厂类提供了一个getProxy()方法,该方法为target对象生成一个动态代理对象,这个动态代理对象与target实现了相同的接口,所以具有相同的public方法——从这个意义上来看,动态代理对象可以当成target对象使用。当程序调用动态代理对象的制定方法时,实际上将变为执行MyInvokationHandler对象的invoke()方法。例如:调用动态代理对象的Info()方法,程序将开始执行invoke()方法,其执行步骤如下:
(1)创建DogUtil实例。
(2)执行DogUtil实例的method1()方法。
(3)使用反射以target作为调用者执行info()方法。
(4)执行DogUtil实例的method2()方法。
     当使用动态代理对象来代替target对象时,代理对象的方法就实现了前面的要求——程序执行info(),run()方法时既能插入method1(),method2()通用方法,但GunDog的方法中有没有以硬编码方式调用method1()和method2()方法。
     下面是测试程序

*********************************************************************************************************************************

public class Test
{
    public  static void main(String[] args)
{
  //创建一个原始的GunDog对象,作为target
  Dog target=new GunDog();
  //以指定的target来创建动态代理对象
  Dog dog=(Dog)MyProxyFactory.getProxy(target);
  dog.info();
  dog.run();
  
}
}

*********************************************************************************************************************************

dog对象实际上是动态代理对象,只是该动态代理对象也实现了Dog接口,所以也可以当成Dog对象使用。程序执行dog的info()和run()方法时,实际上会执行DogUtil的method1()方法,再执行target对象的info()和run()方法,最后执行DogUtil的method2()方法。运行上面程序,会看到如图结果



这种代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前,之后插入一些通用处理。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值