JAVA代理:静态代理和动态代理

JAVA代理:静态代理和动态代理

在java编程中代理是一种很常见的设计模式,它允许我们通过创建一个中间层来间接的访问某个对象,从而实现对该对象的控制和增加。它主要分为静态代理和动态代理,其中Spring AOP就是动态代理的典型例子。

举一个例子比如点外卖,我们只要通过外卖app进行下单,不需要我们自去取餐吧,是外卖骑手取完餐后送到我们手上,而外面骑手就是代理,帮我们取餐的。代理可以简单理解为一个对象不想干的事情,让其他对象帮你干。

静待代理

代理类实现与目标类相同的接口,使用时实例化代理类而不是目标类。
在这里插入图片描述

拿外卖取餐作为例子 :

接口:

public interface DineService {
    void eat();
}

目标类:

public class DineServiceImpl implements DineService {

    @Override
    public void eat() {
        System.out.println("点餐");
    }
}

代理类:

public class DineServiceProxy implements DineService {
    /**
     * 接受目标类的对象 可以理解为dineService 就是目标类的引用
     */
    DineService dineService;

    public DineServiceProxy(DineService dineService) {
        this.dineService = dineService;
    }


    /**
     * 加入横切逻辑 调用目标方法 横切逻辑可以理解为你想让代理类帮你完成的事
     */
    @Override
    public void eat() {
        dineService.eat();
        System.out.println("取餐");
    }
}

测试:

 @Test
    public void testEat() {
        //目标类
        DineServiceImpl dineService1Impl = new DineServiceImpl();
        //拿到外卖 实例化代理类
        DineService dineService = new DineServiceProxy(dineService1Impl);
        //调用代理类方法
        dineService.eat();
    }

运行结果:

在这里插入图片描述

通过实例可以看到目标类(也就是我们自己)只负责点餐,而代理类(也就时外卖骑手)帮我们取餐。

在实际的应用中,静态代理常常用于已有的类进行加强或扩展,但是静态代理的缺点就是代理类无法做到通用,每增加一个目标类类,都需要手动创建一个代理类,这样会导致代理类的数量快速增加,增加了维护难度。我们可以使用动态代理来解决这个问题。

动态代理

1.代理对象不需要实现接口,但是目标对象要实现接口,否则不用动态代理

2.动态代理分为:JDK代理和CGLib代理

3.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象

JDK代理(接口代理)

代理类所在包:java.lang.reflect.Proxy

JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:static Object newProxyInstance(ClassLOader, Class<?>[] interfaces,InvocationHandler h)

要求:目标类必须实现接口

实现:通过代理工厂类来创建目标代理对象

接口:

public interface OrderService {
    void save();
}

目标类:

public class OrderServiceImpl implements OrderService {

    @Override
    public void save() {
        System.out.println("-----------构建新订单----------");
        System.out.println("订单生成中.....");
        System.out.println("-----------订单生成完成---------");
    }
}

代理工厂:

public class ProxyFactory<T> {
    /**
     * 代理的目标对象
     */
    T target;

    /**
     * 创建代理实例时 传递目标对象
     * @param target
     */
    public ProxyFactory(T target) {
        this.target = target;
    }

    /**
     * 创建目标对象的代理类对象
     * @return
     */
    public T createProxy() {
        /**
         * newProxyInstance方法参数说明
         * ClassLoader loader 类加载器
         * Class<?>[] interfaces 代理类要实现的接口,即目标类实现的接口就是代理类要实现订单接口
         *  InvocationHandler h 代理类要做的工作
         */
        return (T) Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     *
                     * @param proxy 代理实例
                     * @param method 要执行的目标方法
                     * @param args 目标方法的参数
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //增加横切逻辑的地方
                        //添加日志
                        System.out.println("开始订单日志");
                        //添加事务
                        System.out.println("开启事务");
                        //调用目标方法 target目标对象
                        Object obj = method.invoke(target, args);
                        //提交事务
                        System.out.println("提交事务");
                        System.out.println("保存订单成功");
                        return obj; //将执行目标方法的返回值 返回
                    }
                });
    }

}

测试:

 @Test
    public void testProxy() {
        ProxyFactory<OrderService> proxyFactory = new ProxyFactory<>(new OrderServiceImpl());
        OrderService orderService = proxyFactory.createProxy();
        orderService.save();
    }

运行结果:

在这里插入图片描述

由此可见我们已经实现了目标类动态动态代理,这时候我们想换一个接口和目标类,看代理工厂还能不能实现

接口:

public interface GoodsService {
    void structure();
}

目标类:

public class GoodsServiceImpl implements GoodsService {
    @Override
    public void structure() {
        System.out.println("-------生成商品列表----------");
    }
}

测试:

   @Test
    public void testProxy1() {
        ProxyFactory<GoodsService> proxyFactory = new ProxyFactory<>(new GoodsServiceImpl());
        GoodsService goodsService = proxyFactory.createProxy();
        goodsService.structure();
    }

运行结果:

在这里插入图片描述

我们发现换一个接口和目标类,代理工厂仍然能够运行,说明动态代理成功。但是代理工厂类代码多不够简洁,并且每次使用工厂类时还要进行实例化,这是我们可以对代理工厂类进行一个改造:

public class ProxyFactory2 {
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), target.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    //增加横切逻辑的地方
                    //添加日志
                    System.out.println("开始订单日志");
                    //添加事务
                    System.out.println("开启事务");
                    //调用目标方法 target目标对象
                    Object obj = method.invoke(target, args);
                    //提交事务
                    System.out.println("提交事务");
                    System.out.println("保存订单成功");
                    return obj; //将执行目标方法的返回值 返回
                });
    }
}

改造完成,测试一下:

  @Test
    public void testFactory2() {
        GoodsService goodsService = ProxyFactory2.createProxy(new GoodsServiceImpl());
        goodsService.structure();
    }

运行结果:

在这里插入图片描述

结果显示正常表示工厂类改造成功,我们发现测试时,将创建代理工厂改成静态方法后,代码比之前少了几行,使用时也不需要在实例化工厂了。

CGLib代理

CGLib代理也被称为子代理,不要目标类实现接口。

使用前先导入CGLib的依赖:

 <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
 </dependency>

目标类:

public class OrderServiceImpl {
    public void save() {
        System.out.println("-----------构建新订单----------");
        System.out.println("订单生成中.....");
        System.out.println("-----------订单生成完成---------");
    }
}

代理类:

public class ProxyFactory {
    public static <T> T createProxy(T target) {
        //1.创建工具类的实例
        Enhancer enhancer = new Enhancer();
        //2.设置代理类的父类  目标类类型
        enhancer.setSuperclass(target.getClass());
        //3.增加横切逻辑 设置方法拦截 拦截目标方法 增加逻辑
        enhancer.setCallback(new MethodInterceptor() {
            /**
             *
             * @param o 代理类实例
             * @param method  代理的目标方法
             * @param objects 目标方法的参数
             * @param methodProxy
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //增加横切逻辑的地方
                //添加日志
                System.out.println("开始订单日志");
                //添加事务
                System.out.println("开启事务");
                //调用目标方法 执行目标对象target中的method方法
                Object obj = method.invoke(target, objects);
                //提交事务
                System.out.println("提交事务");
                System.out.println("保存订单成功");
                return obj; //将执行目标方法的返回值 返回
            }
        });
        //4.返回创建代理类实例
        return (T) enhancer.create();
    }
}

测试:

 @Test
    public void testCGLib() {
        OrderServiceImpl orderService = ProxyFactory.createProxy(new OrderServiceImpl());
        orderService.save();
    }

运行结果:

在这里插入图片描述

注意:

1.CGLib代理是在内存中动态构建子类,所以代理的类不能为final

2.目标对象的方法如果是为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法


测试:

~~~java
 @Test
    public void testCGLib() {
        OrderServiceImpl orderService = ProxyFactory.createProxy(new OrderServiceImpl());
        orderService.save();
    }

运行结果:

[外链图片转存中…(img-xMiZOYF6-1697723037836)]

注意:

1.CGLib代理是在内存中动态构建子类,所以代理的类不能为final

2.目标对象的方法如果是为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值