spring之动态代理
动态代理使用反射的机制来为自定义类自动生成代理,废话不多说,上代码
首先是被代理的接口Rent
package com.loubin.pojo; public interface Rent { public void rent(); }
然后是被代理类Host,也叫做目标类,其实现了Rent接口
package com.loubin.pojo; public class Host implements Rent{ @Override public void rent() { System.out.println("房东要租房了"); } }
既然要代理,那肯定除了目标类的rent方法实现的功能外,还需要为其加上额外的其他功能,为rent类增加额外功能的类这里我们称为事务类,例如下面的代码的MyTransaction类
里面定义了before和after方法,待会我们使用代理类代理了Host后,需要为代理类的rent方法加上before和after的功能
package com.loubin.transaction; public class MyTransaction { public void before(){ System.out.println("处理前置事务"); } public void after(){ System.out.println("处理后置事务"); } }
主菜来了哦,下面是测试代码,直接在测试代码中对Host进行动态代码,不用在编写代理类了
package com.loubin; import com.loubin.pojo.Host; import com.loubin.pojo.Rent; import com.loubin.transaction.MyTransaction; import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { MyTransaction transaction = new MyTransaction(); Rent host = new Host(); MyInvocationHandler invocationHandler = new MyInvocationHandler(host, transaction); Rent proxy = (Rent) Proxy.newProxyInstance(Host.class.getClassLoader(), host.getClass().getInterfaces(), invocationHandler); proxy.rent(); System.out.println("Hello world!"); } }
测试代码首先是定义了两个对象,对象host输入Host类,是需要被代理的目标类,而transaction对象属于MyTranction类,是要为rent方法加上事务的事务类。
接下来将transction和host对象作为参数输入到MyInvocationHandler的构造函数中,生成MyInvocationHandler类的对象invocationHandler。
ops,忘了介绍invocationHandler了,如下代码所示,其实啊,这个MyInvocationHandler就是InvocationHandler的子类,里面只有一个invoke函数需要我们关注。
来来来,联系上面的测试代码一起看,上面测试代码有一个Rent接口的proxy对象,这个proxy对象其实就是host的代理类,对没错,他就是我们需要的,自动生成的动态代理类。那么
他干了什么事儿呢?
首先我们需要关注一下(Rent) Proxy.newProxyInstance(Host.class.getClassLoader(), host.getClass().getInterfaces(),invocationHandler);这句话,这里其实就是创建代理类的语句。第一参数不用管,反正就是系统加载器而已啦,第二个参数,传入的其实就是Rent接口,为什么传这个呢。上面说了,proxy是代理类,所以当然要有Rent接口中的rent函数啊,所以就需要实现Rent接口。第三个参数是我定义的invocationHandler对象,也就是下面这段代码定义的类的对象,invocationHandler你可以认为就是proxy对象的一个属性而已。当我们在测试类中调用proxy.rent的时候,
proxy的rent函数内部并不会输出“房东要租房”,而是执行h.invoke(),是的,这里的h就是我传入的invocationHandler,也就是这里的第三个参数。
那么聪明的小明就会问了,h.invoke怎么知道该执行哪个具体实例对象的rent函数呢?甚至说h.invoke怎么知道是要执行rent函数呢?我们先一个一个问题来看:
h.invoke怎么知道是要执行rent函数呢?
当proxy对象调用h.invoke()时,会给invoke函数传入三个参数,分别是被代理的接口proxy,rent方法对应的Methon对象method,以及rent方法需要的参数args,这些信息proxy是拥有的,当然可以传给h.invoke了。不过这里的method需要使用反射来获得。
h.invoke怎么知道该执行哪个具体实例对象的rent函数呢?
这个问题就更简单了啊,h使我们创建proxy对象之前自己定义的,然后才传给proxy的。那么我们自然可以在定义h,也就是invocationHandler的时候,告诉它我们应该代理哪个具体的对象,需要做哪些额外工作啊。
所以你就看到了下面的定义MyInvocationHandler类的代码了。该代码首先定义了Object的对象o,表示被代理的目标类,也就是这个例子里面的host对象;myTransaction表示需要增加额外功能的事务类。这两个对象都是在构造函数类注入的,当然你可以用set注入。
再梳理一下执行流程:
proxy对象是动态代理,当调用proxy的rent方法时,proxy调用我们实现定义好的invocationHandler的invoke函数,在proxy的rent方法中其实就是执行h.invoke函数,并且传入Method对象让invocationHandler的invoke函数知道要调用哪个接口的哪个方法,这里的Method对象其实就是rent方法的反射(不过注意,不是host的rent方法,是Rent的rent方法)。invocationHandler获得传入的参数后,首先执行事务类的方法,添加额外功能,然后使用method.invoke(o)来执行host的rent方法(这里的o就是host对象,使我们在定义invocationHandler的时候传入的)。
如果下一次需要代理其他类了,只需要在定义invocationHandler的时候,将传入的host对象改成其他对象,定义proxy的时候,将代理目标接口Rent改成其他接口,就可以了。
其实弄懂还是挺简单的,无非就是动态代理对象proxy调用rent函数后,触发h.invoke,这里的h就是自定义的invocationHandler。然后h.invoke通过自定义实现了对目标类host的rent方法的调用。这里如何让h.invoke知道是该嗲用rent方法呢。proxy对象中使用反射机制获取了rent方法的Method对象,并且传入给了h属性。so easy
package com.loubin; import com.loubin.transaction.MyTransaction; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { Object o; MyTransaction myTransaction; public MyInvocationHandler(Object o, MyTransaction myTransaction) { this.o = o; this.myTransaction = myTransaction; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("rent".equals(method.getName())){ myTransaction.before(); method.invoke(o); myTransaction.after(); } return null; } }

浙公网安备 33010602011771号