【SSM】Spring6(九.代理模式)

文章目录

  • 1.代理模式
  • 2. 静态代理
  • 3. 动态代理
    • 3.1 JDK动态代理
    • 3.2 CGLIB动态代理

1.代理模式

代理模式主要有两种:
静态代理模式
动态代理模式

2. 静态代理

有这样一个业务:订单的生成,修改,查看详情。实现如下

package com.sdnu.proxy.service;

/**
 * 订单业务接口
 */
public interface OrderService {
    void generate();
    void modify();
    void detail();
}
package com.sdnu.proxy.service;

public class OrderServiceImpl implements OrderService{
    @Override
    public void generate() {
        //模拟生成订单的耗时
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已生产");
    }

    @Override
    public void modify() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已修改");
    }

    @Override
    public void detail() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("查看订单详情");
    }
}

测试

package com.sdnu.proxy.client;

import com.sdnu.proxy.service.OrderService;
import com.sdnu.proxy.service.OrderServiceImpl;

public class Test {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceImpl();
        orderService.generate();
        orderService.modify();
        orderService.detail();
    }
}

过了一段时间,有一个新的业务,我们需要统计订单的生成总时间,于是我们有如下的解决方案:

方案一:采用硬编码的方式,在每一个业务接口中的每一个业务方法直接写一个统计时间。
这种方案的缺点:(1)违背OCP原则
(2)代码没有得到复用

方案二:编写业务的子类,让子类继承原来的业务类,对每个业务方法进行重写。
这种方案的缺点:(1)虽然没有违背OCP原则,但是由于采用继承方式会导致代码的耦合度变高。
(2)代码没有得到复用

方案三:静态代理

代理对象

package com.sdnu.proxy.service;

//代理对象
public class OrderServiceProxy implements OrderService{
    //将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系耦合度低
    private OrderService target;

    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void generate() {//代理方法
        long begin = System.currentTimeMillis();
        target.generate();
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
    }

    @Override
    public void modify() {//代理方法
        long begin = System.currentTimeMillis();
        target.modify();
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
    }

    @Override
    public void detail() {//代理方法
        long begin = System.currentTimeMillis();
        target.detail();
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
    }
}

测试

package com.sdnu.proxy.client;

import com.sdnu.proxy.service.OrderService;
import com.sdnu.proxy.service.OrderServiceImpl;
import com.sdnu.proxy.service.OrderServiceProxy;

public class Test {
    public static void main(String[] args) {
        OrderService orderService = new OrderServiceImpl();
        OrderServiceProxy orderServiceProxy = new OrderServiceProxy(orderService);
        orderServiceProxy.generate();
        orderServiceProxy.modify();
        orderServiceProxy.detail();
    }
}

这种静态代理符合OCP开闭原则,同时采用的是关联关系,所以程序的耦合度较低。
缺点:类爆炸问题。

3. 动态代理

在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。

常见的动态代理:

  • JDK动态代理
  • CGLIB动态代理
  • javassit动态代理

3.1 JDK动态代理

测试程序Test

package com.sdnu.proxy.client;

import com.sdnu.proxy.service.OrderService;
import com.sdnu.proxy.service.OrderServiceImpl;
import com.sdnu.proxy.service.TimerInvocationHandler;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                                                               target.getClass().getInterfaces(),
                                                                               new TimerInvocationHandler(target));
        //调用代理对象的代理方法
        orderServiceProxy.generate();
        orderServiceProxy.modify();
        orderServiceProxy.detail();
    }
}

代码OrderService orderServiceProxy = Proxy.newProxyInstance(类加载器, 接口类型, 调用处理器);做了两件事
● 第一件事:在内存中生成了代理类的字节码
● 第二件事:创建代理对象

参数:

类加载器:在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。

接口类型:代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。

调用的处理器:这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。


代理类
package com.sdnu.proxy.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimerInvocationHandler implements InvocationHandler {
    private Object target;

    public TimerInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //目标执行之前的增强
        long begin = System.currentTimeMillis();
        //反射机制调用目标对象上的目标方法
        Object retValue = method.invoke(target, args);
        //目标执行之后的增强
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
        return retValue;
    }
}

invoke方法有三个参数

  • Object proxy。代理对象。计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用
  • Method method。目标方法。
  • Object[] args。目标方法调用时要传的参数。

3.2 CGLIB动态代理

CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。

引入依赖:

<dependency>
  <groupId>cglibgroupId>
  <artifactId>cglibartifactId>
  <version>3.3.0version>
dependency>

你可能感兴趣的:(SSM,代理模式)