Java中的代理设计模式

本文最后更新于:1 年前

代理设计模式的优点:将通用性的工作都交给代理对象完成,被代理对象只需专注自己的核心业务。

  • 被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离;
  • 将通用的代码放在代理类中实现,提高了代码的复用性;
  • 通过在代理类添加业务逻辑,即可实现对原有业务逻辑的扩展(增强)。

生活中的代理

代理设计模式的优点:将通用性的工作都交给代理对象完成,被代理对象只需专注自己的核心业务。

静态代理

静态代理中,代理类只能够为特定的类生产代理对象,不能代理任意类。

使用代理的好处:

  • 被代理类中只用关注核心业务的实现,将通用的管理型逻辑(事务管理、日志管理)和业务逻辑分离;
  • 将通用的代码放在代理类中实现,提高了代码的复用性;
  • 通过在代理类添加业务逻辑,即可实现对原有业务逻辑的扩展(增强)。

动态代理

动态代理,几乎可以为所有的类产生代理对象。

动态代理的实现方式有2种:

  • JDK 代理 : 基于接口的动态代理技术。
  • cglib 代理:基于父类的动态代理技术。

JDK 代理

JDK动态代理是通过被代理对象实现的接口产生其代理对象的。

写法一

  1. JDK动态代理类实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    /***
    * JDK动态代理:是通过被代理对象实现的接口产生其代理对象的
    * 1.创建一个类,实现InvocationHandler接口,重写invoke方法
    * 2.在类种定义一个Object类型的变量,并提供这个变量的有参构造器,用于将被代理对象传递进来
    * 3.定义getProxy方法,用于创建并返回代理对象
    */
    public class JDKDynamicProxy implements InvocationHandler {
    //被代理对象
    private Object obj;
    public JDKDynamicProxy(Object obj) {
    this.obj = obj;
    }
    //产生代理对象,返回代理对象
    public Object getProxy(){
    //1.获取被代理对象的类加载器
    ClassLoader classLoader = obj.getClass().getClassLoader();
    //2.获取被代理对象的类实现的接口
    Class<?>[] interfaces = obj.getClass().getInterfaces();
    //3.产生代理对象(通过被代理对象的类加载器及实现的接口)
    //第一个参数:被代理对象的类加载器
    //第二个参数:被代理对象实现的接口
    //第三个参数:使用产生代理对象调用方法时,用于拦截方法执行的处理器
    Object proxy = Proxy.newProxyInstance(classLoader, interfaces,this);
    return proxy;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    begin();
    Object returnValue = method.invoke(obj,args); //执行method方法(insert)
    commit();
    return returnValue;
    }

    public void begin(){
    System.out.println("----------开启事务");
    }

    public void commit(){
    System.out.println("----------提交事务");
    }
    }
  2. 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //创建被代理对象
    BookDAOImpl bookDAO = new BookDAOImpl();
    StudentDAOImpl studentDAO = new StudentDAOImpl();

    //创建动态代理类对象,并将被代理对象传递到代理类中赋值给obj
    JDKDynamicProxy jdkDynamicProxy = new JDKDynamicProxy(studentDAO);

    //proxy就是产生的代理对象:产生的代理对象可以强转成被代理对象实现的接口类型
    GenaralDAO proxy = (GenaralDAO)jdkDynamicProxy.getProxy();

    //使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类种的invoke方法
    //调用的方法作为一个Method参数,传递给了invoke方法
    proxy.insert(student);

写法二

  1. 目标类接口

    1
    2
    3
       public interface TargetInterface {
    public void save();
    }
  2. 目标类

    1
    2
    3
    4
    5
    6
    public class Target implements TargetInterface {
    @Override
    public void save() {
    System.out.println("save running……");
    }
    }
  3. 未使用动态代理增强

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class ProxyTest {

    public static void main(String[] args) {

    //目标对象
    final Target target = new Target();

    //返回值,就是动态生成的代理对象
    TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
    //目标对象类加载器
    target.getClass().getClassLoader(),
    //目标对象相同的接口字节码对象数组
    target.getClass().getInterfaces(),
    new InvocationHandler() {

    //调用代理对象的任何方法,实质执行的都是invoke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //执行目标方法
    method.invoke(target, args);
    return null;
    }
    }
    );

    //调用代理对象的方法
    proxy.save();

    }
    }

  4. 被用来代理的类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Advice {

    public void before(){
    System.out.println("前置增强……");
    }

    public void after(){
    System.out.println("后置增强……");
    }
    }
  5. 使用动态代理增强

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class ProxyTest {

    public static void main(String[] args) {

    //目标对象
    final Target target = new Target();

    //增强对象
    Advice advice = new Advice();

    TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //前置增强
    advice.before();
    method.invoke(target, args);
    //后置增强
    advice.after();
    return null;
    }
    }
    );

    //调用代理对象的方法
    proxy.save();

    }
    }

关于Java代理,更详细说明见这两篇博客:**『传送门1』『传送门2』**。

cglib 代理

由于JDK动态代理是通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类的对象。如果一个类没有实现任何接口,该如何产生代理对象呢?

CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象。

CGLib动态代理不能为final类创建代理对象。

案例一

  1. 添加CGLib的依赖

    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
    </dependency>
  2. CGLib动态代理实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    /**
    * 1.添加cglib依赖
    * 2.创建一个类,实现MethodInterceptor接口,同时实现接口中的intercept方法
    * 3.在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于传递被代理对象
    * 4.定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
    */
    public class CGLibDynamicProxy implements MethodInterceptor {

    private Object obj;
    public CGLibDynamicProxy(Object obj) {
    this.obj = obj;
    }

    public Object getProxy(){
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(obj.getClass());
    enhancer.setCallback(this);
    Object proxy = enhancer.create();
    return proxy;
    }


    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    begin();
    Object returnValue = method.invoke(obj,objects); //通过反射调用被代理类的方法
    commit();
    return returnValue;
    }

    public void begin(){
    System.out.println("----------开启事务");
    }

    public void commit(){
    System.out.println("----------提交事务");
    }
    }
  3. 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //创建被代理对象
    BookDAOImpl bookDAO = new BookDAOImpl();
    StudentDAOImpl studentDAO = new StudentDAOImpl();

    //通过cglib动态代理类创建代理对象
    CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
    //代理对象实际上是被代理对象子类,因此代理对象可直接强转为被代理类类型
    BookDAOImpl proxy = (BookDAOImpl) cgLibDynamicProxy.getProxy();

    //使用对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的参数传递到intercept方法
    proxy.update();

案例二

  1. 导入 pom 坐标

    spring-context 已经集成了 cglib 相关依赖,只需导入 spring-context 即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <dependencies>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
    </dependency>
    </dependencies>
  2. 目标类

    1
    2
    3
    4
    5
    6
    public class Target implements TargetInterface {
    @Override
    public void save() {
    System.out.println("save running……");
    }
    }
  3. 动态代理代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class ProxyTest {

    public static void main(String[] args) {

    //目标对象
    final cc.gaojie.proxy.jdk.Target target = new Target();

    //增强对象
    Advice advice = new Advice();

    //返回值,就是动态生成的代理对象,基于cglib
    //1. 创建增强器
    Enhancer enhancer = new Enhancer();
    //设置父类
    enhancer.setSuperclass(Target.class);
    //设置回调
    enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object o, Method method, Object[] objects,
    MethodProxy methodProxy) throws Throwable {
    //执行前置
    System.out.println("前置代码增强....");
    //执行目标
    Object invoke = method.invoke(target, objects);
    //执行后置
    System.out.println("后置代码增强....");
    return invoke;
    }
    });
    //创建代理对象
    Target proxy = (Target) enhancer.create();

    //测试,当调用接口的任何方法时,代理对象的代码都无序修改
    proxy.save();
    }
    }


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!