菜单

Java动态代理

duckflew
发布于 2021-10-10 / 238 阅读
0
0

Java动态代理

Java动态代理

代理 顾名思义就是替代某个对象完成它的功能 在不改变原有功能的情况下 做一些附加操作

就好比去看电影 你买票其实本质是买观影权, 这是电影院做了上层片方的代理人 为你提供服务 此外电影不仅可以看电影 卖票的同时也会卖零食水之类的 这就是附加的操作

回到Java代理本身 Java的代理分两种

  • 静态代理
  • 动态代理

这两者实现的目的是一样的 只是实现的方法有所不同

静态代理

先介绍静态代理

我们有一个这样的接口

public interface BookService
{
    void save();
}

有如下实现类

public class BookServiceImpl implements BookService
{
    @Override
    public void save()
    {
        System.out.println("调用Dao");
    }
}

理论上来说Service层都要做事务的控制 也就是 先开启事务 然后调用 dao执行相关操作逻辑 然后提交事务 如果有异常就要回滚

接下来创建一个静态代理对象 可以想象到 要代理原本对象实现其应该实现的功能 那必然要实现同一个接口 换言之,我们需要代理BookServiceImpl 那肯定也要实现BookService接口

所以静态代理代理的写法如下

public class BookServiceProxy implements BookService
{
    BookService bookService=new BookServiceImpl();
    @Override
    public void save()
    {
        System.out.println("开启事务");
        try
        {
            bookService.save();
            System.out.println("提交事务");
        } catch (Exception e)
        {
            System.out.println("回滚");
        }
    }
}

如果使用了Spring框架 那就在BookServiceProxy类里面注入BookServiceImpl

最后在执行的时候调用代理对象即可

动态代理

可以看到 静态代理有一个很明显缺点 那就是需要新建对象 而且需要为每个方法编写附加逻辑 那如果有很多被代理的类呢 这会使得我们需要些双倍量的代码 而且耦合性太强了

于是就考虑到动态代理 也就是说 当我需要使用到代理对象的时候让系统为我们生成一个临时的代理对象 用完了就丢

代码如下

Thread thread = Thread.currentThread();
ClassLoader classLoader = thread.getContextClassLoader();
Class [] classes={BookService.class};
BookService bookServiceProxy= (BookService) Proxy.newProxyInstance(classLoader, classes,(proxy, method, args1) -> {
    System.out.println(Arrays.toString(args1));
    System.out.println("开启事务");
    Object returnValue = method.invoke(new BookServiceImpl(),args1);
    System.out.println("提交事务");
    return returnValue;
});
bookServiceProxy.save();

我们通过反射包内的Proxy.newProxyInstance方法进行创建 这个方法需要三个参数

  • 类加载器 ClassLoader
  • 被代理对象实现的接口
  • InvocationHandler

首先是通过当前线程拿到类加载器 然后 既然是代理 前面提到要实现被代理对象相同的接口 所以要new一个接口数组 最后 我们知道要代理哪个对象了 也要指定附加的逻辑 所以就第三个参数就是InvocationHandler

代码里面第三个参数实现了一个匿名对象 InvocationHandler 重写了

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

方法

这个方法里面也有三个参数 当调用代理对象执行方法时 就会进入到这个方法里面 显而易见

三个参数的意思是

  • proxy-> 代理对象本身
  • method->被执行的方法本身
  • args->被执行的方法的参数

下面我们执行动态代理对象的方法

image-20211010231431656


评论