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;
    }
}

 

posted @ 2025-02-09 11:33  地球上最后一个直男  阅读(93)  评论(0)    收藏  举报