java动态代理

1.静态代理

  如果我们有如下接口及具体实现:

public interface StudentService {
    void setScore(long id, int score);

    void awardStudent(long id);
}

public class StudentServiceImpl implements StudentService {
    public void setScore(long id, int score){
        System.out.println("设置分数,学号:" + id + ",得分:" + score);
        return;
    }

    public void awardStudent(long id) {
        System.out.println("表扬学生,学号:" + id);
        return;
    }
}

假如我们希望在调用StudentServiceImpl类的方法前后加上日志,以记录对方法的调用,而又不想或者不能修改StudentServiceImpl这个类(比如StudentServiceImpl这个类是别人提供的,我们无法修改),那我们可以写个代理类包装一下,如下:

public class StudentServiceProxy implements StudentService {

    private StudentService studentService;

    public StudentServiceProxy(StudentService studentService) {
        this.studentService = studentService;
    }

    public void setScore(long id, int score) {
        System.out.println("-----方法开始执行------");
        studentService.setScore(id, score);
        System.out.println("-----方法执行完毕------");
    }

    public void awardStudent(long id) {
        System.out.println("-----方法开始执行------");
        studentService.awardStudent(id);
        System.out.println("-----方法执行完毕------");
    }
}
代理类StudentServiceProxy通过实现被代理的StudentService接口,实现了代理StudentServiceImpl类的功能。代理类StudentServiceProxy在调用被代理对象的相应方法前后都加上了日志。我们这么使用它:

public class Main {

    public static void main(String[] args) {
        StudentService target = new StudentServiceImpl();  // 被代理的对象
        StudentService proxy = new StudentServiceProxy(target);
        proxy.setScore(1, 100);
        proxy.awardStudent(1);
    }
}
输出为:

-----方法开始执行------
设置分数,学号:1,得分:100
-----方法执行完毕------
-----方法开始执行------
表扬学生,学号:1
-----方法执行完毕------
这里,在StudentServiceProxy代理类中,我们在调用被代理对象的方法前后,都加了记录日志的代码。因为代理类StudentServiceProxy实现了要代理的StudentService接口,因此,我们可以像使用StudentServiceImpl对象一样,使用代理类对象proxy。这里,被代理的类实现了某个接口,我们通过编写一个实现了该接口的类的方法为其创建代理类。如果某个类没有接口,怎么为其创建代理类呢?比如有如下类:

public class MsgSender {
    public boolean sendWarnMsg(String msg) {
        System.out.println("发送警告消息息:" + msg);
        return true;
    }

    public boolean sendCriticalMsg(String msg) {
        System.out.println("发送关键消息:" + msg);
        return true;
    }
}
我们也希望在调用它们的方法前后都加上日志,那我们可以通过为其创建子类的方法来为其创建代理类,在子类方法中加上记录日志的代码:

public class MsgSenderProxy extends MsgSender {
    private MsgSender msgSender;

    public MsgSenderProxy(MsgSender msgSender) {
        this.msgSender = msgSender;
    }

    public boolean sendWarnMsg(String msg) {
        System.out.println("-----方法开始执行------");
        boolean ret = msgSender.sendWarnMsg(msg);
        System.out.println("-----方法执行完毕------");
        return ret;
    }

    public boolean sendCriticalMsg(String msg) {
        System.out.println("-----方法开始执行------");
        boolean ret = msgSender.sendCriticalMsg(msg);
        System.out.println("-----方法执行完毕------");
        return ret;
    }
}
使用方法;

public class Main {

    public static void main(String[] args) {

        MsgSender target = new MsgSender();
        MsgSender msgSenderProxy = new MsgSenderProxy(target);
        boolean ret = msgSenderProxy.sendWarnMsg("容量警告");
        System.out.println("发送结果:" + ret);
        ret = msgSenderProxy.sendCriticalMsg("服务不可用");
        System.out.println("发送结果:" + ret);
    }
}

输出为:

-----方法开始执行------
发送警告消息息:容量警告
-----方法执行完毕------
发送结果:true
-----方法开始执行------
发送关键消息:服务不可用
-----方法执行完毕------
发送结果:true
因为代理类MsgSenderProxy继承了被代理类MsgSender,因此我们可以像使用MsgSender类一样来使用MsgSenderProxy对象。MsgSenderProxy在子类方法里增加了日志的功能,然后再调用被代理对象的实际方法。


可以看出,不论是通过实现被代理的接口、还是通过继承被代理类来创建代理类,代理类对象中都持有一个被代理类的实例对象,在代理类的方法中实现增加的功能,再调用被代理类实例对象的相应方法。并且我们可以像使用被代理类的对象一样使用代理类对象,而不会感觉有什么不同。


上面的例子中,我们想要增加的功能很简单,就是在方法调用前后记录日志,理想状态下记录日志的代码我们编写一份即可,但这里我们却写了4份,即每个要代理的类、类中每个然方法都要各写一份,这些都是重复性的代码。如果被代理类的方法更多,我们还需要编写更多的重复性代码。另外,如果还有许多其他的类的方法也需要在调用前后记录日志,那无疑我们需要为每个类都编写一个代理类,在代理类中为类的每个方法都加上日志,这个工作量是很可怕的。有没有更好的办法,记录日志的代码我们只需要编写一次,然后就可以为所有的类、为类的所有方法在调用时都加上日志呢?也就是说,有没有一种通用的方法,我们只需要把要增加的功能编写一次,然后就可以自动为每个类都生成代理类对象,并且被代理类的每个方法都加上了这个功能(比如上例中的记录日志的功能)呢?

理想的解决方案就是,该方案只需我们把要增加的功能(比如上例中的记录日志功能)写到一个类里(这样我们就只需要为该功能编写一次代码,而不用为每个代理类都写重复的代码了),然后该方案还提供一个智能创建者,当我们希望为某个类创建代理类对象时,我们只需把我们要代理的类要代理的接口实现了要增加的功能的类对象被代理对象传给这个智能创建者,它就可以自动为我们创建一个代理类,然后生成并返回这个代理类的一个实例对象。这个自动生成的代理类必须具备如下两个特点:

** 要么实现了被代理的接口,要么是被代理类的子类,这样我们就可以像使用被代理对象自身一样使用这个自动生成的代理类对象,丝毫感觉不出来有什么差别;

** 代理类实现了所有的被代理方法,为被代理的每个方法都加上了这个要增加的功能。


显然,Java提供了这种理想的解决方案。一种是jdk的动态代理,一种是cglib的动态代理。jdk是通过实现被代理的接口来为目标类来为其创建代理类的,而cglib则是通过继承目标类来为其创建代理类的。如果某个类没有接口,那么只能通过cglib的方试来创建动态代理。

2.jdk动态代理

jdk的动态代理方案中,Java提供了java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类。我们要为某批类增加一个功能时,我们首先需要创建一个类,这个类需要实现java.lang.reflect.InvocationHandler接口,该接口只有一个方法:

Object invoke(Object object, Method method, Object[] args) throws Throwable;

在jdk动态代理创建出来的代理类中,被代理的接口的每个方法被调用时,都会转而调用这个invoke方法,method参数就表明是被代理接口中的哪个方法被调用的,args参数则是调用那个方法时传来的参数。在invoke方法中我们把要增加的功能写上,然后调用被代理类对象的实际方法。


创建完实现了java.lang.reflect.InvocationHandler接口的类后,我们就可以调用java.lang.reflect.Proxy类的newProxyInstance方法来创建代理类对象了,它的原型是:

Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) 
throws IllegalArgumentException;
参数loader就是被代理类的ClassLoader,因为你要让它帮你创建一个代理类对象,它当然得知道被代理类的信息了,给了它ClassLoader,它就知道上哪加载被代理类。interfaces则是希望为哪些接口创建代理。而参数h则是我们为要增加的功能编写的类的实例对象。 newProxyInstance方法会创建一个 临时类,然后生成并返回该临时类的一个实例对象。临时类实现了被代理的接口,并且接口的每个方法都是直接调用 InvocationHandler 对象的invoke方法。


下面看看,怎么用jdk动态代理的方法来为StudentServiceImpl类创建代理类对象:

第一步,为要增加的功能写一个类,该类实现java.lang.reflect.InvocationHandler接口的invoke函数,在invoke函数中实现要增加的功能,并调用被代理对象的实际方法。

我们要增加的功能是在方法执行前后记录日志,为此我们把该类命名为LogHandler:

public class LogHandler implements InvocationHandler {
    private Object proxiedObject;

    public LogHandler(Object proxiedObject) {
        this.proxiedObject = proxiedObject;
    }

    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        System.out.println("-----方法开始执行------");
        Object ret = method.invoke(proxiedObject, args);
        System.out.println("-----方法执行完毕------");
        return ret;
    }
}
LogHandler里持有一个被代理类的对象实例。在它的invoke方法中,我们加上了记录日志的代码,然后调用被代理类对象实例的相应方法,并将执行的结果返回。

第二步,调用Proxy类的newProxyInstance方法生成代理类对象:

public class Main {

    public static void main(String[] args) {

        StudentService target = new StudentServiceImpl();
        LogHandler logHandler = new LogHandler(target);
        StudentService studentServiceProxy = (StudentService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), logHandler);
        studentServiceProxy.setScore(1, 100);
        studentServiceProxy.awardStudent(1);
        System.out.println(studentServiceProxy.getClass().getName());
    }
}

输出为:

-----方法开始执行------
设置分数,学号:1,得分:100
-----方法执行完毕------
-----方法开始执行------
表扬学生,学号:1
-----方法执行完毕------
com.sun.proxy.$Proxy0

可以看出,记录日志的代码,我们只编写了一份,jdk动态代理创建的代理类,就为被代理接口中每个方法都加上了我们要增加的功能。另外,通过输出代理类的类名称,也可以看出jdk的动态代理方案生成了一个临时类,这个临时类就是我们要的代理类。

总结:

** jdk动态代理可以为具有接口的类创建代理对象;

** jdk动态代理会创建一个临时类,生成并返回这个临时类的一个对象实例作为代理类对象;

** 创建的临时类实现了被代理的接口,因此可以像使用被代理类对象一样使用代理类对象。
3.cglig动态代理

cglib提供了net.sf.cglib.proxy.MethodInterceptor接口和net.sf.cglib.proxy.Enhancer类。如同jdk动态代理,我们首先要为需要增加的功能写一个类,这个类实现MethodInterceptor接口。MethodInterceptor,顾名思义,方法拦截器,即代理类的所有方法都会被它拦截。该接口只有一个方法:

Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
与jdk动态代理的invoke方法很类似。我们需要在这个方法实现要增加的功能,并调用被代理对象的实际方法。

cglib通过继承被代理类的方法来为目标类创建代理类,也就是说,代理类是目标类的子类,代理类会覆盖目标类的每个非final的public方法,代理类的这些方法中会直接调用MethodInterceptor的intercept方法。

我们看看,用cglib怎么实现增加记录日志的功能。

首先,创建一个类,该类实现net.sf.cglib.proxy.MethodInterceptor接口,并实现我们要增加的功能:

public class LogInterceptor implements MethodInterceptor {
    
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("-----方法开始执行------");
        Object ret = proxy.invokeSuper(obj, args);
        System.out.println("-----方法执行完毕------");
        return ret;
    }
}
在intercept方法中,MethodProxy参数代表了被调用的方法,obj则是生成的代理对象。方法中记录了日志,由于生成的代理类是目标类的子类,因此随后调用父类的相应方法,即被代理对象的实际方法。

然后就可以创建代理类对象并使用了:

public class Main {

    public static void main(String[] args) {

        LogInterceptor logInterceptor = new LogInterceptor();
        MsgSender msgSenderProxy = (MsgSender) Enhancer.create(MsgSender.class, logInterceptor);
        boolean ret = msgSenderProxy.sendWarnMsg("容量警告");
        System.out.println("发送结果:" + ret);
        ret = msgSenderProxy.sendCriticalMsg("服务不可用");
        System.out.println("发送结果:" + ret);
        System.out.println(msgSenderProxy.getClass().getName());
    }
}
输出如下:

-----方法开始执行------
发送警告消息息:容量警告
-----方法执行完毕------
发送结果:true
-----方法开始执行------
发送关键消息:服务不可用
-----方法执行完毕------
发送结果:true
MsgSender$$EnhancerByCGLIB$$a7f2c153
可以看出,cglib会代理目标类的所有非final的public方法。这里调用Enhancer类的create方法来创建代理类对象,传入的参数为被代理类的Class对象,和实现增加功能的interceptor对象。由于Enhancer.create()方法会创建一个临时类(即代理类),该类是目标类的子类,随后生成并返回该类的一个实例,因此这里不需要再为被代理类生成一个对象。

总结:

** cglib通过继承目标类的方式来创建代理类;

** cglib会代理被代理类的所有非final的public方法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值